From a64cca0797aa2745325bcd34c2b2ec3cc2d7594f Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Wed, 11 Mar 2026 14:35:03 -0300 Subject: [PATCH 01/31] Fixing pluralization of table names in DataModel. This was silently introducing an error in AO2D merging, due to the previous way my local aod-writer was written. Now fixed using local o2-aod-merger tests. --- PWGLF/DataModel/lambdaJetPolarizationIons.h | 8 ++++---- .../Strangeness/lambdaJetPolarizationIons.cxx | 4 ++-- .../Strangeness/lambdaJetPolarizationIonsDerived.cxx | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/PWGLF/DataModel/lambdaJetPolarizationIons.h b/PWGLF/DataModel/lambdaJetPolarizationIons.h index fe1116ee525..faa06e78b22 100644 --- a/PWGLF/DataModel/lambdaJetPolarizationIons.h +++ b/PWGLF/DataModel/lambdaJetPolarizationIons.h @@ -88,7 +88,7 @@ DECLARE_SOA_DYNAMIC_COLUMN(LeadParticlePz, leadParticlePz, //! Leading particle [](float leadParticlePt, float leadParticleEta) -> float { return leadParticlePt * std::sinh(leadParticleEta); }); } // namespace lambdajetpol -DECLARE_SOA_TABLE(RingCollisions, "AOD", "RINGCOLLISIONS", +DECLARE_SOA_TABLE(RingCollisions, "AOD", "RINGCOLLISION", o2::soa::Index<>, // self-index: auto-assigned row number lambdajetpol::CentFT0M, lambdajetpol::CentFT0C, @@ -99,7 +99,7 @@ namespace lambdajetpol DECLARE_SOA_INDEX_COLUMN(RingCollision, ringCollision); // Declare index after table is available } // namespace lambdajetpol -DECLARE_SOA_TABLE(RingJets, "AOD", "RINGJETS", +DECLARE_SOA_TABLE(RingJets, "AOD", "RINGJET", lambdajetpol::RingCollisionId, // relational index -> RingCollisions lambdajetpol::JetPt, lambdajetpol::JetEta, @@ -110,7 +110,7 @@ DECLARE_SOA_TABLE(RingJets, "AOD", "RINGJETS", lambdajetpol::JetPy, lambdajetpol::JetPz); -DECLARE_SOA_TABLE(RingLeadP, "AOD", "RINGLEADP", +DECLARE_SOA_TABLE(RingLeadPs, "AOD", "RINGLEADP", lambdajetpol::RingCollisionId, lambdajetpol::LeadParticlePt, lambdajetpol::LeadParticleEta, @@ -120,7 +120,7 @@ DECLARE_SOA_TABLE(RingLeadP, "AOD", "RINGLEADP", lambdajetpol::LeadParticlePy, lambdajetpol::LeadParticlePz); -DECLARE_SOA_TABLE(RingLaV0s, "AOD", "RINGLAV0S", +DECLARE_SOA_TABLE(RingLaV0s, "AOD", "RINGLAV0", lambdajetpol::RingCollisionId, lambdajetpol::V0Pt, lambdajetpol::V0Eta, diff --git a/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx b/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx index 7359008febf..0ed6559e140 100644 --- a/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx +++ b/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx @@ -156,7 +156,7 @@ struct lambdajetpolarizationions { // } products; Produces tableV0s; Produces tableJets; - Produces tableLeadParticles; + Produces tableLeadParticles; Produces tableCollisions; // Define histogram registries: @@ -274,7 +274,7 @@ struct lambdajetpolarizationions { Configurable phiHighCut{"phiHighCut", "0.1/x+pi/18.0+0.06", "High azimuth cut parametrisation"}; // PID (TPC/TOF) - Configurable tpcPidNsigmaCut{"tpcPidNsigmaCut", 3, "tpcPidNsigmaCut"}; // Default is 5. Reduced to agree with strangenessInJetsIons + Configurable tpcPidNsigmaCut{"tpcPidNsigmaCut", 4, "tpcPidNsigmaCut"}; // Default is 5 Configurable tofPidNsigmaCutLaPr{"tofPidNsigmaCutLaPr", 1e+6, "tofPidNsigmaCutLaPr"}; Configurable tofPidNsigmaCutLaPi{"tofPidNsigmaCutLaPi", 1e+6, "tofPidNsigmaCutLaPi"}; diff --git a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx index 1681867e98b..dcf28b07b7f 100644 --- a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx @@ -432,9 +432,9 @@ struct lambdajetpolarizationionsderived { // (TODO: test using custom grouping) Preslice perColJets = o2::aod::lambdajetpol::ringCollisionId; Preslice perColV0s = o2::aod::lambdajetpol::ringCollisionId; - Preslice perColLeadPs = o2::aod::lambdajetpol::ringCollisionId; + Preslice perColLeadPs = o2::aod::lambdajetpol::ringCollisionId; void processPolarizationData(o2::aod::RingCollisions const& collisions, o2::aod::RingJets const& jets, o2::aod::RingLaV0s const& v0s, - o2::aod::RingLeadP const& leadPs) + o2::aod::RingLeadPs const& leadPs) { for (auto const& collision : collisions) { const auto collId = collision.globalIndex(); // The self-index accessor From 6f24d615039b024105b4ed1d14e79f0f225284b6 Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Fri, 13 Mar 2026 13:51:54 -0300 Subject: [PATCH 02/31] Adding TPC-onwards track selection for Jet building, more JetQA histograms on consumer and simple QAs to estimate Cov(R, N_Lambda) --- .../Strangeness/lambdaJetPolarizationIons.cxx | 41 +++++++++++++++---- .../lambdaJetPolarizationIonsDerived.cxx | 24 +++++++++++ 2 files changed, 57 insertions(+), 8 deletions(-) diff --git a/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx b/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx index 0ed6559e140..89ca559e59e 100644 --- a/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx +++ b/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx @@ -475,6 +475,9 @@ struct lambdajetpolarizationions { // Configurable minTPCrowsOverFindableClusters{"minTPCrowsOverFindableClusters", -1, "minimum nbr of TPC crossed rows over findable clusters"}; // Configurable minTPCfoundOverFindableClusters{"minTPCfoundOverFindableClusters", 0.8f, "minimum nbr of found over findable TPC clusters"}; + // QA checks for detector asymmetry: + Configurable forceNoITS{"forceNoITS", false, "Use onlt TPC-onwards tracks as pseudoJet candidates to QA asymmetries/pile-up in detector"}; + // Jets typical cuts (suppress non-primary candidates): Configurable doDCAcuts{"doDCAcuts", false, "Apply DCA cuts to jet candidates (biases towards primary-vertex/prompt hadron jets)"}; Configurable maxDCAz{"maxDCAz", 3.2f, "Max DCAz to primary vertex [cm] (remove pileup influence)"}; @@ -664,6 +667,7 @@ struct lambdajetpolarizationions { // makes that cut active, so disabled stages are shown in grey in the output. std::vector jetTrackSelectionLabels = { {"All track candidates", true}, + {"forceNoITS", pseudoJetCandidateTrackSelections.forceNoITS}, {"ITS clusters (min)", pseudoJetCandidateTrackSelections.minITSnCls >= 0}, {"TPC crossed rows (min)", pseudoJetCandidateTrackSelections.minNCrossedRowsTPC > 0}, {"TPC #chi^{2}/N_{cls} (max)", pseudoJetCandidateTrackSelections.maxChi2TPC < 1.e8f}, @@ -775,6 +779,8 @@ struct lambdajetpolarizationions { if (analyseLambda && analyseAntiLambda) { histos.add("hAmbiguousLambdaCandidates", "hAmbiguousLambdaCandidates", kTH1D, {{1, 0, 1}}); histos.add("hAmbiguousPerEvent", "hAmbiguousPerEvent", kTH1D, {{15, 0, 15}}); + histos.add("hNonAmbiguousPerEvent", "hNonAmbiguousPerEvent", kTH1D, {{25, 0, 25}}); // To understand the population of correlated Lambda-likes per event + histos.add("hLambdasAndAntiLambdasPerEvent", "hLambdasAndAntiLambdasPerEvent", kTH1D, {{25, 0, 25}}); // Alternative check that shows how bad is the possibly correlated full population (Ambig+NonAmbig) } // QA histograms if requested @@ -1210,6 +1216,13 @@ struct lambdajetpolarizationions { { // (TODO: add an equivalent for photon jets and Z jets, which don't consider charged particles) // if (track.sign() == 0) return false; // Tracks are always either positive or negative, at least in TPC and ITS, which are the ones used (not looking at photon-jets right now) // ITS/TPC cuts: + if (pseudoJetCandidateTrackSelections.forceNoITS) { // reject tracks that have ITS bits (no need to reject hasTPC as we always demand minNCrossedRowsTPC > 70 in all analyses) + const auto detMap = track.detectorMap(); + if (detMap & o2::aod::track::ITS) + return false; + } + JetTrackSelCounter.fill(); // bin: forceNoITS + if (pseudoJetCandidateTrackSelections.minITSnCls >= 0) { if (track.itsNCls() < pseudoJetCandidateTrackSelections.minITSnCls) return false; @@ -1657,7 +1670,7 @@ struct lambdajetpolarizationions { if (doEventQA) fillEventSelectionQA(lastBinEvSel - 1, centrality); // hasRingJet passes - const auto& leadingJet = jets[0]; + const auto& leadingJet = jets[0]; // A leading jet before eta cuts. Different type of QA from what we will be able to do down the consumer pipeline for (const auto& jet : jets) { // Jet must be fully contained in the acceptance (0.9 for ITS+TPC barrel) const float jet_eta = jet.eta(); @@ -1775,8 +1788,10 @@ struct lambdajetpolarizationions { // Call to jets process: jetsProcess(V0DauTracks, ringCollIdx, centrality); // V0DauTracks takes the place of jetTracks now - uint NLambdas = 0; // Counting particles per event + // Counting particles per event: + uint NLambdas = 0; uint NAntiLambdas = 0; + uint NNonAmbiguous = 0; uint NAmbiguous = 0; for (auto const& v0 : V0s) { V0SelCounter.resetForNewV0(); @@ -1816,8 +1831,10 @@ struct lambdajetpolarizationions { histos.fill(HIST("GeneralQA/h2dArmenterosFullSelectedAntiLambda"), v0.alpha(), v0.qtarm()); // XOR check: - if (isLambda ^ isAntiLambda) + if (isLambda ^ isAntiLambda){ histos.fill(HIST("GeneralQA/h2dArmenterosFullSelectedNonAmbiguous"), v0.alpha(), v0.qtarm()); + NNonAmbiguous++; + } // int lambdaIdx = -1; // No need to pass armenteros if (isLambda && isAntiLambda) { @@ -2002,11 +2019,19 @@ struct lambdajetpolarizationions { } // end V0s loop // Fill histograms on a per-event level: - histos.fill(HIST("Lambda/hLambdasPerEvent"), NLambdas); - histos.fill(HIST("AntiLambda/hAntiLambdasPerEvent"), NAntiLambdas); - histos.fill(HIST("hAmbiguousPerEvent"), NAmbiguous); - histos.fill(HIST("Lambda/h2dNbrOfLambdaVsCentrality"), centrality, NLambdas); - histos.fill(HIST("AntiLambda/h2dNbrOfAntiLambdaVsCentrality"), centrality, NAntiLambdas); + if (analyseLambda) { + histos.fill(HIST("Lambda/hLambdasPerEvent"), NLambdas); + histos.fill(HIST("Lambda/h2dNbrOfLambdaVsCentrality"), centrality, NLambdas); + } + if (analyseAntiLambda) { + histos.fill(HIST("AntiLambda/hAntiLambdasPerEvent"), NAntiLambdas); + histos.fill(HIST("AntiLambda/h2dNbrOfAntiLambdaVsCentrality"), centrality, NAntiLambdas); + } + if (analyseLambda && analyseAntiLambda) { + histos.fill(HIST("hAmbiguousPerEvent"), NAmbiguous); + histos.fill(HIST("hNonAmbiguousPerEvent"), NNonAmbiguous); // The actual + histos.fill(HIST("hLambdasAndAntiLambdasPerEvent"), NLambdas + NAntiLambdas); + } } void processData(SelCollisions::iterator const& collision, aod::V0Datas const& V0s, DauTracks const& V0DauTracks, aod::BCsWithTimestamps const& bcs) diff --git a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx index dcf28b07b7f..33b58145433 100644 --- a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx @@ -206,6 +206,7 @@ struct lambdajetpolarizationionsderived { // Jet axes: ConfigurableAxis axisLeadingParticlePt{"axisLeadingParticlePt", {100, 0.f, 200.f}, "Leading particle p_{T} (GeV/c)"}; // Simpler version! ConfigurableAxis axisJetPt{"axisJetPt", {50, 0.f, 200.f}, "Jet p_{t} (GeV)"}; + ConfigurableAxis axisEta{"axisEta", {50, -1.0f, 1.0f}, "#eta"}; ConfigurableAxis axisDeltaTheta{"axisDeltaTheta", {40, 0, constants::math::PI}, "#Delta #theta_{jet}"}; ConfigurableAxis axisDeltaPhi{"axisDeltaPhi", {40, -constants::math::PI, constants::math::PI}, "#Delta #phi_{jet}"}; @@ -410,6 +411,16 @@ struct lambdajetpolarizationionsderived { histos.get(HIST("pRingCutsLeadingP"))->GetXaxis()->SetBinLabel(2, "p_{T}^{#Lambda}@[0.5,1.5],|y_{#Lambda}|<0.5"); histos.get(HIST("pRingCutsLeadingP"))->GetXaxis()->SetBinLabel(3, "|LeadP_{#eta}|<0.5"); histos.get(HIST("pRingCutsLeadingP"))->GetXaxis()->SetBinLabel(4, "#Lambda + LeadP cuts"); + + // Integrated observable for events with NLambda+NAntiLambda V0s per event + // (an interesting measurement of correlation between and Lambda-like V0s multiplicity. A proxy of covariance) + // (calculated for leading jets only) + histos.add("pRingVsNV0s", "pRingVsNV0s; N_{#Lambda}+N_{#bar{#Lambda}};<#it{R}>", kTProfile, {{20, 0, 20}}); + + // Leading Jet QA: + histos.add("JetKinematicsQA/hLeadJetEta", "hLeadJetEta", kTH1D, {axisConfigurations.axisEta}); + histos.add("JetKinematicsQA/hSubLeadJetEta", "hSubLeadJetEta", kTH1D, {axisConfigurations.axisEta}); + histos.add("JetKinematicsQA/hLeadPEta", "hLeadPEta", kTH1D, {axisConfigurations.axisEta}); } // Helper to get centrality (same from TableProducer, thanks to templating!): @@ -480,6 +491,7 @@ struct lambdajetpolarizationionsderived { // Discard events with no leading particle (FastJet didn't even run in these cases!): if (leadPPt < 0.) continue; + histos.fill(HIST("JetKinematicsQA/hLeadPEta"), leadPEta); // Build leading particle unit vector, outside the V0 loop for performance: XYZVector leadPUnitVec = XYZVector(leadPPx, leadPPy, leadPPz).Unit(); @@ -521,6 +533,7 @@ struct lambdajetpolarizationionsderived { leadingJetPhi = leadingJet->jetPhi(); // Using internal getters to make code cleaner: leadingJetUnitVec = XYZVector(leadingJet->jetPx(), leadingJet->jetPy(), leadingJet->jetPz()).Unit(); + histos.fill(HIST("JetKinematicsQA/hLeadJetEta"), leadingJetEta); // This will not be subject to the forcePerpToJet nor the forceJetDirectionSmudge QAs, for simplicity // QA block -- Purposefully changing the jet direction (should kill signal, if any): if (forcePerpToJet) { // Use modified jet direction (done outside loop to guarantee all V0s inside event use same fake jet) @@ -578,6 +591,8 @@ struct lambdajetpolarizationionsderived { subleadingJetPhi = subleadingJet->jetPhi(); // Using internal getters to make code cleaner: subJetUnitVec = XYZVector(subleadingJet->jetPx(), subleadingJet->jetPy(), subleadingJet->jetPz()).Unit(); + + histos.fill(HIST("JetKinematicsQA/hSubLeadJetEta"), subleadingJetEta); } // (jet eta cuts only meaningful when the jet actually exists) @@ -585,6 +600,14 @@ struct lambdajetpolarizationionsderived { const bool kinematic2ndJetCheck = hasValidSubJet && (std::abs(subleadingJetEta) < 0.5); const bool kinematicLeadPCheck = std::abs(leadPEta) < 0.5; + // Fetching number of Lambda-like V0s in collision (must be known before full loop, to fill "pRingVsNV0s"): + int NLambdaLikeV0s = 0; + for (auto const& v0 : v0sInColl) { + if (v0.isLambda() ^ v0.isAntiLambda()){ // XOR (only the non-ambiguous candidates) + NLambdaLikeV0s++; + } + } + for (auto const& v0 : v0sInColl) { const bool isLambda = v0.isLambda(); const bool isAntiLambda = v0.isAntiLambda(); @@ -711,6 +734,7 @@ struct lambdajetpolarizationionsderived { if (hasValidLeadingJet) { RING_OBSERVABLE_FILL_LIST(APPLY_HISTO_FILL, "Ring") histos.fill(HIST("pRingCuts"), 0, ringObservable); + histos.fill(HIST("pRingVsNV0s"), NLambdaLikeV0s, ringObservable); } if (hasValidSubJet) { RING_OBSERVABLE_2NDJET_FILL_LIST(APPLY_HISTO_FILL, "Ring") From d3690eb03cfda15cd9362c0dddc94b09d50dbbd4 Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Tue, 24 Mar 2026 17:12:40 +0000 Subject: [PATCH 03/31] TableProducer: Adding extra V0Selection QA histograms, removing unused ones. Consumer: Adding uncertainty estimation via delta method (considers covariance), post-processing options for minimum leading jet and leading particle pT (purity knobs), QA of the observable in positive eta and negative eta --- .../Strangeness/lambdaJetPolarizationIons.cxx | 107 ++--- .../lambdaJetPolarizationIonsDerived.cxx | 420 +++++++++++++++--- 2 files changed, 402 insertions(+), 125 deletions(-) diff --git a/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx b/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx index 89ca559e59e..c2a09cef0dd 100644 --- a/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx +++ b/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx @@ -151,9 +151,6 @@ enum BkgSubtraction { ////////////////////////////////////////////// struct lambdajetpolarizationions { - - // struct : ProducesGroup { - // } products; Produces tableV0s; Produces tableJets; Produces tableLeadParticles; @@ -164,23 +161,20 @@ struct lambdajetpolarizationions { // master analysis switches Configurable analyseLambda{"analyseLambda", true, "process Lambda-like candidates"}; - Configurable analyseAntiLambda{"analyseAntiLambda", false, "process AntiLambda-like candidates"}; // Will work only with Lambdas, in a first analysis + Configurable analyseAntiLambda{"analyseAntiLambda", true, "process AntiLambda-like candidates"}; Configurable doPPAnalysis{"doPPAnalysis", false, "if in pp, set to true. Default is HI"}; Configurable irSource{"irSource", "ZNChadronic", "Estimator of the interaction rate (Recommended: pp --> T0VTX, Pb-Pb --> ZNChadronic)"}; // Renamed David's "ZNC hadronic" to the proper code "ZNChadronic" Configurable centralityEstimatorForQA{"centralityEstimatorForQA", kCentFT0M, "Run 3 centrality estimator (0:CentFT0C, 1:CentFT0M, 2:CentFV0A)"}; // Default is FT0M - // (Now saving all centralities at the derived data level -- Makes them all available for consumer) - // (But still using this variable for QA histograms) ///////////////////////////////////////////// - Configurable doEventQA{"doEventQA", false, "do event QA histograms"}; + Configurable doEventQA{"doEventQA", true, "do event QA histograms"}; // Configurable qaCentrality{"qaCentrality", false, "qa centrality flag: check base raw values"}; - Configurable doCompleteTopoQA{"doCompleteTopoQA", false, "do topological variables QA histograms"}; // Includes doPlainTopoQA from derivedlambdakzeroanalysis - Configurable doV0KinematicQA{"doV0KinematicQA", false, "do kinematic variables QA histograms"}; + Configurable doTopoQA{"doTopoQA", true, "do topological variables QA histograms"}; + Configurable doV0KinematicQA{"doV0KinematicQA", true, "do kinematic variables QA histograms"}; Configurable doArmenterosQA{"doArmenterosQA", false, "do Armenteros QA histograms"}; Configurable doTPCQA{"doTPCQA", false, "do TPC QA histograms"}; Configurable doTOFQA{"doTOFQA", false, "do TOF QA histograms"}; - Configurable doEtaPhiQA{"doEtaPhiQA", false, "do Eta/Phi QA histograms for V0s and daughters"}; Configurable doJetKinematicsQA{"doJetKinematicsQA", false, "do pT,Eta,Phi QA histograms for jets"}; ///////////////////////////////////////////// @@ -331,6 +325,7 @@ struct lambdajetpolarizationions { ConfigurableAxis axisPtXi{"axisPtXi", {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 feeddown from Xi"}; ConfigurableAxis axisPtCoarse{"axisPtCoarse", {VARIABLE_WIDTH, 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 7.0f, 10.0f, 15.0f}, "pt axis for QA"}; ConfigurableAxis axisLambdaMass{"axisLambdaMass", {450, 1.08f, 1.15f}, ""}; // Default is {200, 1.101f, 1.131f} + ConfigurableAxis axisLambdaMassCoarse{"axisLambdaMassCoarse", {200, 1.101f, 1.131f}, ""}; ConfigurableAxis axisCentrality{"axisCentrality", {VARIABLE_WIDTH, 0.0f, 5.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 60.0f, 70.0f, 80.0f, 90.0f}, "Centrality"}; ConfigurableAxis axisNch{"axisNch", {500, 0.0f, +5000.0f}, "Number of charged particles"}; ConfigurableAxis axisIRBinning{"axisIRBinning", {500, 0, 50}, "Binning for the interaction rate (kHz)"}; @@ -661,6 +656,28 @@ struct lambdajetpolarizationions { lbl = "#color[16]{(off) " + lbl + "}"; hSelectionV0s->GetXaxis()->SetBinLabel(i + 1, lbl.c_str()); // First non-underflow bin is bin 1 } + + // TH2D: "selection flow" vs "Lambda invariant mass" (to investigate whether we are removing background or just signal) + auto h2dSelectionLambdaMass = histos.add("GeneralQA/h2dSelectionLambdaMass", "V0 selection flow vs M_{#Lambda}; ; M_{#Lambda} (GeV/#it{c}^{2})", kTH2D, + {{static_cast(v0LambdaSelectionLabels.size()), -0.5, static_cast(v0LambdaSelectionLabels.size()) - 0.5}, + axisConfigurations.axisLambdaMassCoarse}); + for (size_t i = 0; i < v0LambdaSelectionLabels.size(); ++i) { + auto lbl = v0LambdaSelectionLabels[i].label; + if (!v0LambdaSelectionLabels[i].enabled) + lbl = "#color[16]{(off) " + lbl + "}"; + h2dSelectionLambdaMass->GetXaxis()->SetBinLabel(i + 1, lbl.c_str()); + } + + // Same for AntiLambda mass hypothesis: + auto h2dSelectionAntiLambdaMass = histos.add("GeneralQA/h2dSelectionAntiLambdaMass", "V0 selection flow vs M_{#bar{#Lambda}}; ; M_{#bar{#Lambda}} (GeV/#it{c}^{2})", kTH2D, + {{static_cast(v0LambdaSelectionLabels.size()), -0.5, static_cast(v0LambdaSelectionLabels.size()) - 0.5}, + axisConfigurations.axisLambdaMassCoarse}); + for (size_t i = 0; i < v0LambdaSelectionLabels.size(); ++i) { + auto lbl = v0LambdaSelectionLabels[i].label; + if (!v0LambdaSelectionLabels[i].enabled) + lbl = "#color[16]{(off) " + lbl + "}"; + h2dSelectionAntiLambdaMass->GetXaxis()->SetBinLabel(i + 1, lbl.c_str()); + } //////////////////////////////////////////////// // Jet track candidate selection flow (analogous to hSelectionV0s): // Each label's "enabled" flag reflects whether the corresponding configurable @@ -722,11 +739,6 @@ struct lambdajetpolarizationions { histos.add("Lambda/h3dNegTOFdeltaTvsTrackPt", "h3dNegTOFdeltaTvsTrackPt", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTOFdeltaT}); } // (TODO: add collision association capabilities in MC) - if (doEtaPhiQA) { - histos.add("Lambda/h5dV0PhiVsEta", "h5dV0PhiVsEta", kTHnD, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisLambdaMass, axisConfigurations.axisPhi, axisConfigurations.axisEta}); - histos.add("Lambda/h5dPosPhiVsEta", "h5dPosPhiVsEta", kTHnD, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisLambdaMass, axisConfigurations.axisPhi, axisConfigurations.axisEta}); - histos.add("Lambda/h5dNegPhiVsEta", "h5dNegPhiVsEta", kTHnD, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisLambdaMass, axisConfigurations.axisPhi, axisConfigurations.axisEta}); - } } if (analyseAntiLambda) { histos.add("AntiLambda/h2dNbrOfAntiLambdaVsCentrality", "h2dNbrOfAntiLambdaVsCentrality", kTH2D, {axisConfigurations.axisCentrality, {10, -0.5f, 9.5f}}); @@ -761,11 +773,6 @@ struct lambdajetpolarizationions { histos.add("AntiLambda/h3dPosTOFdeltaTvsTrackPt", "h3dPosTOFdeltaTvsTrackPt", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTOFdeltaT}); histos.add("AntiLambda/h3dNegTOFdeltaTvsTrackPt", "h3dNegTOFdeltaTvsTrackPt", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTOFdeltaT}); } - if (doEtaPhiQA) { - histos.add("AntiLambda/h5dV0PhiVsEta", "h5dV0PhiVsEta", kTHnD, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisLambdaMass, axisConfigurations.axisPhi, axisConfigurations.axisEta}); - histos.add("AntiLambda/h5dPosPhiVsEta", "h5dPosPhiVsEta", kTHnD, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisLambdaMass, axisConfigurations.axisPhi, axisConfigurations.axisEta}); - histos.add("AntiLambda/h5dNegPhiVsEta", "h5dNegPhiVsEta", kTHnD, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisLambdaMass, axisConfigurations.axisPhi, axisConfigurations.axisEta}); - } } if (analyseLambda) { @@ -813,22 +820,7 @@ struct lambdajetpolarizationions { } } - if (doCompleteTopoQA) { - if (analyseLambda) { - histos.add("Lambda/h4dPosDCAToPV", "h4dPosDCAToPV", kTHnD, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisLambdaMass, axisConfigurations.axisDCAtoPV}); - histos.add("Lambda/h4dNegDCAToPV", "h4dNegDCAToPV", kTHnD, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisLambdaMass, axisConfigurations.axisDCAtoPV}); - histos.add("Lambda/h4dDCADaughters", "h4dDCADaughters", kTHnD, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisLambdaMass, axisConfigurations.axisDCAdau}); - histos.add("Lambda/h4dPointingAngle", "h4dPointingAngle", kTHnD, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisLambdaMass, axisConfigurations.axisPointingAngle}); - histos.add("Lambda/h4dV0Radius", "h4dV0Radius", kTHnD, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisLambdaMass, axisConfigurations.axisV0Radius}); - } - if (analyseAntiLambda) { - histos.add("AntiLambda/h4dPosDCAToPV", "h4dPosDCAToPV", kTHnD, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisLambdaMass, axisConfigurations.axisDCAtoPV}); - histos.add("AntiLambda/h4dNegDCAToPV", "h4dNegDCAToPV", kTHnD, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisLambdaMass, axisConfigurations.axisDCAtoPV}); - histos.add("AntiLambda/h4dDCADaughters", "h4dDCADaughters", kTHnD, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisLambdaMass, axisConfigurations.axisDCAdau}); - histos.add("AntiLambda/h4dPointingAngle", "h4dPointingAngle", kTHnD, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisLambdaMass, axisConfigurations.axisPointingAngle}); - histos.add("AntiLambda/h4dV0Radius", "h4dV0Radius", kTHnD, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisLambdaMass, axisConfigurations.axisV0Radius}); - } - + if (doTopoQA) { // For all received candidates: histos.add("V0KinematicQA/hPosDCAToPV", "hPosDCAToPV", kTH1D, {axisConfigurations.axisDCAtoPV}); histos.add("V0KinematicQA/hNegDCAToPV", "hNegDCAToPV", kTH1D, {axisConfigurations.axisDCAtoPV}); @@ -984,22 +976,21 @@ struct lambdajetpolarizationions { int binValue = -1; // Starts at x=-1, which will go to bin 0 (underflow) in the definition of hSelectionV0s // Made it like this because we use ++binValue when filling, so the first filled // bin will always be x=0 due to operator precedence. - HistogramRegistry* histos = nullptr; // Had to pass the histos group to this struct, as it was not visible to the members of this struct - - void resetForNewV0() { binValue = -1; } - // Advance to targetBinX, filling all intermediate bins. - // Use this for DISABLED cuts within a single hypothesis - // (shows pass-through count as a flat line, making it visually - // clear that the stage was not active). - // (Replaces N dummy fill() calls) - void fillUpTo(int targetBinX) - { - while (binValue < targetBinX) - histos->fill(HIST("GeneralQA/hSelectionV0s"), ++binValue); + HistogramRegistry* histos = nullptr; // Had to pass the histos group to this struct, as it was not visible to the members of the struct + float mL = -1.f; // mLambda for current V0, -1 at initialization + float mAL = -1.f; // mAntiLambda for current V0, -1 at initialization + void resetForNewV0(float massLambda, float massAntiLambda) { binValue = -1; mL = massLambda; mAL = massAntiLambda; } + void advanceTo(int targetBinX) { binValue = targetBinX - 1; } // next fill() lands at targetBin. Needed to deal with early exits at isLambda vs isAntiLambda checks + + void fill() { + histos->fill(HIST("GeneralQA/hSelectionV0s"), ++binValue); // Hardcoded hSelectionV0s histogram, as it will not change. Increments before filling, by default + histos->fill(HIST("GeneralQA/h2dSelectionLambdaMass"), binValue, mL); + histos->fill(HIST("GeneralQA/h2dSelectionAntiLambdaMass"), binValue, mAL); } - - void advanceTo(int targetBinX) { binValue = targetBinX - 1; } // next fill() lands at targetBin. Needed to deal with early exits at isLambda vs isAntiLambda checks - void fill() { histos->fill(HIST("GeneralQA/hSelectionV0s"), ++binValue); } // Hardcoded hSelectionV0s histogram, as it will not change. Increments before filling, by default + // Use this for DISABLED cuts within a single hypothesis + // (shows pass-through count as a flat line, making it visually clear that the stage was not active. Replaces N dummy fill() calls) + // fillUpTo advances through disabled bins, filling all three histograms uniformly. + void fillUpTo(int targetBinX) { while (binValue < targetBinX) fill(); } }; V0SelectionFlowCounter V0SelCounter{-1, &histos}; // Could initialize with any index (resetForNewV0 is always called for a new V0 anyways) // Calculating some bins, for convenience: @@ -1794,7 +1785,7 @@ struct lambdajetpolarizationions { uint NNonAmbiguous = 0; uint NAmbiguous = 0; for (auto const& v0 : V0s) { - V0SelCounter.resetForNewV0(); + V0SelCounter.resetForNewV0(v0.mLambda(), v0.mAntiLambda()); V0SelCounter.fill(); // Fill for all v0 candidates if (doArmenterosQA) histos.fill(HIST("GeneralQA/h2dArmenterosAll"), v0.alpha(), v0.qtarm()); // fill AP plot for all V0s @@ -1908,7 +1899,7 @@ struct lambdajetpolarizationions { } } - if (doCompleteTopoQA) { + if (doTopoQA) { // Remaking these variables outside of the passesLambdaLambdaBarHypothesis. Loses performance, but that should be OK for QA histos.fill(HIST("V0KinematicQA/hPosDCAToPV"), v0.dcapostopv()); histos.fill(HIST("V0KinematicQA/hNegDCAToPV"), v0.dcanegtopv()); @@ -1961,11 +1952,6 @@ struct lambdajetpolarizationions { histos.fill(HIST("Lambda/h3dNegTOFdeltaTvsTrackPt"), centrality, v0.negativept(), v0.negTOFDeltaTLaPi()); } } - if (doEtaPhiQA) { - histos.fill(HIST("Lambda/h5dV0PhiVsEta"), centrality, v0pt, v0.mLambda(), v0.phi(), v0.eta()); - histos.fill(HIST("Lambda/h5dPosPhiVsEta"), centrality, v0.positivept(), v0.mLambda(), v0.positivephi(), v0.positiveeta()); - histos.fill(HIST("Lambda/h5dNegPhiVsEta"), centrality, v0.negativept(), v0.mLambda(), v0.negativephi(), v0.negativeeta()); - } } if (isAntiLambda && analyseAntiLambda) { histos.fill(HIST("hMassAntiLambda"), v0.mAntiLambda()); @@ -2009,11 +1995,6 @@ struct lambdajetpolarizationions { histos.fill(HIST("AntiLambda/h3dNegTOFdeltaTvsTrackPt"), centrality, v0.negativept(), v0.negTOFDeltaTLaPr()); } } - if (doEtaPhiQA) { - histos.fill(HIST("AntiLambda/h5dV0PhiVsEta"), centrality, v0pt, v0.mAntiLambda(), v0.phi(), v0.eta()); - histos.fill(HIST("AntiLambda/h5dPosPhiVsEta"), centrality, v0.positivept(), v0.mAntiLambda(), v0.positivephi(), v0.positiveeta()); - histos.fill(HIST("AntiLambda/h5dNegPhiVsEta"), centrality, v0.negativept(), v0.mAntiLambda(), v0.negativephi(), v0.negativeeta()); - } } } // end CompleteTopoQA } // end V0s loop diff --git a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx index 33b58145433..a42330a1f16 100644 --- a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx @@ -174,6 +174,43 @@ enum CentEstimator { // would not know to end each statement with a semicolon): #define APPLY_HISTO_FILL(NAME, ...) histos.fill(HIST(NAME), __VA_ARGS__); + +// Delta Method Fill Lists +#define DELTA_INTEGRATED_FILL_LIST(X, FOLDER, r, n) \ + X(FOLDER "/DeltaMethod/hIntegrated", 0.5, r) \ + X(FOLDER "/DeltaMethod/hIntegrated", 1.5, (double)(n)) \ + X(FOLDER "/DeltaMethod/hIntegrated", 2.5, (r)*(r)) \ + X(FOLDER "/DeltaMethod/hIntegrated", 3.5, (double)((n)*(n))) \ + X(FOLDER "/DeltaMethod/hIntegrated", 4.5, (r)*(n)) + +#define DELTA_2D_FILL_LIST(X, FOLDER, HIST_NAME, center, r, n) \ + X(FOLDER "/DeltaMethod/" HIST_NAME, center, 0.5, r) \ + X(FOLDER "/DeltaMethod/" HIST_NAME, center, 1.5, (double)(n)) \ + X(FOLDER "/DeltaMethod/" HIST_NAME, center, 2.5, (r)*(r)) \ + X(FOLDER "/DeltaMethod/" HIST_NAME, center, 3.5, (double)((n)*(n))) \ + X(FOLDER "/DeltaMethod/" HIST_NAME, center, 4.5, (r)*(n)) + +// Master flush macro to dump an event tracker into the histograms: +#define FLUSH_DELTA_TRACKER(FOLDER, TRACKER, AXIS_PT, AXIS_MASS, AXIS_DTHETA) \ + if ((TRACKER).n_int > 0) { \ + DELTA_INTEGRATED_FILL_LIST(APPLY_HISTO_FILL, FOLDER, (TRACKER).r_int, (TRACKER).n_int) \ + } \ + for (const auto& [bin, r_val] : (TRACKER).r_pt) { \ + int n_val = (TRACKER).n_pt.at(bin); \ + double center = (AXIS_PT)->GetBinCenter(bin); \ + DELTA_2D_FILL_LIST(APPLY_HISTO_FILL, FOLDER, "h2dLambdaPtVsDeltaComp", center, r_val, n_val) \ + } \ + for (const auto& [bin, r_val] : (TRACKER).r_mass) { \ + int n_val = (TRACKER).n_mass.at(bin); \ + double center = (AXIS_MASS)->GetBinCenter(bin); \ + DELTA_2D_FILL_LIST(APPLY_HISTO_FILL, FOLDER, "h2dMassVsDeltaComp", center, r_val, n_val) \ + } \ + for (const auto& [bin, r_val] : (TRACKER).r_dtheta) { \ + int n_val = (TRACKER).n_dtheta.at(bin); \ + double center = (AXIS_DTHETA)->GetBinCenter(bin); \ + DELTA_2D_FILL_LIST(APPLY_HISTO_FILL, FOLDER, "h2dDeltaThetaVsDeltaComp", center, r_val, n_val) \ + } + struct lambdajetpolarizationionsderived { // Define histogram registries: @@ -192,7 +229,10 @@ struct lambdajetpolarizationionsderived { Configurable forcePolSignQA{"forcePolSignQA", false, "force antiLambda decay constant to be positive: should kill all the signal, if any. For QA"}; Configurable forcePerpToJet{"forcePerpToJet", false, "force jet direction to be perpendicular to jet estimator. For QA"}; Configurable forceJetDirectionSmudge{"forceJetDirectionSmudge", false, "fluctuate jet direction by 10% of R around original axis. For QA (tests sensibility)"}; - Configurable jetRForSmuding{"jetRForSmuding", 0.4, "QA quantity: the chosen R scale for the jet direction smudge"}; + // Configurable jetRForSmudging{"jetRForSmudging", 0.4, "QA quantity: the chosen R scale for the jet direction smudge"}; // Superseeded by jetR: kept the same scale in analysis and QA + Configurable jetR{"jetR", 0.4, "Radius of the jet"}; // Provide manually, please. + Configurable minLeadParticlePt{"minLeadParticlePt", 2.0f, "Minimum Pt for a lead track to be considered a valid proxy for a jet"}; + Configurable minLeadJetPt{"minLeadJetPt", 10.0f, "Minimum Pt for leading jet to be considered valid (may be more restrictive than TableProducer)"}; ///////////////////////// // Configurable blocks: @@ -233,6 +273,9 @@ struct lambdajetpolarizationionsderived { // (TODO: add a lambdaPt axis that is pre-selected only on the 0.5 to 1.5 Pt region for the Ring observable with lambda cuts to not store a huge histogram with empty bins by construction) ConfigurableAxis axisCentrality{"axisCentrality", {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.0f}, "Centrality"}; + + // For the delta method error propagation (slightly better than just SEM error propagation with TProfiles): + ConfigurableAxis axisDeltaComponents{"axisDeltaComponents", {5, 0.0, 5.0}, "0: r_k, 1: n_k, 2: r_k^2, 3: n_k^2, 4: r_k*n_k"}; } axisConfigurations; // Helper functions: @@ -248,6 +291,25 @@ struct lambdajetpolarizationionsderived { return phi; } + // A small tracker struct for convenience -- Accumulates values for the Delta Method error estimator: + struct EventDeltaTracker { + double r_int = 0.0; int n_int = 0; + std::map r_pt, r_mass, r_dtheta; + std::map n_pt, n_mass, n_dtheta; + + void addV0(double ringObs, int binPt, int binMass, int binDTheta) { + r_int += ringObs; n_int += 1; + r_pt[binPt] += ringObs; n_pt[binPt] += 1; + r_mass[binMass] += ringObs; n_mass[binMass] += 1; + r_dtheta[binDTheta] += ringObs; n_dtheta[binDTheta] += 1; + } + }; + + // Axis pointers for Delta Method binning (fetched once in init, declared once here) + TAxis* mAxisPt = nullptr; + TAxis* mAxisMass = nullptr; + TAxis* mAxisDTheta = nullptr; + void init(InitContext const&) { // Ring observable histograms: @@ -386,6 +448,16 @@ struct lambdajetpolarizationionsderived { // Added to a separate folder for further control (changed the usage of the "folder" string): // histos.add(("QA_Numbers/" + folder + "/hValidLeadJets").c_str(), "hValidLeadJets", kTH1D, {{1,0,1}}); // TODO: Add "frequency of jets per pT" histograms either here or in the TableProducer + + + // Estimating error bars with the Delta Method for = A/B: + // 1D Delta Method for Integrated observable: + histos.add((folder + "/DeltaMethod/hIntegrated").c_str(), "Delta Method Accumulators Integrated", kTH1D, {axisConfigurations.axisDeltaComponents}); + + // 2D Delta Method for Differentials + histos.add((folder + "/DeltaMethod/h2dDeltaThetaVsDeltaComp").c_str(), "Delta Method vs DeltaTheta", kTH2D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisDeltaComponents}); + histos.add((folder + "/DeltaMethod/h2dLambdaPtVsDeltaComp").c_str(), "Delta Method vs Lambda pT", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisDeltaComponents}); + histos.add((folder + "/DeltaMethod/h2dMassVsDeltaComp").c_str(), "Delta Method vs Mass", kTH2D, {axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisDeltaComponents}); }; // Execute local lambda to register histogram families: addRingObservableFamily("Ring"); @@ -393,34 +465,106 @@ struct lambdajetpolarizationionsderived { addRingObservableFamily("JetKinematicCuts"); addRingObservableFamily("JetAndLambdaKinematicCuts"); - histos.add("pRingCuts", "pRingCuts; ;<#it{R}>", kTProfile, {{4, 0, 4}}); - histos.get(HIST("pRingCuts"))->GetXaxis()->SetBinLabel(1, "All #Lambda"); - histos.get(HIST("pRingCuts"))->GetXaxis()->SetBinLabel(2, "p_{T}^{#Lambda}@[0.5,1.5],|y_{#Lambda}|<0.5"); // (v0pt > 0.5 && v0pt < 1.5) && std::abs(lambdaRapidity) < 0.5; - histos.get(HIST("pRingCuts"))->GetXaxis()->SetBinLabel(3, "|Jet_{#eta}|<0.5"); - histos.get(HIST("pRingCuts"))->GetXaxis()->SetBinLabel(4, "#Lambda + Jet cuts"); + histos.add("IntegratedCuts/pRingCuts", "pRingCuts; ;<#it{R}>", kTProfile, {{4, 0, 4}}); + histos.get(HIST("IntegratedCuts/pRingCuts"))->GetXaxis()->SetBinLabel(1, "All #Lambda"); + histos.get(HIST("IntegratedCuts/pRingCuts"))->GetXaxis()->SetBinLabel(2, "p_{T}^{#Lambda}@[0.5,1.5],|y_{#Lambda}|<0.5"); // (v0pt > 0.5 && v0pt < 1.5) && std::abs(lambdaRapidity) < 0.5; + histos.get(HIST("IntegratedCuts/pRingCuts"))->GetXaxis()->SetBinLabel(3, "|Jet_{#eta}|<0.5"); + histos.get(HIST("IntegratedCuts/pRingCuts"))->GetXaxis()->SetBinLabel(4, "#Lambda + Jet cuts"); + + // Same for subleading jet and leading particle: + histos.add("IntegratedCuts/pRingCutsSubLeadingJet", "pRingCutsSubLeadingJet; ;<#it{R}>", kTProfile, {{4, 0, 4}}); + histos.get(HIST("IntegratedCuts/pRingCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(1, "All #Lambda"); + histos.get(HIST("IntegratedCuts/pRingCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(2, "p_{T,#Lambda}@[0.5,1.5],|y_{#Lambda}|<0.5"); + histos.get(HIST("IntegratedCuts/pRingCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(3, "|SubJet_{#eta}|<0.5"); + histos.get(HIST("IntegratedCuts/pRingCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(4, "#Lambda + SubJet cuts"); + + histos.add("IntegratedCuts/pRingCutsLeadingP", "pRingCutsLeadingP; ;<#it{R}>", kTProfile, {{4, 0, 4}}); + histos.get(HIST("IntegratedCuts/pRingCutsLeadingP"))->GetXaxis()->SetBinLabel(1, "All #Lambda"); + histos.get(HIST("IntegratedCuts/pRingCutsLeadingP"))->GetXaxis()->SetBinLabel(2, "p_{T}^{#Lambda}@[0.5,1.5],|y_{#Lambda}|<0.5"); + histos.get(HIST("IntegratedCuts/pRingCutsLeadingP"))->GetXaxis()->SetBinLabel(3, "|LeadP_{#eta}|<0.5"); + histos.get(HIST("IntegratedCuts/pRingCutsLeadingP"))->GetXaxis()->SetBinLabel(4, "#Lambda + LeadP cuts"); + + // Counters for each case to understand statistics loss: + histos.add("IntegratedCuts/hCountCuts", "hCountCuts; ;N V0s", kTH1D, {{4, 0, 4}}); + histos.get(HIST("IntegratedCuts/hCountCuts"))->GetXaxis()->SetBinLabel(1, "All #Lambda"); + histos.get(HIST("IntegratedCuts/hCountCuts"))->GetXaxis()->SetBinLabel(2, "p_{T}^{#Lambda}@[0.5,1.5],|y_{#Lambda}|<0.5"); // (v0pt > 0.5 && v0pt < 1.5) && std::abs(lambdaRapidity) < 0.5; + histos.get(HIST("IntegratedCuts/hCountCuts"))->GetXaxis()->SetBinLabel(3, "|Jet_{#eta}|<0.5"); + histos.get(HIST("IntegratedCuts/hCountCuts"))->GetXaxis()->SetBinLabel(4, "#Lambda + Jet cuts"); // Same for subleading jet and leading particle: - histos.add("pRingCutsSubLeadingJet", "pRingCutsSubLeadingJet; ;<#it{R}>", kTProfile, {{4, 0, 4}}); - histos.get(HIST("pRingCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(1, "All #Lambda"); - histos.get(HIST("pRingCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(2, "p_{T,#Lambda}@[0.5,1.5],|y_{#Lambda}|<0.5"); - histos.get(HIST("pRingCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(3, "|SubJet_{#eta}|<0.5"); - histos.get(HIST("pRingCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(4, "#Lambda + SubJet cuts"); - - histos.add("pRingCutsLeadingP", "pRingCutsLeadingP; ;<#it{R}>", kTProfile, {{4, 0, 4}}); - histos.get(HIST("pRingCutsLeadingP"))->GetXaxis()->SetBinLabel(1, "All #Lambda"); - histos.get(HIST("pRingCutsLeadingP"))->GetXaxis()->SetBinLabel(2, "p_{T}^{#Lambda}@[0.5,1.5],|y_{#Lambda}|<0.5"); - histos.get(HIST("pRingCutsLeadingP"))->GetXaxis()->SetBinLabel(3, "|LeadP_{#eta}|<0.5"); - histos.get(HIST("pRingCutsLeadingP"))->GetXaxis()->SetBinLabel(4, "#Lambda + LeadP cuts"); + histos.add("IntegratedCuts/hCountCutsSubLeadingJet", "hCountCutsSubLeadingJet; ;N V0s", kTH1D, {{4, 0, 4}}); + histos.get(HIST("IntegratedCuts/hCountCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(1, "All #Lambda"); + histos.get(HIST("IntegratedCuts/hCountCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(2, "p_{T,#Lambda}@[0.5,1.5],|y_{#Lambda}|<0.5"); + histos.get(HIST("IntegratedCuts/hCountCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(3, "|SubJet_{#eta}|<0.5"); + histos.get(HIST("IntegratedCuts/hCountCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(4, "#Lambda + SubJet cuts"); + + histos.add("IntegratedCuts/hCountCutsLeadingP", "hCountCutsLeadingP; ;N V0s", kTH1D, {{4, 0, 4}}); + histos.get(HIST("IntegratedCuts/hCountCutsLeadingP"))->GetXaxis()->SetBinLabel(1, "All #Lambda"); + histos.get(HIST("IntegratedCuts/hCountCutsLeadingP"))->GetXaxis()->SetBinLabel(2, "p_{T}^{#Lambda}@[0.5,1.5],|y_{#Lambda}|<0.5"); + histos.get(HIST("IntegratedCuts/hCountCutsLeadingP"))->GetXaxis()->SetBinLabel(3, "|LeadP_{#eta}|<0.5"); + histos.get(HIST("IntegratedCuts/hCountCutsLeadingP"))->GetXaxis()->SetBinLabel(4, "#Lambda + LeadP cuts"); + + + // Integrated observable dependent on jet proxy #eta to unfold possible asymmetries in detector: + histos.add("ProxyEta/pRingEtaCuts", "pRingEtaCuts; ;<#it{R}>", kTProfile, {{15, 0, 15}}); + histos.get(HIST("ProxyEta/pRingEtaCuts"))->GetXaxis()->SetBinLabel(1, "All #Lambda"); + histos.get(HIST("ProxyEta/pRingEtaCuts"))->GetXaxis()->SetBinLabel(2, "#eta_{Jet} #geq 0"); + histos.get(HIST("ProxyEta/pRingEtaCuts"))->GetXaxis()->SetBinLabel(3, "#eta_{Jet} < 0"); + histos.get(HIST("ProxyEta/pRingEtaCuts"))->GetXaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); + histos.get(HIST("ProxyEta/pRingEtaCuts"))->GetXaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); + histos.get(HIST("ProxyEta/pRingEtaCuts"))->GetXaxis()->SetBinLabel(6, "#eta_{Jet} #geq 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("ProxyEta/pRingEtaCuts"))->GetXaxis()->SetBinLabel(7, "#eta_{Jet} #geq 0, #eta_{#Lambda} < 0"); + histos.get(HIST("ProxyEta/pRingEtaCuts"))->GetXaxis()->SetBinLabel(8, "#eta_{Jet} < 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("ProxyEta/pRingEtaCuts"))->GetXaxis()->SetBinLabel(9, "#eta_{Jet} < 0, #eta_{#Lambda} < 0"); + histos.get(HIST("ProxyEta/pRingEtaCuts"))->GetXaxis()->SetBinLabel(10, "#eta_{Jet} > R"); + histos.get(HIST("ProxyEta/pRingEtaCuts"))->GetXaxis()->SetBinLabel(11, "#eta_{Jet} < -R"); + histos.get(HIST("ProxyEta/pRingEtaCuts"))->GetXaxis()->SetBinLabel(12, "#eta_{Jet} > R, #eta_{#Lambda} #geq 0"); + histos.get(HIST("ProxyEta/pRingEtaCuts"))->GetXaxis()->SetBinLabel(13, "#eta_{Jet} > R, #eta_{#Lambda} < 0"); + histos.get(HIST("ProxyEta/pRingEtaCuts"))->GetXaxis()->SetBinLabel(14, "#eta_{Jet} < -R, #eta_{#Lambda} #geq 0"); + histos.get(HIST("ProxyEta/pRingEtaCuts"))->GetXaxis()->SetBinLabel(15, "#eta_{Jet} < -R, #eta_{#Lambda} < 0"); + + histos.add("ProxyEta/pRingEtaCutsSubLeadingJet", "pRingEtaCutsSubLeadingJet; ;<#it{R}>", kTProfile, {{15, 0, 15}}); + histos.get(HIST("ProxyEta/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(1, "All #Lambda"); + histos.get(HIST("ProxyEta/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(2, "#eta_{SubJet} #geq 0"); + histos.get(HIST("ProxyEta/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(3, "#eta_{SubJet} < 0"); + histos.get(HIST("ProxyEta/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); + histos.get(HIST("ProxyEta/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); + histos.get(HIST("ProxyEta/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(6, "#eta_{SubJet} #geq 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("ProxyEta/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(7, "#eta_{SubJet} #geq 0, #eta_{#Lambda} < 0"); + histos.get(HIST("ProxyEta/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(8, "#eta_{SubJet} < 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("ProxyEta/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(9, "#eta_{SubJet} < 0, #eta_{#Lambda} < 0"); + histos.get(HIST("ProxyEta/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(10, "#eta_{SubJet} > R"); + histos.get(HIST("ProxyEta/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(11, "#eta_{SubJet} < -R"); + histos.get(HIST("ProxyEta/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(12, "#eta_{SubJet} > R, #eta_{#Lambda} #geq 0"); + histos.get(HIST("ProxyEta/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(13, "#eta_{SubJet} > R, #eta_{#Lambda} < 0"); + histos.get(HIST("ProxyEta/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(14, "#eta_{SubJet} < -R, #eta_{#Lambda} #geq 0"); + histos.get(HIST("ProxyEta/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(15, "#eta_{SubJet} < -R, #eta_{#Lambda} < 0"); + + histos.add("ProxyEta/pRingEtaCutsLeadingP", "pRingEtaCutsLeadingP; ;<#it{R}>", kTProfile, {{9, 0, 9}}); + histos.get(HIST("ProxyEta/pRingEtaCutsLeadingP"))->GetXaxis()->SetBinLabel(1, "All #Lambda"); + histos.get(HIST("ProxyEta/pRingEtaCutsLeadingP"))->GetXaxis()->SetBinLabel(2, "#eta_{LeadP} #geq 0"); + histos.get(HIST("ProxyEta/pRingEtaCutsLeadingP"))->GetXaxis()->SetBinLabel(3, "#eta_{LeadP} < 0"); + histos.get(HIST("ProxyEta/pRingEtaCutsLeadingP"))->GetXaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); + histos.get(HIST("ProxyEta/pRingEtaCutsLeadingP"))->GetXaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); + histos.get(HIST("ProxyEta/pRingEtaCutsLeadingP"))->GetXaxis()->SetBinLabel(6, "#eta_{LeadP} #geq 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("ProxyEta/pRingEtaCutsLeadingP"))->GetXaxis()->SetBinLabel(7, "#eta_{LeadP} #geq 0, #eta_{#Lambda} < 0"); + histos.get(HIST("ProxyEta/pRingEtaCutsLeadingP"))->GetXaxis()->SetBinLabel(8, "#eta_{LeadP} < 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("ProxyEta/pRingEtaCutsLeadingP"))->GetXaxis()->SetBinLabel(9, "#eta_{LeadP} < 0, #eta_{#Lambda} < 0"); // Integrated observable for events with NLambda+NAntiLambda V0s per event // (an interesting measurement of correlation between and Lambda-like V0s multiplicity. A proxy of covariance) // (calculated for leading jets only) - histos.add("pRingVsNV0s", "pRingVsNV0s; N_{#Lambda}+N_{#bar{#Lambda}};<#it{R}>", kTProfile, {{20, 0, 20}}); + histos.add("IntegratedCuts/pRingVsNV0s", "pRingVsNV0s; N_{#Lambda}+N_{#bar{#Lambda}};<#it{R}>", kTProfile, {{20, 0, 20}}); // Leading Jet QA: histos.add("JetKinematicsQA/hLeadJetEta", "hLeadJetEta", kTH1D, {axisConfigurations.axisEta}); histos.add("JetKinematicsQA/hSubLeadJetEta", "hSubLeadJetEta", kTH1D, {axisConfigurations.axisEta}); histos.add("JetKinematicsQA/hLeadPEta", "hLeadPEta", kTH1D, {axisConfigurations.axisEta}); + + // Fetch the X-axes from one of the families (since they all share the same ConfigurableAxis binning) + mAxisPt = histos.get(HIST("Ring/DeltaMethod/h2dLambdaPtVsDeltaComp"))->GetXaxis(); + mAxisMass = histos.get(HIST("Ring/DeltaMethod/h2dMassVsDeltaComp"))->GetXaxis(); + mAxisDTheta = histos.get(HIST("Ring/DeltaMethod/h2dDeltaThetaVsDeltaComp"))->GetXaxis(); } // Helper to get centrality (same from TableProducer, thanks to templating!): @@ -465,13 +609,14 @@ struct lambdajetpolarizationionsderived { if (!v0sInColl.size()) continue; - // 2) We require at least a leading particle, then we get the leading jet only if it exists: + // 2) We require at least one leading particle: // (The goal is to see how diluted the signal gets with events which don't even have a loose FastJet jet) // (The leading particle is built from all tracks that passed the pseudojet // selection, so it exists whenever FastJet was run on this collision. // Events that have a leading jet always have a leading particle too, but // the converse is not true: events can have a leading particle with no jet // if no jet survives the pT threshold/the background subtraction) + // (At least that is the case when minLeadParticlePt = 0) float leadPPt = -1.; // pT = -1 means "table entry not found for this collision". float leadPEta = 0.; float leadPPhi = 0.; @@ -488,13 +633,38 @@ struct lambdajetpolarizationionsderived { leadPPz = lp.leadParticlePz(); break; } - // Discard events with no leading particle (FastJet didn't even run in these cases!): - if (leadPPt < 0.) - continue; - histos.fill(HIST("JetKinematicsQA/hLeadPEta"), leadPEta); - - // Build leading particle unit vector, outside the V0 loop for performance: - XYZVector leadPUnitVec = XYZVector(leadPPx, leadPPy, leadPPz).Unit(); + // // Discard events with no leading particle (FastJet didn't even run in these cases!): + // if (leadPPt < 0.) + // continue; + + // Apply minimum pT selection for the leading particle (not necessarily the same as in derived data builder. Can be a stricter cut!): + const bool hasValidLeadingP = leadPPt > minLeadParticlePt; + + // Build leading particle unit vector, outside the V0 loop for performance. + XYZVector leadPUnitVec(1., 0., 0.); // dummy (overwritten below when hasValidLeadingP) + if (hasValidLeadingP) { + histos.fill(HIST("JetKinematicsQA/hLeadPEta"), leadPEta); + leadPUnitVec = XYZVector(leadPPx, leadPPy, leadPPz).Unit(); + // QA: same direction-smearing/perp logic as for the leading jet estimator. + // The hLeadPEta histogram above intentionally uses the unmodified direction. + if (forcePerpToJet) { + XYZVector refVec(1., 0., 0.); + if (std::abs(leadPUnitVec.Dot(refVec)) > 0.99) + refVec = XYZVector(0., 1., 0.); + XYZVector perpVec = leadPUnitVec.Cross(refVec).Unit(); + double randomAngle = randomGen.Uniform(0., o2::constants::math::TwoPI); + leadPUnitVec = perpVec * std::cos(randomAngle) + leadPUnitVec.Cross(perpVec) * std::sin(randomAngle); + } else if (forceJetDirectionSmudge) { + XYZVector refVec(1., 0., 0.); + if (std::abs(leadPUnitVec.Dot(refVec)) > 0.99) + refVec = XYZVector(0., 1., 0.); + XYZVector perpVec = leadPUnitVec.Cross(refVec).Unit(); + double smearAzimuth = randomGen.Uniform(0., o2::constants::math::TwoPI); + XYZVector smearAxis = perpVec * std::cos(smearAzimuth) + leadPUnitVec.Cross(perpVec) * std::sin(smearAzimuth); + double smearAngle = std::abs(randomGen.Gaus(0., 0.05 * jetR)); + leadPUnitVec = leadPUnitVec * std::cos(smearAngle) + smearAxis.Cross(leadPUnitVec) * std::sin(smearAngle); + } + } // 3) Checking if the event has a leading jet: auto jetsInColl = jets.sliceBy(perColJets, collId); @@ -520,7 +690,8 @@ struct lambdajetpolarizationionsderived { } // Some useful bools to check if we have a leading jet and a subleading jet: - const bool hasValidLeadingJet = leadingJetPt > 0.; + // const bool hasValidLeadingJet = leadingJetPt > 0.; + const bool hasValidLeadingJet = leadingJetPt > minLeadJetPt; // Finer control on jet momentum const bool hasValidSubJet = subleadingJetPt > 0.; // Build jet vectors (only when the corresponding jet exists): @@ -571,12 +742,12 @@ struct lambdajetpolarizationionsderived { // ~5% see a displacement > 0.1*R (a very "badly determined jet", for our QA purposes) // std::abs() folds the symmetric Gaussian onto a half-normal ([0, inf)) // -- R is not really an angle: just gives me a scale for the angular shift I am performing. - // -- This may pose problems for forward jets: a small displacemente in \theta becomes a large displacement in \eta space - double smearSigma = 0.05 * jetRForSmuding; + // -- This may pose problems for forward jets: a small displacement in \theta becomes a large displacement in \eta space + double smearSigma = 0.05 * jetR; double smearAngle = std::abs(randomGen.Gaus(0., smearSigma)); // Step 3: rotate the jet axis by smearAngle around smearAxis. - // Rodrigues is v_rot = v*cos(theta) + (k \croos v)*sin(theta) + k*(k \cdot v)*(1-cos(theta)) + // Rodrigues is v_rot = v*cos(theta) + (k \cross v)*sin(theta) + k*(k \cdot v)*(1-cos(theta)) // But the last term vanishes because smearAxis is perpendicular to leadingJetUnitVec: leadingJetUnitVec = leadingJetUnitVec * std::cos(smearAngle) + smearAxis.Cross(leadingJetUnitVec) * std::sin(smearAngle); // Also, rotation preserves the norm, so no re-normalisation is needed for this to be a unit vector. @@ -589,16 +760,47 @@ struct lambdajetpolarizationionsderived { if (hasValidSubJet) { subleadingJetEta = subleadingJet->jetEta(); subleadingJetPhi = subleadingJet->jetPhi(); - // Using internal getters to make code cleaner: subJetUnitVec = XYZVector(subleadingJet->jetPx(), subleadingJet->jetPy(), subleadingJet->jetPz()).Unit(); - - histos.fill(HIST("JetKinematicsQA/hSubLeadJetEta"), subleadingJetEta); + histos.fill(HIST("JetKinematicsQA/hSubLeadJetEta"), subleadingJetEta); // Unmodified direction + // QA: same direction-smearing/perp logic as for the leading jet estimator. + if (forcePerpToJet) { + XYZVector refVec(1., 0., 0.); + if (std::abs(subJetUnitVec.Dot(refVec)) > 0.99) + refVec = XYZVector(0., 1., 0.); + XYZVector perpVec = subJetUnitVec.Cross(refVec).Unit(); + double randomAngle = randomGen.Uniform(0., o2::constants::math::TwoPI); + subJetUnitVec = perpVec * std::cos(randomAngle) + subJetUnitVec.Cross(perpVec) * std::sin(randomAngle); + } else if (forceJetDirectionSmudge) { + XYZVector refVec(1., 0., 0.); + if (std::abs(subJetUnitVec.Dot(refVec)) > 0.99) + refVec = XYZVector(0., 1., 0.); + XYZVector perpVec = subJetUnitVec.Cross(refVec).Unit(); + double smearAzimuth = randomGen.Uniform(0., o2::constants::math::TwoPI); + XYZVector smearAxis = perpVec * std::cos(smearAzimuth) + subJetUnitVec.Cross(perpVec) * std::sin(smearAzimuth); + double smearAngle = std::abs(randomGen.Gaus(0., 0.05 * jetR)); + subJetUnitVec = subJetUnitVec * std::cos(smearAngle) + smearAxis.Cross(subJetUnitVec) * std::sin(smearAngle); + } } // (jet eta cuts only meaningful when the jet actually exists) const bool kinematicJetCheck = hasValidLeadingJet && (std::abs(leadingJetEta) < 0.5); const bool kinematic2ndJetCheck = hasValidSubJet && (std::abs(subleadingJetEta) < 0.5); - const bool kinematicLeadPCheck = std::abs(leadPEta) < 0.5; + const bool kinematicLeadPCheck = hasValidLeadingP && (std::abs(leadPEta) < 0.5); + + // Quick bools that are useful for detector asymmetry QA: + const bool jetEtaPos = hasValidLeadingJet && (leadingJetEta >= 0.); // Only perform >= check if has validJet + const bool subJetEtaPos = hasValidSubJet && (subleadingJetEta >= 0.); + const bool leadPEtaPos = hasValidLeadingP && (leadPEta >= 0.); + + // Stricter QA version of the bools -- Jets have a radius that makes it possible eta_{jet} > 0, yet half its tracks are in eta < 0 + // (This does not apply to leading particles, obviously. They have no substructure in eta) + const bool jetEtaStrict = hasValidLeadingJet && (std::abs(leadingJetEta) >= jetR); + const bool subJetEtaStrict = hasValidSubJet && (std::abs(subleadingJetEta) >= jetR); + // If one was to define bools for each side of the detector (not needed in the current if-else structure on TProfile fills) + // const bool jetEtaStrictPos = jetEtaPos && jetEtaStrict; + // const bool jetEtaStrictNeg = !jetEtaPos && jetEtaStrict; + // const bool subJetEtaStrictPos = subJetEtaPos && subJetEtaStrict; + // const bool subJetEtaStrictNeg = !subJetEtaPos && subJetEtaStrict; // Fetching number of Lambda-like V0s in collision (must be known before full loop, to fill "pRingVsNV0s"): int NLambdaLikeV0s = 0; @@ -608,6 +810,8 @@ struct lambdajetpolarizationionsderived { } } + // Initialize delta method accumulators: + EventDeltaTracker trackRing, trackRingKinCuts, trackJetKinCuts, trackJetLambdaKinCuts; for (auto const& v0 : v0sInColl) { const bool isLambda = v0.isLambda(); const bool isAntiLambda = v0.isAntiLambda(); @@ -655,19 +859,24 @@ struct lambdajetpolarizationionsderived { //////////////////////////////////////////// // Ring observable: Leading particle proxy - // Always computed -- leading particle existence is guaranteed by the second check above + // Only computed when a valid leading particle exists (pT > minLeadParticlePt) //////////////////////////////////////////// - // Cross product - XYZVector crossLeadP = leadPUnitVec.Cross(lambdaLike3Vec); - float ringObservableLeadP = protonLikeStarUnit3Vec.Dot(crossLeadP) / crossLeadP.R(); - // Adding the prefactor related to the CP-violating decay (decay constants have different signs) - if (!forcePolSignQA) - ringObservableLeadP *= (isLambda) ? polPrefactorLambda : polPrefactorAntiLambda; - else - ringObservableLeadP *= (isLambda) ? polPrefactorLambda : -1.0 * polPrefactorAntiLambda; - // Angular variables - float deltaPhiLeadP = wrapToPiFast(v0phi - leadPPhi); // Wrapped to [-PI, pi), for convenience - float deltaThetaLeadP = ROOT::Math::VectorUtil::Angle(leadPUnitVec, lambdaLike3Vec); // 3D angular separation + float ringObservableLeadP = 0.; + float deltaPhiLeadP = 0.; + float deltaThetaLeadP = 0.; + if (hasValidLeadingP) { + // Cross product + XYZVector crossLeadP = leadPUnitVec.Cross(lambdaLike3Vec); + ringObservableLeadP = protonLikeStarUnit3Vec.Dot(crossLeadP) / crossLeadP.R(); + // Adding the prefactor related to the CP-violating decay (decay constants have different signs) + if (!forcePolSignQA) + ringObservableLeadP *= (isLambda) ? polPrefactorLambda : polPrefactorAntiLambda; + else + ringObservableLeadP *= (isLambda) ? polPrefactorLambda : -1.0 * polPrefactorAntiLambda; + // Angular variables + deltaPhiLeadP = wrapToPiFast(v0phi - leadPPhi); // Wrapped to [-PI, pi), for convenience + deltaThetaLeadP = ROOT::Math::VectorUtil::Angle(leadPUnitVec, lambdaLike3Vec); // 3D angular separation + } ////////////////////////////////////////// // Ring observable: Leading jet proxy @@ -726,34 +935,106 @@ struct lambdajetpolarizationionsderived { float v0phiToFillHists = wrapToPiFast(v0phi); // A short wrap to reuse some predefined axes // Fill ring histograms: (1D, lambda 2D correlations and jet 2D correlations): - RING_OBSERVABLE_LEADP_FILL_LIST(APPLY_HISTO_FILL, "Ring") // Notice the usage of macros! If you change the variable names, this WILL break the code! - // No, there should NOT be any ";" here! Read the macro definition for an explanation - histos.fill(HIST("pRingCutsLeadingP"), 0, ringObservableLeadP); // First bin of comparison + if (hasValidLeadingP) { + RING_OBSERVABLE_LEADP_FILL_LIST(APPLY_HISTO_FILL, "Ring") // Notice the usage of macros! If you change the variable names, this WILL break the code! + // No, there should NOT be any ";" here! Read the macro definition for an explanation + histos.fill(HIST("IntegratedCuts/pRingCutsLeadingP"), 0, ringObservableLeadP); // First bin of comparison + histos.fill(HIST("IntegratedCuts/hCountCutsLeadingP"), 0); + } POLARIZATION_PROFILE_FILL_LIST(APPLY_HISTO_FILL, "Ring") + // Binary search using the pre-fetched axes for delta method of error bar estimation: + int binPt = 0; // Dummy declarations + int binMass = 0; + int binDTheta = 0; if (hasValidLeadingJet) { RING_OBSERVABLE_FILL_LIST(APPLY_HISTO_FILL, "Ring") - histos.fill(HIST("pRingCuts"), 0, ringObservable); - histos.fill(HIST("pRingVsNV0s"), NLambdaLikeV0s, ringObservable); + histos.fill(HIST("IntegratedCuts/pRingCuts"), 0, ringObservable); + histos.fill(HIST("IntegratedCuts/hCountCuts"), 0); + histos.fill(HIST("IntegratedCuts/pRingVsNV0s"), NLambdaLikeV0s, ringObservable); + + // Properly fetching values as they are needed: + binPt = mAxisPt->FindBin(v0pt); + binMass = mAxisMass->FindBin(v0LambdaLikeMass); + binDTheta = mAxisDTheta->FindBin(deltaThetaJet); + trackRing.addV0(ringObservable, binPt, binMass, binDTheta); } if (hasValidSubJet) { RING_OBSERVABLE_2NDJET_FILL_LIST(APPLY_HISTO_FILL, "Ring") - histos.fill(HIST("pRingCutsSubLeadingJet"), 0, ringObservable2ndJet); + histos.fill(HIST("IntegratedCuts/pRingCutsSubLeadingJet"), 0, ringObservable2ndJet); + histos.fill(HIST("IntegratedCuts/hCountCutsSubLeadingJet"), 0); + } + + // Filling eta dependence QAs of the result (both for V0 and jet proxy): + const bool lambdaEtaPos = v0eta >= 0.; + if (hasValidLeadingJet) { + histos.fill(HIST("ProxyEta/pRingEtaCuts"), 0, ringObservable); + histos.fill(HIST("ProxyEta/pRingEtaCuts"), lambdaEtaPos ? 3 : 4, ringObservable); + if (jetEtaPos) { // Less readable than "if ( jetEtaPos && lambdaEtaPos)", yet more efficient + histos.fill(HIST("ProxyEta/pRingEtaCuts"), 1, ringObservable); + histos.fill(HIST("ProxyEta/pRingEtaCuts"), lambdaEtaPos ? 5 : 6, ringObservable); + if (jetEtaStrict) { // eta_{Jet} >= R + histos.fill(HIST("ProxyEta/pRingEtaCuts"), 9, ringObservable); + histos.fill(HIST("ProxyEta/pRingEtaCuts"), lambdaEtaPos ? 11 : 12, ringObservable); + } + } else { + histos.fill(HIST("ProxyEta/pRingEtaCuts"), 2, ringObservable); + histos.fill(HIST("ProxyEta/pRingEtaCuts"), lambdaEtaPos ? 7 : 8, ringObservable); + if (jetEtaStrict) { // eta_{Jet} <= -R + histos.fill(HIST("ProxyEta/pRingEtaCuts"), 10, ringObservable); + histos.fill(HIST("ProxyEta/pRingEtaCuts"), lambdaEtaPos ? 13 : 14, ringObservable); + } + } + } + if (hasValidSubJet) { + histos.fill(HIST("ProxyEta/pRingEtaCutsSubLeadingJet"), 0, ringObservable2ndJet); + histos.fill(HIST("ProxyEta/pRingEtaCutsSubLeadingJet"), lambdaEtaPos ? 3 : 4, ringObservable2ndJet); + if (subJetEtaPos) { + histos.fill(HIST("ProxyEta/pRingEtaCutsSubLeadingJet"), 1, ringObservable2ndJet); + histos.fill(HIST("ProxyEta/pRingEtaCutsSubLeadingJet"), lambdaEtaPos ? 5 : 6, ringObservable2ndJet); + if (subJetEtaStrict) { // eta_{SubJet} >= R + histos.fill(HIST("ProxyEta/pRingEtaCutsSubLeadingJet"), 9, ringObservable2ndJet); + histos.fill(HIST("ProxyEta/pRingEtaCutsSubLeadingJet"), lambdaEtaPos ? 11 : 12, ringObservable2ndJet); + } + } else { + histos.fill(HIST("ProxyEta/pRingEtaCutsSubLeadingJet"), 2, ringObservable2ndJet); + histos.fill(HIST("ProxyEta/pRingEtaCutsSubLeadingJet"), lambdaEtaPos ? 7 : 8, ringObservable2ndJet); + if (subJetEtaStrict) { // eta_{SubJet} <= -R + histos.fill(HIST("ProxyEta/pRingEtaCutsSubLeadingJet"), 10, ringObservable2ndJet); + histos.fill(HIST("ProxyEta/pRingEtaCutsSubLeadingJet"), lambdaEtaPos ? 13 : 14, ringObservable2ndJet); + } + } + } + if (hasValidLeadingP) { + histos.fill(HIST("ProxyEta/pRingEtaCutsLeadingP"), 0, ringObservableLeadP); + histos.fill(HIST("ProxyEta/pRingEtaCutsLeadingP"), lambdaEtaPos ? 3 : 4, ringObservableLeadP); + if (leadPEtaPos) + histos.fill(HIST("ProxyEta/pRingEtaCutsLeadingP"), 1, ringObservableLeadP); + histos.fill(HIST("ProxyEta/pRingEtaCutsLeadingP"), lambdaEtaPos ? 5 : 6, ringObservableLeadP); + else + histos.fill(HIST("ProxyEta/pRingEtaCutsLeadingP"), 2, ringObservableLeadP); + histos.fill(HIST("ProxyEta/pRingEtaCutsLeadingP"), lambdaEtaPos ? 7 : 8, ringObservableLeadP); } // Extra kinematic criteria for Lambda candidates (removes polarization background): const bool kinematicLambdaCheck = (v0pt > 0.5 && v0pt < 1.5) && std::abs(lambdaRapidity) < 0.5; if (kinematicLambdaCheck) { - RING_OBSERVABLE_LEADP_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts") - histos.fill(HIST("pRingCutsLeadingP"), 1, ringObservableLeadP); + if (hasValidLeadingP) { + RING_OBSERVABLE_LEADP_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts") + histos.fill(HIST("IntegratedCuts/pRingCutsLeadingP"), 1, ringObservableLeadP); + histos.fill(HIST("IntegratedCuts/hCountCutsLeadingP"), 1); + } POLARIZATION_PROFILE_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts") if (hasValidLeadingJet) { RING_OBSERVABLE_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts") - histos.fill(HIST("pRingCuts"), 1, ringObservable); + histos.fill(HIST("IntegratedCuts/pRingCuts"), 1, ringObservable); + histos.fill(HIST("IntegratedCuts/hCountCuts"), 1); + trackRingKinCuts.addV0(ringObservable, binPt, binMass, binDTheta); } if (hasValidSubJet) { RING_OBSERVABLE_2NDJET_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts") - histos.fill(HIST("pRingCutsSubLeadingJet"), 1, ringObservable2ndJet); + histos.fill(HIST("IntegratedCuts/pRingCutsSubLeadingJet"), 1, ringObservable2ndJet); + histos.fill(HIST("IntegratedCuts/hCountCutsSubLeadingJet"), 1); } } @@ -761,35 +1042,50 @@ struct lambdajetpolarizationionsderived { // (redundant for jets with R=0.4, but for jets with R<0.4 the leading jet may be farther in eta) if (kinematicJetCheck) { // Already includes hasValidLeadingJet in the bool! (no need to check again) RING_OBSERVABLE_FILL_LIST(APPLY_HISTO_FILL, "JetKinematicCuts") - histos.fill(HIST("pRingCuts"), 2, ringObservable); + histos.fill(HIST("IntegratedCuts/pRingCuts"), 2, ringObservable); + histos.fill(HIST("IntegratedCuts/hCountCuts"), 2); POLARIZATION_PROFILE_FILL_LIST(APPLY_HISTO_FILL, "JetKinematicCuts") + trackJetKinCuts.addV0(ringObservable, binPt, binMass, binDTheta); } // Extra selection criteria on both Lambda and jet candidates: if (kinematicLambdaCheck && kinematicJetCheck) { RING_OBSERVABLE_FILL_LIST(APPLY_HISTO_FILL, "JetAndLambdaKinematicCuts") - histos.fill(HIST("pRingCuts"), 3, ringObservable); + histos.fill(HIST("IntegratedCuts/pRingCuts"), 3, ringObservable); + histos.fill(HIST("IntegratedCuts/hCountCuts"), 3); POLARIZATION_PROFILE_FILL_LIST(APPLY_HISTO_FILL, "JetAndLambdaKinematicCuts") + trackJetLambdaKinCuts.addV0(ringObservable, binPt, binMass, binDTheta); } // Same variations for the leading particle and for the subleading jet: + // (kinematicLeadPCheck already encodes hasValidLeadingP, so no extra gate needed here) if (kinematicLeadPCheck) { RING_OBSERVABLE_LEADP_FILL_LIST(APPLY_HISTO_FILL, "JetKinematicCuts") - histos.fill(HIST("pRingCutsLeadingP"), 2, ringObservableLeadP); + histos.fill(HIST("IntegratedCuts/pRingCutsLeadingP"), 2, ringObservableLeadP); + histos.fill(HIST("IntegratedCuts/hCountCutsLeadingP"), 2); } if (kinematic2ndJetCheck) { RING_OBSERVABLE_2NDJET_FILL_LIST(APPLY_HISTO_FILL, "JetKinematicCuts") - histos.fill(HIST("pRingCutsSubLeadingJet"), 2, ringObservable2ndJet); + histos.fill(HIST("IntegratedCuts/pRingCutsSubLeadingJet"), 2, ringObservable2ndJet); + histos.fill(HIST("IntegratedCuts/hCountCutsSubLeadingJet"), 2); } if (kinematicLambdaCheck && kinematicLeadPCheck) { RING_OBSERVABLE_LEADP_FILL_LIST(APPLY_HISTO_FILL, "JetAndLambdaKinematicCuts") - histos.fill(HIST("pRingCutsLeadingP"), 3, ringObservableLeadP); + histos.fill(HIST("IntegratedCuts/pRingCutsLeadingP"), 3, ringObservableLeadP); + histos.fill(HIST("IntegratedCuts/hCountCutsLeadingP"), 3); } if (kinematicLambdaCheck && kinematic2ndJetCheck) { RING_OBSERVABLE_2NDJET_FILL_LIST(APPLY_HISTO_FILL, "JetAndLambdaKinematicCuts") - histos.fill(HIST("pRingCutsSubLeadingJet"), 3, ringObservable2ndJet); + histos.fill(HIST("IntegratedCuts/pRingCutsSubLeadingJet"), 3, ringObservable2ndJet); + histos.fill(HIST("IntegratedCuts/hCountCutsSubLeadingJet"), 3); } } // end v0s loop + + // Flush trackers to the actual O2 histograms (via macros, so that O2 compiles properly): + FLUSH_DELTA_TRACKER("Ring", trackRing, mAxisPt, mAxisMass, mAxisDTheta) + FLUSH_DELTA_TRACKER("RingKinematicCuts", trackRingKinCuts, mAxisPt, mAxisMass, mAxisDTheta) + FLUSH_DELTA_TRACKER("JetKinematicCuts", trackJetKinCuts, mAxisPt, mAxisMass, mAxisDTheta) + FLUSH_DELTA_TRACKER("JetAndLambdaKinematicCuts", trackJetLambdaKinCuts, mAxisPt, mAxisMass, mAxisDTheta) } // end collisions } From cf6429b9b9377050dc5bdbe356603eb098891755 Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Fri, 27 Mar 2026 01:04:11 +0000 Subject: [PATCH 04/31] Adding functions to study the "negative helicity problem": a problem were specific decay topologies are favored due to easier V0 reconstruction (pions boosted in such a way that their momentum is small are harder to reconstruct). This introduces a non-trivial FAKE signal of polarization which must be understood and properly accounted for in the computations! Fixed BoostToCM usage (which, surprisingly, by the definition of the observable, does an effect close to a scaling factor only). Added QA histograms to understand negative signal from positive #eta and negative #eta. --- .../Strangeness/lambdaJetPolarizationIons.cxx | 2 +- .../lambdaJetPolarizationIonsDerived.cxx | 267 +++++++++++++++++- 2 files changed, 254 insertions(+), 15 deletions(-) diff --git a/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx b/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx index c2a09cef0dd..1bfa7fefaa9 100644 --- a/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx +++ b/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx @@ -1771,7 +1771,7 @@ struct lambdajetpolarizationions { // Fill event table: tableCollisions(collision.centFT0M(), collision.centFT0C(), - collision.centFV0A()); // (TODO: add InteractionRate info and other useful cuts for later on in the analysis?) + collision.centFV0A()); // (TODO: add InteractionRate info and other useful cuts for later on in the analysis) // Get the derived collision row index for this event: const int ringCollIdx = tableCollisions.lastIndex(); diff --git a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx index a42330a1f16..1ee1e6c6f90 100644 --- a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx @@ -84,6 +84,7 @@ enum CentEstimator { /* Counters */ \ X(FOLDER "/QA/hDeltaPhi", deltaPhiJet) \ X(FOLDER "/QA/hDeltaTheta", deltaThetaJet) \ + X(FOLDER "/QA/hCosDeltaTheta", cosDeltaThetaJet) \ X(FOLDER "/QA/hIntegrated", 0.) \ /* Lambda pT variation -- Youpeng's proposal */ \ X(FOLDER "/QA/hLambdaPt", v0pt) \ @@ -93,6 +94,8 @@ enum CentEstimator { /* Additional plots for instant gratification - 1D Profiles */ \ X(FOLDER "/pRingObservableDeltaPhi", deltaPhiJet, ringObservable) \ X(FOLDER "/pRingObservableDeltaTheta", deltaThetaJet, ringObservable) \ + X(FOLDER "/pRingObservableEtaLambda", v0eta, ringObservable) \ + X(FOLDER "/pRingObservableEtaJet", leadingJetEta, ringObservable) \ X(FOLDER "/pRingObservableIntegrated", 0., ringObservable) \ X(FOLDER "/pRingObservableLambdaPt", v0pt, ringObservable) \ /* 2D Profiles */ \ @@ -121,6 +124,8 @@ enum CentEstimator { /* 2D Profiles: Angle vs Mass */ \ X(FOLDER "/p2dRingObservableDeltaPhiVsMass", deltaPhiJet, v0LambdaLikeMass, ringObservable) \ X(FOLDER "/p2dRingObservableDeltaThetaVsMass", deltaThetaJet, v0LambdaLikeMass, ringObservable) \ + /* 2D Profile: Ring vs Eta variables */ \ + X(FOLDER "/p2dRingObservableEtaLambdaVsEtaJet", v0eta, leadingJetEta, ringObservable) \ /* 3D Profiles: Angle vs Mass vs Lambda pT */ \ X(FOLDER "/p3dRingObservableDeltaPhiVsMassVsLambdaPt", deltaPhiJet, v0LambdaLikeMass, v0pt, ringObservable) \ X(FOLDER "/p3dRingObservableDeltaThetaVsMassVsLambdaPt", deltaThetaJet, v0LambdaLikeMass, v0pt, ringObservable) \ @@ -137,17 +142,29 @@ enum CentEstimator { // For leading particle #define RING_OBSERVABLE_LEADP_FILL_LIST(X, FOLDER) \ + X(FOLDER "/QA/hDeltaPhiLeadP", deltaPhiLeadP) \ + X(FOLDER "/QA/hDeltaThetaLeadP", deltaThetaLeadP) \ + X(FOLDER "/QA/hCosDeltaThetaLeadP", cosDeltaThetaLeadP) \ X(FOLDER "/pRingObservableLeadPDeltaPhi", deltaPhiLeadP, ringObservableLeadP) \ X(FOLDER "/pRingObservableLeadPDeltaTheta", deltaThetaLeadP, ringObservableLeadP) \ + X(FOLDER "/pRingObservableEtaLambdaLeadP", v0eta, ringObservableLeadP) \ + X(FOLDER "/pRingObservableEtaLeadP", leadPEta, ringObservableLeadP) \ X(FOLDER "/pRingObservableLeadPIntegrated", 0., ringObservableLeadP) \ - X(FOLDER "/pRingObservableLeadPLambdaPt", v0pt, ringObservableLeadP) + X(FOLDER "/pRingObservableLeadPLambdaPt", v0pt, ringObservableLeadP) \ + X(FOLDER "/p2dRingObservableEtaLambdaVsEtaLeadP", v0eta, leadPEta, ringObservableLeadP) // For subleading jet: #define RING_OBSERVABLE_2NDJET_FILL_LIST(X, FOLDER) \ + X(FOLDER "/QA/hDeltaPhi2ndJet", deltaPhi2ndJet) \ + X(FOLDER "/QA/hDeltaTheta2ndJet", deltaTheta2ndJet) \ + X(FOLDER "/QA/hCosDeltaTheta2ndJet", cosDeltaTheta2ndJet) \ X(FOLDER "/pRingObservable2ndJetDeltaPhi", deltaPhi2ndJet, ringObservable2ndJet) \ X(FOLDER "/pRingObservable2ndJetDeltaTheta", deltaTheta2ndJet, ringObservable2ndJet) \ + X(FOLDER "/pRingObservableEtaLambda2ndJet", v0eta, ringObservable2ndJet) \ + X(FOLDER "/pRingObservableEta2ndJet", subleadingJetEta, ringObservable2ndJet) \ X(FOLDER "/pRingObservable2ndJetIntegrated", 0., ringObservable2ndJet) \ - X(FOLDER "/pRingObservable2ndJetLambdaPt", v0pt, ringObservable2ndJet) + X(FOLDER "/pRingObservable2ndJetLambdaPt", v0pt, ringObservable2ndJet) \ + X(FOLDER "/p2dRingObservableEtaLambdaVsEta2ndJet", v0eta, subleadingJetEta, ringObservable2ndJet) #define POLARIZATION_PROFILE_FILL_LIST(X, FOLDER) \ /* =============================== */ \ @@ -219,6 +236,7 @@ struct lambdajetpolarizationionsderived { // Master analysis switches Configurable analyseLambda{"analyseLambda", true, "process Lambda-like candidates"}; Configurable analyseAntiLambda{"analyseAntiLambda", false, "process AntiLambda-like candidates"}; + // Configurable analyseMagField{"analyseMagField", true, "analyse efficiency effects wrt magnetic field"}; // Older DerivedData lacks magField. "if constexpr (requires { collision.magField(); })" would only see the current header definition, so need a flag for retro-comp. Configurable doPPAnalysis{"doPPAnalysis", false, "if in pp, set to true. Default is HI"}; // Centrality: @@ -230,9 +248,10 @@ struct lambdajetpolarizationionsderived { Configurable forcePerpToJet{"forcePerpToJet", false, "force jet direction to be perpendicular to jet estimator. For QA"}; Configurable forceJetDirectionSmudge{"forceJetDirectionSmudge", false, "fluctuate jet direction by 10% of R around original axis. For QA (tests sensibility)"}; // Configurable jetRForSmudging{"jetRForSmudging", 0.4, "QA quantity: the chosen R scale for the jet direction smudge"}; // Superseeded by jetR: kept the same scale in analysis and QA - Configurable jetR{"jetR", 0.4, "Radius of the jet"}; // Provide manually, please. + Configurable jetR{"jetR", 0.4f, "Radius of the jet"}; // Provide manually, please. Configurable minLeadParticlePt{"minLeadParticlePt", 2.0f, "Minimum Pt for a lead track to be considered a valid proxy for a jet"}; Configurable minLeadJetPt{"minLeadJetPt", 10.0f, "Minimum Pt for leading jet to be considered valid (may be more restrictive than TableProducer)"}; + Configurable minSubLeadJetPt{"minSubLeadJetPt", 5.0f, "Minimum Pt for subleading jet to be considered valid (may be more restrictive than TableProducer)"}; ///////////////////////// // Configurable blocks: @@ -240,14 +259,16 @@ struct lambdajetpolarizationionsderived { struct : ConfigurableGroup { std::string prefix = "axisConfigurations"; // JSON group name ConfigurableAxis axisPt{"axisPt", {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 analysis"}; - ConfigurableAxis axisPtCoarseQA{"axisPtCoarse", {VARIABLE_WIDTH, 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 7.0f, 10.0f, 15.0f}, "pt axis for QA"}; + ConfigurableAxis axisPtCoarseQA{"axisPtCoarseQA", {VARIABLE_WIDTH, 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 7.0f, 10.0f, 15.0f}, "pt axis for QA"}; ConfigurableAxis axisLambdaMass{"axisLambdaMass", {450, 1.08f, 1.15f}, "Lambda mass in GeV/c"}; // Default is {200, 1.101f, 1.131f} // Jet axes: ConfigurableAxis axisLeadingParticlePt{"axisLeadingParticlePt", {100, 0.f, 200.f}, "Leading particle p_{T} (GeV/c)"}; // Simpler version! ConfigurableAxis axisJetPt{"axisJetPt", {50, 0.f, 200.f}, "Jet p_{t} (GeV)"}; ConfigurableAxis axisEta{"axisEta", {50, -1.0f, 1.0f}, "#eta"}; + ConfigurableAxis axisEtaCoarse{"axisEtaCoarse", {20, -0.9f, 0.9f}, "#eta coarse axis"}; ConfigurableAxis axisDeltaTheta{"axisDeltaTheta", {40, 0, constants::math::PI}, "#Delta #theta_{jet}"}; + ConfigurableAxis axisCosTheta{"axisCosTheta", {50, -1, 1}, "cos(#theta)"}; ConfigurableAxis axisDeltaPhi{"axisDeltaPhi", {40, -constants::math::PI, constants::math::PI}, "#Delta #phi_{jet}"}; // Coarser axes for signal extraction: @@ -321,7 +342,16 @@ struct lambdajetpolarizationionsderived { // =============================== histos.add((folder + "/QA/hDeltaPhi").c_str(), "hDeltaPhi", kTH1D, {axisConfigurations.axisDeltaPhi}); histos.add((folder + "/QA/hDeltaTheta").c_str(), "hDeltaTheta", kTH1D, {axisConfigurations.axisDeltaTheta}); + histos.add((folder + "/QA/hCosDeltaTheta").c_str(), "hCosDeltaTheta", kTH1D, {axisConfigurations.axisCosTheta}); // Should actually be flat due to the geometry histos.add((folder + "/QA/hIntegrated").c_str(), "hIntegrated", kTH1D, {{1, -0.5, 0.5}}); + + histos.add((folder + "/QA/hDeltaPhiLeadP").c_str(), "hDeltaPhiLeadP", kTH1D, {axisConfigurations.axisDeltaPhi}); + histos.add((folder + "/QA/hDeltaThetaLeadP").c_str(), "hDeltaThetaLeadP", kTH1D, {axisConfigurations.axisDeltaTheta}); + histos.add((folder + "/QA/hCosDeltaThetaLeadP").c_str(), "hCosDeltaThetaLeadP", kTH1D, {axisConfigurations.axisCosTheta}); // Should actually be flat due to the geometry + histos.add((folder + "/QA/hDeltaPhi2ndJet").c_str(), "hDeltaPhi2ndJet", kTH1D, {axisConfigurations.axisDeltaPhi}); + histos.add((folder + "/QA/hDeltaTheta2ndJet").c_str(), "hDeltaTheta2ndJet", kTH1D, {axisConfigurations.axisDeltaTheta}); + histos.add((folder + "/QA/hCosDeltaTheta2ndJet").c_str(), "hCosDeltaTheta2ndJet", kTH1D, {axisConfigurations.axisCosTheta}); // Should actually be flat due to the geometry + // =============================== // Lambda pT dependence // =============================== @@ -364,6 +394,15 @@ struct lambdajetpolarizationionsderived { histos.add((folder + "/pRingObservableDeltaTheta").c_str(), "pRingObservableDeltaTheta;#Delta#theta_{jet};<#it{R}>", kTProfile, {axisConfigurations.axisDeltaTheta}); histos.add((folder + "/pRingObservableIntegrated").c_str(), "pRingObservableIntegrated; ;<#it{R}>", kTProfile, {{1, -0.5, 0.5}}); histos.add((folder + "/pRingObservableLambdaPt").c_str(), "pRingObservableLambdaPt;#it{p}_{T}^{#Lambda};<#it{R}>", kTProfile, {axisConfigurations.axisPt}); + // Understanding eta dependence seen in pRingEtaCuts: + histos.add((folder + "/pRingObservableEtaLambda").c_str(), "pRingObservableEtaLambda;#eta_{#Lambda};<#it{R}>", kTProfile, {axisConfigurations.axisEtaCoarse}); + histos.add((folder + "/pRingObservableEtaJet").c_str(), "pRingObservableEtaJet;#eta_{Jet};<#it{R}>", kTProfile, {axisConfigurations.axisEtaCoarse}); + + histos.add((folder + "/pRingObservableEtaLambda2ndJet").c_str(), "pRingObservableEtaLambda2ndJet;#eta_{#Lambda};<#it{R}>", kTProfile, {axisConfigurations.axisEtaCoarse}); + histos.add((folder + "/pRingObservableEta2ndJet").c_str(), "pRingObservableEta2ndJet;#eta_{2ndJet};<#it{R}>", kTProfile, {axisConfigurations.axisEtaCoarse}); + + histos.add((folder + "/pRingObservableEtaLambdaLeadP").c_str(), "pRingObservableEtaLambdaLeadP;#eta_{#Lambda};<#it{R}>", kTProfile, {axisConfigurations.axisEtaCoarse}); + histos.add((folder + "/pRingObservableEtaLeadP").c_str(), "pRingObservableEtaLeadP;#eta_{LeadP};<#it{R}>", kTProfile, {axisConfigurations.axisEtaCoarse}); // For the leading particle: histos.add((folder + "/pRingObservableLeadPDeltaPhi").c_str(), "pRingObservableLeadPDeltaPhi;#Delta#varphi_{leadP};<#it{R}>", kTProfile, {axisConfigurations.axisDeltaPhi}); histos.add((folder + "/pRingObservableLeadPDeltaTheta").c_str(), "pRingObservableLeadPDeltaTheta;#Delta#theta_{leadP};<#it{R}>", kTProfile, {axisConfigurations.axisDeltaTheta}); @@ -415,6 +454,10 @@ struct lambdajetpolarizationionsderived { histos.add((folder + "/p2dRingObservableDeltaPhiVsMass").c_str(), "p2dRingObservableDeltaPhiVsMass;#Delta#varphi;m_{p#pi};<#it{R}>", kTProfile2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract}); // TProfile2D: vs Mass (DeltaTheta) histos.add((folder + "/p2dRingObservableDeltaThetaVsMass").c_str(), "p2dRingObservableDeltaThetaVsMass;#Delta#theta;m_{p#pi};<#it{R}>", kTProfile2D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract}); + // TProfile2D: vs Eta Lambda vs Eta Jet (Understanding eta dependence seen in pRingEtaCuts) + histos.add((folder + "/p2dRingObservableEtaLambdaVsEtaJet").c_str(), "p2dRingObservableEtaLambdaVsEtaJet;#eta_{#Lambda};#eta_{Jet};<#it{R}>", kTProfile2D, {axisConfigurations.axisEtaCoarse, axisConfigurations.axisEtaCoarse}); + histos.add((folder + "/p2dRingObservableEtaLambdaVsEtaLeadP").c_str(), "p2dRingObservableEtaLambdaVsEtaLeadP;#eta_{#Lambda};#eta_{LeadP};<#it{R}>", kTProfile2D, {axisConfigurations.axisEtaCoarse, axisConfigurations.axisEtaCoarse}); + histos.add((folder + "/p2dRingObservableEtaLambdaVsEta2ndJet").c_str(), "p2dRingObservableEtaLambdaVsEta2ndJet;#eta_{#Lambda};#eta_{2ndJet};<#it{R}>", kTProfile2D, {axisConfigurations.axisEtaCoarse, axisConfigurations.axisEtaCoarse}); // --- TProfile3D: vs DeltaPhi vs Mass vs LambdaPt --- histos.add((folder + "/p3dRingObservableDeltaPhiVsMassVsLambdaPt").c_str(), "p3dRingObservableDeltaPhiVsMassVsLambdaPt;#Delta#varphi;m_{p#pi};p_{T}^{#Lambda};<#it{R}>", kTProfile3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisPtSigExtract}); // --- TProfile3D: vs DeltaTheta vs Mass vs LambdaPt --- @@ -551,6 +594,83 @@ struct lambdajetpolarizationionsderived { histos.get(HIST("ProxyEta/pRingEtaCutsLeadingP"))->GetXaxis()->SetBinLabel(8, "#eta_{LeadP} < 0, #eta_{#Lambda} #geq 0"); histos.get(HIST("ProxyEta/pRingEtaCutsLeadingP"))->GetXaxis()->SetBinLabel(9, "#eta_{LeadP} < 0, #eta_{#Lambda} < 0"); + // Fake polarization signal QA + // --> The "negative helicity problem", where topologies with a proton decaying opposite to the Lambda momentum are enhanced by + // efficiency of reconstruction. The geometries where the proton moves in the same direction as the boost will have a very small + // momentum pion, which is not as easily detected as the opposite case! This may insert a fake signal of polarization in the measurement! + histos.add("HelicityEfficiencyQA/hFakePolCounts", "FakePolCounts; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda};", kTH2D, {axisConfigurations.axisCosTheta, {9, 0, 9}}); + histos.get(HIST("HelicityEfficiencyQA/hFakePolCounts"))->GetZaxis()->SetTitle("N_{V0s}"); + histos.get(HIST("HelicityEfficiencyQA/hFakePolCounts"))->GetYaxis()->SetBinLabel(1, "All #Lambda"); + histos.get(HIST("HelicityEfficiencyQA/hFakePolCounts"))->GetYaxis()->SetBinLabel(2, "#eta_{Jet} #geq 0"); + histos.get(HIST("HelicityEfficiencyQA/hFakePolCounts"))->GetYaxis()->SetBinLabel(3, "#eta_{Jet} < 0"); + histos.get(HIST("HelicityEfficiencyQA/hFakePolCounts"))->GetYaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); + histos.get(HIST("HelicityEfficiencyQA/hFakePolCounts"))->GetYaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); + histos.get(HIST("HelicityEfficiencyQA/hFakePolCounts"))->GetYaxis()->SetBinLabel(6, "#eta_{Jet} #geq 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("HelicityEfficiencyQA/hFakePolCounts"))->GetYaxis()->SetBinLabel(7, "#eta_{Jet} #geq 0, #eta_{#Lambda} < 0"); + histos.get(HIST("HelicityEfficiencyQA/hFakePolCounts"))->GetYaxis()->SetBinLabel(8, "#eta_{Jet} < 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("HelicityEfficiencyQA/hFakePolCounts"))->GetYaxis()->SetBinLabel(9, "#eta_{Jet} < 0, #eta_{#Lambda} < 0"); + + // Doing the same for leading particles: + // (eta_{Jet} may be a bad estimator!) + histos.add("HelicityEfficiencyQA/hFakePolCountsLeadP", "FakePolCounts; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda};", kTH2D, {axisConfigurations.axisCosTheta, {9, 0, 9}}); + histos.get(HIST("HelicityEfficiencyQA/hFakePolCountsLeadP"))->GetZaxis()->SetTitle("N_{V0s}"); + histos.get(HIST("HelicityEfficiencyQA/hFakePolCountsLeadP"))->GetYaxis()->SetBinLabel(1, "All #Lambda"); + histos.get(HIST("HelicityEfficiencyQA/hFakePolCountsLeadP"))->GetYaxis()->SetBinLabel(2, "#eta_{LeadP} #geq 0"); + histos.get(HIST("HelicityEfficiencyQA/hFakePolCountsLeadP"))->GetYaxis()->SetBinLabel(3, "#eta_{LeadP} < 0"); + histos.get(HIST("HelicityEfficiencyQA/hFakePolCountsLeadP"))->GetYaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); + histos.get(HIST("HelicityEfficiencyQA/hFakePolCountsLeadP"))->GetYaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); + histos.get(HIST("HelicityEfficiencyQA/hFakePolCountsLeadP"))->GetYaxis()->SetBinLabel(6, "#eta_{LeadP} #geq 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("HelicityEfficiencyQA/hFakePolCountsLeadP"))->GetYaxis()->SetBinLabel(7, "#eta_{LeadP} #geq 0, #eta_{#Lambda} < 0"); + histos.get(HIST("HelicityEfficiencyQA/hFakePolCountsLeadP"))->GetYaxis()->SetBinLabel(8, "#eta_{LeadP} < 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("HelicityEfficiencyQA/hFakePolCountsLeadP"))->GetYaxis()->SetBinLabel(9, "#eta_{LeadP} < 0, #eta_{#Lambda} < 0"); + + // Avoid fake signal by jets boosting the Lambda in its own direction, then modifying efficiency of reconstruction in a similar way: + histos.add("HelicityEfficiencyQA/hFakePolCountsLambdaPtCut", "FakePol,p_{T}^{#Lambda}#in[0.5,1.5]; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda};", kTH2D, {axisConfigurations.axisCosTheta, {9, 0, 9}}); + histos.get(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtCut"))->GetZaxis()->SetTitle("N_{V0s}"); + histos.get(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtCut"))->GetYaxis()->SetBinLabel(1, "All #Lambda"); + histos.get(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtCut"))->GetYaxis()->SetBinLabel(2, "#eta_{Jet} #geq 0"); + histos.get(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtCut"))->GetYaxis()->SetBinLabel(3, "#eta_{Jet} < 0"); + histos.get(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtCut"))->GetYaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); + histos.get(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtCut"))->GetYaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); + histos.get(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtCut"))->GetYaxis()->SetBinLabel(6, "#eta_{Jet} #geq 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtCut"))->GetYaxis()->SetBinLabel(7, "#eta_{Jet} #geq 0, #eta_{#Lambda} < 0"); + histos.get(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtCut"))->GetYaxis()->SetBinLabel(8, "#eta_{Jet} < 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtCut"))->GetYaxis()->SetBinLabel(9, "#eta_{Jet} < 0, #eta_{#Lambda} < 0"); + + // Even stricter cut (also demands rapidity cut stricter than jets, so may see different boosting): + histos.add("HelicityEfficiencyQA/hFakePolCountsLambdaPtYCuts", "FakePol,p_{T}^{#Lambda}#in[0.5,1.5],|y_{#Lambda}|<0.5; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda};", kTH2D, {axisConfigurations.axisCosTheta, {9, 0, 9}}); + histos.get(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtYCuts"))->GetZaxis()->SetTitle("N_{V0s}"); + histos.get(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtYCuts"))->GetYaxis()->SetBinLabel(1, "All #Lambda"); + histos.get(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtYCuts"))->GetYaxis()->SetBinLabel(2, "#eta_{Jet} #geq 0"); + histos.get(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtYCuts"))->GetYaxis()->SetBinLabel(3, "#eta_{Jet} < 0"); + histos.get(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtYCuts"))->GetYaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); + histos.get(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtYCuts"))->GetYaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); + histos.get(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtYCuts"))->GetYaxis()->SetBinLabel(6, "#eta_{Jet} #geq 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtYCuts"))->GetYaxis()->SetBinLabel(7, "#eta_{Jet} #geq 0, #eta_{#Lambda} < 0"); + histos.get(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtYCuts"))->GetYaxis()->SetBinLabel(8, "#eta_{Jet} < 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtYCuts"))->GetYaxis()->SetBinLabel(9, "#eta_{Jet} < 0, #eta_{#Lambda} < 0"); + + // Another useful quantity -- How much is the fake signal related to the jet's momentum (how much the fake signal is correlated with the Jet-Lambda angular separation): + histos.add("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaJet", "FakePolCounts; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; #Delta#theta_{Jet}", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisDeltaTheta}); + histos.add("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaJetPosEta", "FakePolCounts; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; #Delta#theta_{Jet}", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisDeltaTheta}); + histos.add("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaJetNegEta", "FakePolCounts; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; #Delta#theta_{Jet}", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisDeltaTheta}); + histos.add("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaLeadP", "FakePolCounts; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; #Delta#theta_{LeadP}", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisDeltaTheta}); + histos.add("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaLeadPPosEta", "FakePolCounts; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; #Delta#theta_{LeadP}", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisDeltaTheta}); + histos.add("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaLeadPNegEta", "FakePolCounts; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; #Delta#theta_{LeadP}", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisDeltaTheta}); + + // Understanding the dip at the cos = -1 end: + histos.add("HelicityEfficiencyQA/hFakePolCountsCosThetaVsPtForJets", "FakePolCounts; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; p_{T}^{#Lambda}", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisPtCoarseQA}); + histos.add("HelicityEfficiencyQA/hFakePolCountsCosThetaVsPtForLeadP", "FakePolCounts; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; p_{T}^{#Lambda}", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisPtCoarseQA}); + + // Studying the magnetic field dependence of particle reconstruction efficiency (not magnitude, just sign of field): + // (also for the "negative helicity" problem) + // if (analyseMagField) { + // histos.add("HelicityEfficiencyQA/hLambdaMassDecayGeomRight", "hLambdaMassDecayGeomRight; m_{Inv}; Counts", kTH1D, {axisConfigurations.axisLambdaMassSigExtract}); + // histos.add("HelicityEfficiencyQA/hLambdaMassDecayGeomLeft", "hLambdaMassDecayGeomLeft; m_{Inv}; Counts", kTH1D, {axisConfigurations.axisLambdaMassSigExtract}); + // histos.add("HelicityEfficiencyQA/hAntiLambdaMassDecayGeomRight", "hAntiLambdaMassDecayGeomRight; m_{Inv}; Counts", kTH1D, {axisConfigurations.axisLambdaMassSigExtract}); + // histos.add("HelicityEfficiencyQA/hAntiLambdaMassDecayGeomLeft", "hAntiLambdaMassDecayGeomLeft; m_{Inv}; Counts", kTH1D, {axisConfigurations.axisLambdaMassSigExtract}); + // } + // Integrated observable for events with NLambda+NAntiLambda V0s per event // (an interesting measurement of correlation between and Lambda-like V0s multiplicity. A proxy of covariance) // (calculated for leading jets only) @@ -565,6 +685,14 @@ struct lambdajetpolarizationionsderived { mAxisPt = histos.get(HIST("Ring/DeltaMethod/h2dLambdaPtVsDeltaComp"))->GetXaxis(); mAxisMass = histos.get(HIST("Ring/DeltaMethod/h2dMassVsDeltaComp"))->GetXaxis(); mAxisDTheta = histos.get(HIST("Ring/DeltaMethod/h2dDeltaThetaVsDeltaComp"))->GetXaxis(); + + // // DEBUG! (commit this, then remove from final version) + // histos.add("hPxDebug", "hPxDebug", kTH1D, {{30, -5, 25}}); + // histos.add("hPyDebug", "hPyDebug", kTH1D, {{30, -5, 25}}); + // histos.add("hPzDebug", "hPzDebug", kTH1D, {{30, -5, 25}}); + // histos.add("hPstar", "hPstar", kTH1D, {{20, 0, 0.5}}); + // histos.add("hBetaMag", "hBetaMag", kTH1D, {{30, 0, 3}}); + // histos.add("hMassConsistency", "hMassConsistency", kTH1D, {{100, 0, 1.5}}); } // Helper to get centrality (same from TableProducer, thanks to templating!): @@ -594,6 +722,11 @@ struct lambdajetpolarizationionsderived { for (auto const& collision : collisions) { const auto collId = collision.globalIndex(); // The self-index accessor const double centrality = getCentrality(collision); + + // Fetch magnetic field only if DataModel is in the latest version: + // float magField = 0.f; // Dummy value + // if (analyseMagField) + // magField = collision.magField(); // Slice jets, V0s and leading particle belonging to this collision: // (global collision indices repeat a lot, but they are unique to a same TimeFrame (TF) subfolder in the derived data) @@ -692,7 +825,7 @@ struct lambdajetpolarizationionsderived { // Some useful bools to check if we have a leading jet and a subleading jet: // const bool hasValidLeadingJet = leadingJetPt > 0.; const bool hasValidLeadingJet = leadingJetPt > minLeadJetPt; // Finer control on jet momentum - const bool hasValidSubJet = subleadingJetPt > 0.; + const bool hasValidSubJet = subleadingJetPt > minSubLeadJetPt; // Build jet vectors (only when the corresponding jet exists): // Dummy initialisations are safe: all jet-dependent fills are gated on hasValidLeadingJet / hasValidSubJet. @@ -850,13 +983,45 @@ struct lambdajetpolarizationionsderived { float lambdaRapidity = lambdaLike4Vec.Rapidity(); // For further kinematic selections // Boosting proton into lambda frame: - XYZVector beta = -lambdaLike4Vec.BoostToCM(); // Boost trivector that goes from laboratory frame to the rest frame + XYZVector beta = lambdaLike4Vec.BoostToCM(); // Boost trivector that goes from laboratory frame to Lambda's rest frame (convenient new function, different from TLorentzVector's BoostVector()) auto protonLike4VecStar = ROOT::Math::VectorUtil::boost(protonLike4Vec, beta); + // auto lambdaStar = ROOT::Math::VectorUtil::boost(lambdaLike4Vec, beta); // DEBUG! + // histos.fill(HIST("hPxDebug"), lambdaStar.Px()); + // histos.fill(HIST("hPyDebug"), lambdaStar.Py()); + // histos.fill(HIST("hPzDebug"), lambdaStar.Pz()); + // histos.fill(HIST("hPstar"), protonLike4VecStar.P()); // Should be a constant at ~100 MeV + // histos.fill(HIST("hBetaMag"), beta.R()); + // // More QA: + // float E = lambdaLike4Vec.E(); + // float p2 = lambdaLike4Vec.P2(); + // float m2 = v0LambdaLikeMass * v0LambdaLikeMass; + // histos.fill(HIST("hMassConsistency"), E*E - p2 - m2); // Getting unit vectors and 3-components: XYZVector lambdaLike3Vec = lambdaLike4Vec.Vect(); + auto lambdaLikeUnit3Vec = lambdaLike3Vec.Unit(); XYZVector protonLikeStarUnit3Vec = protonLike4VecStar.Vect().Unit(); + // Calculating fake polarization ("negative helicity problem") estimator: + // (this estimator is calculated outside of any gate, as it does not depend on jet proxy used) + float cosFakePol = protonLikeStarUnit3Vec.Dot(lambdaLikeUnit3Vec); + + // Another reconstruction efficiency measure: + // (Formula is: p_{Lambda} \cross p_{Daughter}^{*} \cdot B, and B points in Z) + // if (analyseMagField) { + // auto crossGeom = lambdaLike3Vec.Cross(protonLikeStarUnit3Vec); + // const bool positiveGeom = crossGeom.Z() * magField > 0; + + // if (isLambda && positiveGeom) + // histos.fill(HIST("HelicityEfficiencyQA/hLambdaMassDecayGeomRight"), v0LambdaLikeMass); + // else if (isLambda && !positiveGeom) + // histos.fill(HIST("HelicityEfficiencyQA/hLambdaMassDecayGeomLeft"), v0LambdaLikeMass); + // else if (isAntiLambda && positiveGeom) + // histos.fill(HIST("HelicityEfficiencyQA/hAntiLambdaMassDecayGeomRight"), v0LambdaLikeMass); + // else + // histos.fill(HIST("HelicityEfficiencyQA/hAntiLambdaMassDecayGeomLeft"), v0LambdaLikeMass); + // } + //////////////////////////////////////////// // Ring observable: Leading particle proxy // Only computed when a valid leading particle exists (pT > minLeadParticlePt) @@ -864,6 +1029,7 @@ struct lambdajetpolarizationionsderived { float ringObservableLeadP = 0.; float deltaPhiLeadP = 0.; float deltaThetaLeadP = 0.; + float cosDeltaThetaLeadP = 0.; if (hasValidLeadingP) { // Cross product XYZVector crossLeadP = leadPUnitVec.Cross(lambdaLike3Vec); @@ -874,8 +1040,10 @@ struct lambdajetpolarizationionsderived { else ringObservableLeadP *= (isLambda) ? polPrefactorLambda : -1.0 * polPrefactorAntiLambda; // Angular variables - deltaPhiLeadP = wrapToPiFast(v0phi - leadPPhi); // Wrapped to [-PI, pi), for convenience - deltaThetaLeadP = ROOT::Math::VectorUtil::Angle(leadPUnitVec, lambdaLike3Vec); // 3D angular separation + deltaPhiLeadP = wrapToPiFast(v0phi - leadPPhi); // Wrapped to [-PI, PI), for convenience + + cosDeltaThetaLeadP = leadPUnitVec.Dot(lambdaLikeUnit3Vec); // Uses the pre-calculated unit vectors to avoid recomputation + deltaThetaLeadP = std::acos(cosDeltaThetaLeadP); // 3D angular separation. Same as ROOT::Math::VectorUtil::Angle(leadPUnitVec, lambdaLike3Vec); } ////////////////////////////////////////// @@ -885,6 +1053,7 @@ struct lambdajetpolarizationionsderived { float ringObservable = 0.; float deltaPhiJet = 0.; float deltaThetaJet = 0.; + float cosDeltaThetaJet = 0.; if (hasValidLeadingJet) { // Cross product XYZVector cross = leadingJetUnitVec.Cross(lambdaLike3Vec); @@ -896,7 +1065,9 @@ struct lambdajetpolarizationionsderived { ringObservable *= (isLambda) ? polPrefactorLambda : -1.0 * polPrefactorAntiLambda; // Angular variables deltaPhiJet = wrapToPiFast(v0phi - leadingJetPhi); - deltaThetaJet = ROOT::Math::VectorUtil::Angle(leadingJetUnitVec, lambdaLike3Vec); + + cosDeltaThetaJet = leadingJetUnitVec.Dot(lambdaLikeUnit3Vec); + deltaThetaJet = std::acos(cosDeltaThetaJet); } ////////////////////////////////////////// @@ -906,6 +1077,7 @@ struct lambdajetpolarizationionsderived { float ringObservable2ndJet = 0.; float deltaPhi2ndJet = 0.; float deltaTheta2ndJet = 0.; + float cosDeltaTheta2ndJet = 0.; if (hasValidSubJet) { XYZVector cross2ndJet = subJetUnitVec.Cross(lambdaLike3Vec); ringObservable2ndJet = protonLikeStarUnit3Vec.Dot(cross2ndJet) / cross2ndJet.R(); @@ -916,7 +1088,8 @@ struct lambdajetpolarizationionsderived { ringObservable2ndJet *= (isLambda) ? polPrefactorLambda : -1.0 * polPrefactorAntiLambda; // Angular variables deltaPhi2ndJet = wrapToPiFast(v0phi - subleadingJetPhi); - deltaTheta2ndJet = ROOT::Math::VectorUtil::Angle(subJetUnitVec, lambdaLike3Vec); + cosDeltaTheta2ndJet = subJetUnitVec.Dot(lambdaLikeUnit3Vec); + deltaTheta2ndJet = std::acos(cosDeltaTheta2ndJet); } // Calculating polarization observables (in the Lambda frame, because that is easier -- does not require boosts): @@ -967,12 +1140,44 @@ struct lambdajetpolarizationionsderived { // Filling eta dependence QAs of the result (both for V0 and jet proxy): const bool lambdaEtaPos = v0eta >= 0.; + const bool pTLambdaCheck = v0pt > 0.5 && v0pt < 1.5; + const bool rapidityLambdaCheck = std::abs(lambdaRapidity) < 0.5; + const bool kinematicLambdaCheck = pTLambdaCheck && rapidityLambdaCheck; if (hasValidLeadingJet) { histos.fill(HIST("ProxyEta/pRingEtaCuts"), 0, ringObservable); histos.fill(HIST("ProxyEta/pRingEtaCuts"), lambdaEtaPos ? 3 : 4, ringObservable); + + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCounts"), cosFakePol, 0); + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCounts"), cosFakePol, lambdaEtaPos ? 3 : 4); + + // Extra correlations test: + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaJet"), cosFakePol, deltaThetaJet); + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsCosThetaVsPtForJets"), cosFakePol, v0pt); + + if (pTLambdaCheck) { + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtCut"), cosFakePol, 0); + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtCut"), cosFakePol, lambdaEtaPos ? 3 : 4); + if (rapidityLambdaCheck){ // Stricter check + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtYCuts"), cosFakePol, 0); + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtYCuts"), cosFakePol, lambdaEtaPos ? 3 : 4); + } + } if (jetEtaPos) { // Less readable than "if ( jetEtaPos && lambdaEtaPos)", yet more efficient histos.fill(HIST("ProxyEta/pRingEtaCuts"), 1, ringObservable); histos.fill(HIST("ProxyEta/pRingEtaCuts"), lambdaEtaPos ? 5 : 6, ringObservable); + + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCounts"), cosFakePol, 1); + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCounts"), cosFakePol, lambdaEtaPos ? 5 : 6); + + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaJetPosEta"), cosFakePol, deltaThetaJet); + if (pTLambdaCheck) { + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtCut"), cosFakePol, 1); + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtCut"), cosFakePol, lambdaEtaPos ? 5 : 6); + if (rapidityLambdaCheck){ // Stricter check + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtYCuts"), cosFakePol, 1); + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtYCuts"), cosFakePol, lambdaEtaPos ? 5 : 6); + } + } if (jetEtaStrict) { // eta_{Jet} >= R histos.fill(HIST("ProxyEta/pRingEtaCuts"), 9, ringObservable); histos.fill(HIST("ProxyEta/pRingEtaCuts"), lambdaEtaPos ? 11 : 12, ringObservable); @@ -980,6 +1185,20 @@ struct lambdajetpolarizationionsderived { } else { histos.fill(HIST("ProxyEta/pRingEtaCuts"), 2, ringObservable); histos.fill(HIST("ProxyEta/pRingEtaCuts"), lambdaEtaPos ? 7 : 8, ringObservable); + + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCounts"), cosFakePol, 2); + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCounts"), cosFakePol, lambdaEtaPos ? 7 : 8); + + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaJetNegEta"), cosFakePol, deltaThetaJet); + + if (pTLambdaCheck) { + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtCut"), cosFakePol, 2); + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtCut"), cosFakePol, lambdaEtaPos ? 7 : 8); + if (rapidityLambdaCheck){ // Stricter check + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtYCuts"), cosFakePol, 2); + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtYCuts"), cosFakePol, lambdaEtaPos ? 7 : 8); + } + } if (jetEtaStrict) { // eta_{Jet} <= -R histos.fill(HIST("ProxyEta/pRingEtaCuts"), 10, ringObservable); histos.fill(HIST("ProxyEta/pRingEtaCuts"), lambdaEtaPos ? 13 : 14, ringObservable); @@ -1008,16 +1227,36 @@ struct lambdajetpolarizationionsderived { if (hasValidLeadingP) { histos.fill(HIST("ProxyEta/pRingEtaCutsLeadingP"), 0, ringObservableLeadP); histos.fill(HIST("ProxyEta/pRingEtaCutsLeadingP"), lambdaEtaPos ? 3 : 4, ringObservableLeadP); - if (leadPEtaPos) + + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsLeadP"), cosFakePol, 0); + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsLeadP"), cosFakePol, lambdaEtaPos ? 3 : 4); + + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsCosThetaVsPtForLeadP"), cosFakePol, v0pt); // Understanding the population of events that has a leading particle (even though this does not need one to be calculated!) + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaLeadP"), cosFakePol, deltaThetaLeadP); + + // Extra correlations test: + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsLeadP"), cosFakePol, deltaThetaLeadP); + if (leadPEtaPos) { histos.fill(HIST("ProxyEta/pRingEtaCutsLeadingP"), 1, ringObservableLeadP); histos.fill(HIST("ProxyEta/pRingEtaCutsLeadingP"), lambdaEtaPos ? 5 : 6, ringObservableLeadP); - else - histos.fill(HIST("ProxyEta/pRingEtaCutsLeadingP"), 2, ringObservableLeadP); + + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsLeadP"), cosFakePol, 1); + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsLeadP"), cosFakePol, lambdaEtaPos ? 5 : 6); + + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaLeadPPosEta"), cosFakePol, deltaThetaLeadP); + } + else { + histos.fill(HIST("ProxyEta/pRingEtaCutsLeadingP"), 2, ringObservableLeadP); histos.fill(HIST("ProxyEta/pRingEtaCutsLeadingP"), lambdaEtaPos ? 7 : 8, ringObservableLeadP); + + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsLeadP"), cosFakePol, 2); + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsLeadP"), cosFakePol, lambdaEtaPos ? 7 : 8); + + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaLeadPNegEta"), cosFakePol, deltaThetaLeadP); + } } // Extra kinematic criteria for Lambda candidates (removes polarization background): - const bool kinematicLambdaCheck = (v0pt > 0.5 && v0pt < 1.5) && std::abs(lambdaRapidity) < 0.5; if (kinematicLambdaCheck) { if (hasValidLeadingP) { RING_OBSERVABLE_LEADP_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts") From 7479180a2501a009aa587febb29b5f72523ae1c4 Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Sat, 28 Mar 2026 00:28:53 +0000 Subject: [PATCH 05/31] Introducing magnetic-field-related tests for diagnosing the helicity efficiency problem. Modifies (minimally) DataModel and TableProducer accordingly. --- PWGLF/DataModel/lambdaJetPolarizationIons.h | 4 +- .../Strangeness/lambdaJetPolarizationIons.cxx | 3 +- .../lambdaJetPolarizationIonsDerived.cxx | 65 +++++++------------ 3 files changed, 28 insertions(+), 44 deletions(-) diff --git a/PWGLF/DataModel/lambdaJetPolarizationIons.h b/PWGLF/DataModel/lambdaJetPolarizationIons.h index faa06e78b22..db2b9b0846e 100644 --- a/PWGLF/DataModel/lambdaJetPolarizationIons.h +++ b/PWGLF/DataModel/lambdaJetPolarizationIons.h @@ -33,6 +33,7 @@ namespace lambdajetpol DECLARE_SOA_COLUMN(CentFT0M, centFT0M, float); DECLARE_SOA_COLUMN(CentFT0C, centFT0C, float); DECLARE_SOA_COLUMN(CentFV0A, centFV0A, float); +DECLARE_SOA_COLUMN(MagField, magField, float); // Jet (and jet proxies) information: DECLARE_SOA_COLUMN(JetPt, jetPt, float); @@ -92,7 +93,8 @@ DECLARE_SOA_TABLE(RingCollisions, "AOD", "RINGCOLLISION", o2::soa::Index<>, // self-index: auto-assigned row number lambdajetpol::CentFT0M, lambdajetpol::CentFT0C, - lambdajetpol::CentFV0A); + lambdajetpol::CentFV0A, + lambdajetpol::MagField); namespace lambdajetpol { diff --git a/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx b/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx index 1bfa7fefaa9..4f1e9b38243 100644 --- a/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx +++ b/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx @@ -1771,7 +1771,8 @@ struct lambdajetpolarizationions { // Fill event table: tableCollisions(collision.centFT0M(), collision.centFT0C(), - collision.centFV0A()); // (TODO: add InteractionRate info and other useful cuts for later on in the analysis) + collision.centFV0A(), + magField); // (TODO: add InteractionRate info and other useful cuts for later on in the analysis) // Get the derived collision row index for this event: const int ringCollIdx = tableCollisions.lastIndex(); diff --git a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx index 1ee1e6c6f90..fbdec2bd4e4 100644 --- a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx @@ -236,7 +236,7 @@ struct lambdajetpolarizationionsderived { // Master analysis switches Configurable analyseLambda{"analyseLambda", true, "process Lambda-like candidates"}; Configurable analyseAntiLambda{"analyseAntiLambda", false, "process AntiLambda-like candidates"}; - // Configurable analyseMagField{"analyseMagField", true, "analyse efficiency effects wrt magnetic field"}; // Older DerivedData lacks magField. "if constexpr (requires { collision.magField(); })" would only see the current header definition, so need a flag for retro-comp. + Configurable analyseMagField{"analyseMagField", true, "analyse efficiency effects wrt magnetic field"}; // Older DerivedData lacks magField. "if constexpr (requires { collision.magField(); })" would only see the current header definition, so need a flag for retro-comp. Configurable doPPAnalysis{"doPPAnalysis", false, "if in pp, set to true. Default is HI"}; // Centrality: @@ -664,12 +664,12 @@ struct lambdajetpolarizationionsderived { // Studying the magnetic field dependence of particle reconstruction efficiency (not magnitude, just sign of field): // (also for the "negative helicity" problem) - // if (analyseMagField) { - // histos.add("HelicityEfficiencyQA/hLambdaMassDecayGeomRight", "hLambdaMassDecayGeomRight; m_{Inv}; Counts", kTH1D, {axisConfigurations.axisLambdaMassSigExtract}); - // histos.add("HelicityEfficiencyQA/hLambdaMassDecayGeomLeft", "hLambdaMassDecayGeomLeft; m_{Inv}; Counts", kTH1D, {axisConfigurations.axisLambdaMassSigExtract}); - // histos.add("HelicityEfficiencyQA/hAntiLambdaMassDecayGeomRight", "hAntiLambdaMassDecayGeomRight; m_{Inv}; Counts", kTH1D, {axisConfigurations.axisLambdaMassSigExtract}); - // histos.add("HelicityEfficiencyQA/hAntiLambdaMassDecayGeomLeft", "hAntiLambdaMassDecayGeomLeft; m_{Inv}; Counts", kTH1D, {axisConfigurations.axisLambdaMassSigExtract}); - // } + if (analyseMagField) { + histos.add("HelicityEfficiencyQA/hLambdaMassDecayGeomRight", "hLambdaMassDecayGeomRight; m_{Inv}; Counts", kTH1D, {axisConfigurations.axisLambdaMassSigExtract}); + histos.add("HelicityEfficiencyQA/hLambdaMassDecayGeomLeft", "hLambdaMassDecayGeomLeft; m_{Inv}; Counts", kTH1D, {axisConfigurations.axisLambdaMassSigExtract}); + histos.add("HelicityEfficiencyQA/hAntiLambdaMassDecayGeomRight", "hAntiLambdaMassDecayGeomRight; m_{Inv}; Counts", kTH1D, {axisConfigurations.axisLambdaMassSigExtract}); + histos.add("HelicityEfficiencyQA/hAntiLambdaMassDecayGeomLeft", "hAntiLambdaMassDecayGeomLeft; m_{Inv}; Counts", kTH1D, {axisConfigurations.axisLambdaMassSigExtract}); + } // Integrated observable for events with NLambda+NAntiLambda V0s per event // (an interesting measurement of correlation between and Lambda-like V0s multiplicity. A proxy of covariance) @@ -685,14 +685,6 @@ struct lambdajetpolarizationionsderived { mAxisPt = histos.get(HIST("Ring/DeltaMethod/h2dLambdaPtVsDeltaComp"))->GetXaxis(); mAxisMass = histos.get(HIST("Ring/DeltaMethod/h2dMassVsDeltaComp"))->GetXaxis(); mAxisDTheta = histos.get(HIST("Ring/DeltaMethod/h2dDeltaThetaVsDeltaComp"))->GetXaxis(); - - // // DEBUG! (commit this, then remove from final version) - // histos.add("hPxDebug", "hPxDebug", kTH1D, {{30, -5, 25}}); - // histos.add("hPyDebug", "hPyDebug", kTH1D, {{30, -5, 25}}); - // histos.add("hPzDebug", "hPzDebug", kTH1D, {{30, -5, 25}}); - // histos.add("hPstar", "hPstar", kTH1D, {{20, 0, 0.5}}); - // histos.add("hBetaMag", "hBetaMag", kTH1D, {{30, 0, 3}}); - // histos.add("hMassConsistency", "hMassConsistency", kTH1D, {{100, 0, 1.5}}); } // Helper to get centrality (same from TableProducer, thanks to templating!): @@ -724,9 +716,9 @@ struct lambdajetpolarizationionsderived { const double centrality = getCentrality(collision); // Fetch magnetic field only if DataModel is in the latest version: - // float magField = 0.f; // Dummy value - // if (analyseMagField) - // magField = collision.magField(); + float magField = 1.f; // Dummy value + if (analyseMagField) + magField = collision.magField(); // Slice jets, V0s and leading particle belonging to this collision: // (global collision indices repeat a lot, but they are unique to a same TimeFrame (TF) subfolder in the derived data) @@ -985,17 +977,6 @@ struct lambdajetpolarizationionsderived { // Boosting proton into lambda frame: XYZVector beta = lambdaLike4Vec.BoostToCM(); // Boost trivector that goes from laboratory frame to Lambda's rest frame (convenient new function, different from TLorentzVector's BoostVector()) auto protonLike4VecStar = ROOT::Math::VectorUtil::boost(protonLike4Vec, beta); - // auto lambdaStar = ROOT::Math::VectorUtil::boost(lambdaLike4Vec, beta); // DEBUG! - // histos.fill(HIST("hPxDebug"), lambdaStar.Px()); - // histos.fill(HIST("hPyDebug"), lambdaStar.Py()); - // histos.fill(HIST("hPzDebug"), lambdaStar.Pz()); - // histos.fill(HIST("hPstar"), protonLike4VecStar.P()); // Should be a constant at ~100 MeV - // histos.fill(HIST("hBetaMag"), beta.R()); - // // More QA: - // float E = lambdaLike4Vec.E(); - // float p2 = lambdaLike4Vec.P2(); - // float m2 = v0LambdaLikeMass * v0LambdaLikeMass; - // histos.fill(HIST("hMassConsistency"), E*E - p2 - m2); // Getting unit vectors and 3-components: XYZVector lambdaLike3Vec = lambdaLike4Vec.Vect(); @@ -1008,19 +989,19 @@ struct lambdajetpolarizationionsderived { // Another reconstruction efficiency measure: // (Formula is: p_{Lambda} \cross p_{Daughter}^{*} \cdot B, and B points in Z) - // if (analyseMagField) { - // auto crossGeom = lambdaLike3Vec.Cross(protonLikeStarUnit3Vec); - // const bool positiveGeom = crossGeom.Z() * magField > 0; - - // if (isLambda && positiveGeom) - // histos.fill(HIST("HelicityEfficiencyQA/hLambdaMassDecayGeomRight"), v0LambdaLikeMass); - // else if (isLambda && !positiveGeom) - // histos.fill(HIST("HelicityEfficiencyQA/hLambdaMassDecayGeomLeft"), v0LambdaLikeMass); - // else if (isAntiLambda && positiveGeom) - // histos.fill(HIST("HelicityEfficiencyQA/hAntiLambdaMassDecayGeomRight"), v0LambdaLikeMass); - // else - // histos.fill(HIST("HelicityEfficiencyQA/hAntiLambdaMassDecayGeomLeft"), v0LambdaLikeMass); - // } + if (analyseMagField) { + auto crossGeom = lambdaLike3Vec.Cross(protonLikeStarUnit3Vec); + const bool positiveGeom = crossGeom.Z() * magField > 0; + + if (isLambda && positiveGeom) + histos.fill(HIST("HelicityEfficiencyQA/hLambdaMassDecayGeomRight"), v0LambdaLikeMass); + else if (isLambda && !positiveGeom) + histos.fill(HIST("HelicityEfficiencyQA/hLambdaMassDecayGeomLeft"), v0LambdaLikeMass); + else if (isAntiLambda && positiveGeom) + histos.fill(HIST("HelicityEfficiencyQA/hAntiLambdaMassDecayGeomRight"), v0LambdaLikeMass); + else + histos.fill(HIST("HelicityEfficiencyQA/hAntiLambdaMassDecayGeomLeft"), v0LambdaLikeMass); + } //////////////////////////////////////////// // Ring observable: Leading particle proxy From 6408d0eb3ae2f1749924674647458a851283e705 Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Mon, 6 Apr 2026 19:13:11 +0000 Subject: [PATCH 06/31] Removing magnetic field fetching. Now assuming magnetic field always points in the same direction (for a simple QA that only uses direction). Additional QAs for the helicity efficiency effect --- PWGLF/DataModel/lambdaJetPolarizationIons.h | 5 +- .../Strangeness/lambdaJetPolarizationIons.cxx | 3 +- .../lambdaJetPolarizationIonsDerived.cxx | 76 ++++++++++++++----- 3 files changed, 61 insertions(+), 23 deletions(-) diff --git a/PWGLF/DataModel/lambdaJetPolarizationIons.h b/PWGLF/DataModel/lambdaJetPolarizationIons.h index db2b9b0846e..fd71f73f6b3 100644 --- a/PWGLF/DataModel/lambdaJetPolarizationIons.h +++ b/PWGLF/DataModel/lambdaJetPolarizationIons.h @@ -33,7 +33,7 @@ namespace lambdajetpol DECLARE_SOA_COLUMN(CentFT0M, centFT0M, float); DECLARE_SOA_COLUMN(CentFT0C, centFT0C, float); DECLARE_SOA_COLUMN(CentFV0A, centFV0A, float); -DECLARE_SOA_COLUMN(MagField, magField, float); +// DECLARE_SOA_COLUMN(MagField, magField, float); // Jet (and jet proxies) information: DECLARE_SOA_COLUMN(JetPt, jetPt, float); @@ -93,8 +93,7 @@ DECLARE_SOA_TABLE(RingCollisions, "AOD", "RINGCOLLISION", o2::soa::Index<>, // self-index: auto-assigned row number lambdajetpol::CentFT0M, lambdajetpol::CentFT0C, - lambdajetpol::CentFV0A, - lambdajetpol::MagField); + lambdajetpol::CentFV0A); namespace lambdajetpol { diff --git a/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx b/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx index 4f1e9b38243..1bfa7fefaa9 100644 --- a/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx +++ b/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx @@ -1771,8 +1771,7 @@ struct lambdajetpolarizationions { // Fill event table: tableCollisions(collision.centFT0M(), collision.centFT0C(), - collision.centFV0A(), - magField); // (TODO: add InteractionRate info and other useful cuts for later on in the analysis) + collision.centFV0A()); // (TODO: add InteractionRate info and other useful cuts for later on in the analysis) // Get the derived collision row index for this event: const int ringCollIdx = tableCollisions.lastIndex(); diff --git a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx index fbdec2bd4e4..39f5b5fa2e5 100644 --- a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx @@ -86,6 +86,7 @@ enum CentEstimator { X(FOLDER "/QA/hDeltaTheta", deltaThetaJet) \ X(FOLDER "/QA/hCosDeltaTheta", cosDeltaThetaJet) \ X(FOLDER "/QA/hIntegrated", 0.) \ + X(FOLDER "/QA/hPtJet", leadingJetPt) \ /* Lambda pT variation -- Youpeng's proposal */ \ X(FOLDER "/QA/hLambdaPt", v0pt) \ /* Counters */ \ @@ -98,6 +99,9 @@ enum CentEstimator { X(FOLDER "/pRingObservableEtaJet", leadingJetEta, ringObservable) \ X(FOLDER "/pRingObservableIntegrated", 0., ringObservable) \ X(FOLDER "/pRingObservableLambdaPt", v0pt, ringObservable) \ + X(FOLDER "/pRingIntVsPtJet", leadingJetPt, ringObservable) \ + X(FOLDER "/pRingIntVsPtJetVsEtaJet", leadingJetPt, leadingJetEta, ringObservable) \ + X(FOLDER "/pRingIntVsPtJetVsEtaV0", leadingJetPt, v0eta, ringObservable) \ /* 2D Profiles */ \ X(FOLDER "/p2dRingObservableDeltaPhiVsLambdaPt", deltaPhiJet, v0pt, ringObservable) \ X(FOLDER "/p2dRingObservableDeltaThetaVsLambdaPt", deltaThetaJet, v0pt, ringObservable) \ @@ -144,6 +148,7 @@ enum CentEstimator { #define RING_OBSERVABLE_LEADP_FILL_LIST(X, FOLDER) \ X(FOLDER "/QA/hDeltaPhiLeadP", deltaPhiLeadP) \ X(FOLDER "/QA/hDeltaThetaLeadP", deltaThetaLeadP) \ + X(FOLDER "/QA/hPtLeadP", leadPPt) \ X(FOLDER "/QA/hCosDeltaThetaLeadP", cosDeltaThetaLeadP) \ X(FOLDER "/pRingObservableLeadPDeltaPhi", deltaPhiLeadP, ringObservableLeadP) \ X(FOLDER "/pRingObservableLeadPDeltaTheta", deltaThetaLeadP, ringObservableLeadP) \ @@ -151,19 +156,26 @@ enum CentEstimator { X(FOLDER "/pRingObservableEtaLeadP", leadPEta, ringObservableLeadP) \ X(FOLDER "/pRingObservableLeadPIntegrated", 0., ringObservableLeadP) \ X(FOLDER "/pRingObservableLeadPLambdaPt", v0pt, ringObservableLeadP) \ + X(FOLDER "/pRingIntVsPtLeadP", leadPPt, ringObservableLeadP) \ + X(FOLDER "/pRingIntVsPtLeadPVsEtaLeadP", leadPPt, leadPEta, ringObservableLeadP) \ + X(FOLDER "/pRingIntVsPtLeadPVsEtaV0", leadPPt, v0eta, ringObservableLeadP) \ X(FOLDER "/p2dRingObservableEtaLambdaVsEtaLeadP", v0eta, leadPEta, ringObservableLeadP) // For subleading jet: -#define RING_OBSERVABLE_2NDJET_FILL_LIST(X, FOLDER) \ - X(FOLDER "/QA/hDeltaPhi2ndJet", deltaPhi2ndJet) \ - X(FOLDER "/QA/hDeltaTheta2ndJet", deltaTheta2ndJet) \ - X(FOLDER "/QA/hCosDeltaTheta2ndJet", cosDeltaTheta2ndJet) \ - X(FOLDER "/pRingObservable2ndJetDeltaPhi", deltaPhi2ndJet, ringObservable2ndJet) \ - X(FOLDER "/pRingObservable2ndJetDeltaTheta", deltaTheta2ndJet, ringObservable2ndJet) \ - X(FOLDER "/pRingObservableEtaLambda2ndJet", v0eta, ringObservable2ndJet) \ - X(FOLDER "/pRingObservableEta2ndJet", subleadingJetEta, ringObservable2ndJet) \ - X(FOLDER "/pRingObservable2ndJetIntegrated", 0., ringObservable2ndJet) \ - X(FOLDER "/pRingObservable2ndJetLambdaPt", v0pt, ringObservable2ndJet) \ +#define RING_OBSERVABLE_2NDJET_FILL_LIST(X, FOLDER) \ + X(FOLDER "/QA/hDeltaPhi2ndJet", deltaPhi2ndJet) \ + X(FOLDER "/QA/hDeltaTheta2ndJet", deltaTheta2ndJet) \ + X(FOLDER "/QA/hCosDeltaTheta2ndJet", cosDeltaTheta2ndJet) \ + X(FOLDER "/QA/hPt2ndJet", subleadingJetPt) \ + X(FOLDER "/pRingObservable2ndJetDeltaPhi", deltaPhi2ndJet, ringObservable2ndJet) \ + X(FOLDER "/pRingObservable2ndJetDeltaTheta", deltaTheta2ndJet, ringObservable2ndJet) \ + X(FOLDER "/pRingObservableEtaLambda2ndJet", v0eta, ringObservable2ndJet) \ + X(FOLDER "/pRingObservableEta2ndJet", subleadingJetEta, ringObservable2ndJet) \ + X(FOLDER "/pRingObservable2ndJetIntegrated", 0., ringObservable2ndJet) \ + X(FOLDER "/pRingObservable2ndJetLambdaPt", v0pt, ringObservable2ndJet) \ + X(FOLDER "/pRingIntVsPt2ndJet", subleadingJetPt, ringObservable2ndJet) \ + X(FOLDER "/pRingIntVsPt2ndJetVsEta2ndJet", subleadingJetPt, subleadingJetEta, ringObservable2ndJet) \ + X(FOLDER "/pRingIntVsPt2ndJetVsEtaV0", subleadingJetPt, v0eta, ringObservable2ndJet) \ X(FOLDER "/p2dRingObservableEtaLambdaVsEta2ndJet", v0eta, subleadingJetEta, ringObservable2ndJet) #define POLARIZATION_PROFILE_FILL_LIST(X, FOLDER) \ @@ -249,7 +261,7 @@ struct lambdajetpolarizationionsderived { Configurable forceJetDirectionSmudge{"forceJetDirectionSmudge", false, "fluctuate jet direction by 10% of R around original axis. For QA (tests sensibility)"}; // Configurable jetRForSmudging{"jetRForSmudging", 0.4, "QA quantity: the chosen R scale for the jet direction smudge"}; // Superseeded by jetR: kept the same scale in analysis and QA Configurable jetR{"jetR", 0.4f, "Radius of the jet"}; // Provide manually, please. - Configurable minLeadParticlePt{"minLeadParticlePt", 2.0f, "Minimum Pt for a lead track to be considered a valid proxy for a jet"}; + Configurable minLeadParticlePt{"minLeadParticlePt", 2.0f, "Minimum Pt for a lead track to be considered a valid proxy for a jet (may be more restrictive than TableProducer)"}; Configurable minLeadJetPt{"minLeadJetPt", 10.0f, "Minimum Pt for leading jet to be considered valid (may be more restrictive than TableProducer)"}; Configurable minSubLeadJetPt{"minSubLeadJetPt", 5.0f, "Minimum Pt for subleading jet to be considered valid (may be more restrictive than TableProducer)"}; @@ -394,6 +406,25 @@ struct lambdajetpolarizationionsderived { histos.add((folder + "/pRingObservableDeltaTheta").c_str(), "pRingObservableDeltaTheta;#Delta#theta_{jet};<#it{R}>", kTProfile, {axisConfigurations.axisDeltaTheta}); histos.add((folder + "/pRingObservableIntegrated").c_str(), "pRingObservableIntegrated; ;<#it{R}>", kTProfile, {{1, -0.5, 0.5}}); histos.add((folder + "/pRingObservableLambdaPt").c_str(), "pRingObservableLambdaPt;#it{p}_{T}^{#Lambda};<#it{R}>", kTProfile, {axisConfigurations.axisPt}); + + // Ring vs Jet proxy pT: + histos.add((folder + "/pRingIntVsPtJet").c_str(), "pRingIntVsPtJet; p_{T}^{Jet} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisJetPt}); + histos.add((folder + "/pRingIntVsPtLeadP").c_str(), "pRingIntVsPtLeadP; p_{T}^{LeadP} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisJetPt}); + histos.add((folder + "/pRingIntVsPt2ndJet").c_str(), "pRingIntVsPt2ndJet; p_{T}^{SubJet} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisJetPt}); + // And some counters to be aware of the amount of Lambdas (and jets) in each pT interval: + histos.add((folder + "/QA/hPtJet").c_str(), "hPtJet", kTH1D, {axisConfigurations.axisJetPt}); + histos.add((folder + "/QA/hPtLeadP").c_str(), "hPtLeadP", kTH1D, {axisConfigurations.axisJetPt}); + histos.add((folder + "/QA/hPt2ndJet").c_str(), "hPt2ndJet", kTH1D, {axisConfigurations.axisJetPt}); + + // Splitting into positive and negative eta contributions: + histos.add((folder + "/pRingIntVsPtJetVsEtaJet").c_str(), "pRingIntVsPtJetVsEtaJet; p_{T}^{Jet} (GeV/c);#eta_{Jet};<#it{R}>", kTProfile2D, {axisConfigurations.axisJetPt, {2, -0.9, 0.9}}); + histos.add((folder + "/pRingIntVsPtLeadPVsEtaLeadP").c_str(), "pRingIntVsPtLeadPVsEtaLeadP; p_{T}^{LeadP} (GeV/c);#eta_{LeadP};<#it{R}>", kTProfile2D, {axisConfigurations.axisJetPt, {2, -0.9, 0.9}}); + histos.add((folder + "/pRingIntVsPt2ndJetVsEta2ndJet").c_str(), "pRingIntVsPt2ndJetVsEta2ndJet; p_{T}^{SubJet} (GeV/c);#eta_{SubJet};<#it{R}>", kTProfile2D, {axisConfigurations.axisJetPt, {2, -0.9, 0.9}}); + // For each Lambda's eta: + histos.add((folder + "/pRingIntVsPtJetVsEtaV0").c_str(), "pRingIntVsPtJetVsEtaV0; p_{T}^{Jet} (GeV/c);#eta_{V0};<#it{R}>", kTProfile2D, {axisConfigurations.axisJetPt, {2, -0.9, 0.9}}); + histos.add((folder + "/pRingIntVsPtLeadPVsEtaV0").c_str(), "pRingIntVsPtLeadPVsEtaV0; p_{T}^{LeadP} (GeV/c);#eta_{V0};<#it{R}>", kTProfile2D, {axisConfigurations.axisJetPt, {2, -0.9, 0.9}}); + histos.add((folder + "/pRingIntVsPt2ndJetVsEtaV0").c_str(), "pRingIntVsPt2ndJetVsEtaV0; p_{T}^{SubJet} (GeV/c);#eta_{V0};<#it{R}>", kTProfile2D, {axisConfigurations.axisJetPt, {2, -0.9, 0.9}}); + // Understanding eta dependence seen in pRingEtaCuts: histos.add((folder + "/pRingObservableEtaLambda").c_str(), "pRingObservableEtaLambda;#eta_{#Lambda};<#it{R}>", kTProfile, {axisConfigurations.axisEtaCoarse}); histos.add((folder + "/pRingObservableEtaJet").c_str(), "pRingObservableEtaJet;#eta_{Jet};<#it{R}>", kTProfile, {axisConfigurations.axisEtaCoarse}); @@ -665,10 +696,10 @@ struct lambdajetpolarizationionsderived { // Studying the magnetic field dependence of particle reconstruction efficiency (not magnitude, just sign of field): // (also for the "negative helicity" problem) if (analyseMagField) { - histos.add("HelicityEfficiencyQA/hLambdaMassDecayGeomRight", "hLambdaMassDecayGeomRight; m_{Inv}; Counts", kTH1D, {axisConfigurations.axisLambdaMassSigExtract}); - histos.add("HelicityEfficiencyQA/hLambdaMassDecayGeomLeft", "hLambdaMassDecayGeomLeft; m_{Inv}; Counts", kTH1D, {axisConfigurations.axisLambdaMassSigExtract}); - histos.add("HelicityEfficiencyQA/hAntiLambdaMassDecayGeomRight", "hAntiLambdaMassDecayGeomRight; m_{Inv}; Counts", kTH1D, {axisConfigurations.axisLambdaMassSigExtract}); - histos.add("HelicityEfficiencyQA/hAntiLambdaMassDecayGeomLeft", "hAntiLambdaMassDecayGeomLeft; m_{Inv}; Counts", kTH1D, {axisConfigurations.axisLambdaMassSigExtract}); + histos.add("HelicityEfficiencyQA/hLambdaMassDecayGeomRight", "hLambdaMassDecayGeomRight; m_{Inv}; Counts", kTH1D, {axisConfigurations.axisLambdaMass}); + histos.add("HelicityEfficiencyQA/hLambdaMassDecayGeomLeft", "hLambdaMassDecayGeomLeft; m_{Inv}; Counts", kTH1D, {axisConfigurations.axisLambdaMass}); + histos.add("HelicityEfficiencyQA/hAntiLambdaMassDecayGeomRight", "hAntiLambdaMassDecayGeomRight; m_{Inv}; Counts", kTH1D, {axisConfigurations.axisLambdaMass}); + histos.add("HelicityEfficiencyQA/hAntiLambdaMassDecayGeomLeft", "hAntiLambdaMassDecayGeomLeft; m_{Inv}; Counts", kTH1D, {axisConfigurations.axisLambdaMass}); } // Integrated observable for events with NLambda+NAntiLambda V0s per event @@ -681,6 +712,11 @@ struct lambdajetpolarizationionsderived { histos.add("JetKinematicsQA/hSubLeadJetEta", "hSubLeadJetEta", kTH1D, {axisConfigurations.axisEta}); histos.add("JetKinematicsQA/hLeadPEta", "hLeadPEta", kTH1D, {axisConfigurations.axisEta}); + // Counting the number of jets/proxies themselves (these count at most once per event) -- Similar to the FOLDER/QA/hPtJet counters: + histos.add("JetKinematicsQA/hJetCounterPtJet", "hJetCounterPtJet; p_{T}^{Jet} (GeV/c)", kTH1D, {axisConfigurations.axisJetPt}); + histos.add("JetKinematicsQA/hJetCounterPtLeadP", "hJetCounterPtLeadP; p_{T}^{LeadP} (GeV/c)", kTH1D, {axisConfigurations.axisJetPt}); + histos.add("JetKinematicsQA/hJetCounterPt2ndJet", "hJetCounterPt2ndJet; p_{T}^{SubJet} (GeV/c)", kTH1D, {axisConfigurations.axisJetPt}); + // Fetch the X-axes from one of the families (since they all share the same ConfigurableAxis binning) mAxisPt = histos.get(HIST("Ring/DeltaMethod/h2dLambdaPtVsDeltaComp"))->GetXaxis(); mAxisMass = histos.get(HIST("Ring/DeltaMethod/h2dMassVsDeltaComp"))->GetXaxis(); @@ -716,9 +752,9 @@ struct lambdajetpolarizationionsderived { const double centrality = getCentrality(collision); // Fetch magnetic field only if DataModel is in the latest version: - float magField = 1.f; // Dummy value - if (analyseMagField) - magField = collision.magField(); + float magField = 1.f; // Used this dummy for backwards compatibility, under the reasonable assumption that the field points always in the same *direction* in the used runs + // if (analyseMagField) + // magField = collision.magField(); // Slice jets, V0s and leading particle belonging to this collision: // (global collision indices repeat a lot, but they are unique to a same TimeFrame (TF) subfolder in the derived data) @@ -769,6 +805,8 @@ struct lambdajetpolarizationionsderived { XYZVector leadPUnitVec(1., 0., 0.); // dummy (overwritten below when hasValidLeadingP) if (hasValidLeadingP) { histos.fill(HIST("JetKinematicsQA/hLeadPEta"), leadPEta); + histos.fill(HIST("JetKinematicsQA/hJetCounterPtLeadP"), leadPPt); + leadPUnitVec = XYZVector(leadPPx, leadPPy, leadPPz).Unit(); // QA: same direction-smearing/perp logic as for the leading jet estimator. // The hLeadPEta histogram above intentionally uses the unmodified direction. @@ -830,6 +868,7 @@ struct lambdajetpolarizationionsderived { // Using internal getters to make code cleaner: leadingJetUnitVec = XYZVector(leadingJet->jetPx(), leadingJet->jetPy(), leadingJet->jetPz()).Unit(); histos.fill(HIST("JetKinematicsQA/hLeadJetEta"), leadingJetEta); // This will not be subject to the forcePerpToJet nor the forceJetDirectionSmudge QAs, for simplicity + histos.fill(HIST("JetKinematicsQA/hJetCounterPtJet"), leadingJetPt); // QA block -- Purposefully changing the jet direction (should kill signal, if any): if (forcePerpToJet) { // Use modified jet direction (done outside loop to guarantee all V0s inside event use same fake jet) @@ -887,6 +926,7 @@ struct lambdajetpolarizationionsderived { subleadingJetPhi = subleadingJet->jetPhi(); subJetUnitVec = XYZVector(subleadingJet->jetPx(), subleadingJet->jetPy(), subleadingJet->jetPz()).Unit(); histos.fill(HIST("JetKinematicsQA/hSubLeadJetEta"), subleadingJetEta); // Unmodified direction + histos.fill(HIST("JetKinematicsQA/hJetCounterPt2ndJet"), subleadingJetPt); // QA: same direction-smearing/perp logic as for the leading jet estimator. if (forcePerpToJet) { XYZVector refVec(1., 0., 0.); From 04e0e02e1f86229833a781be1c22af861cd9309d Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Wed, 8 Apr 2026 17:56:16 +0000 Subject: [PATCH 07/31] Adding plots to study the eta dependence of the Ring Observable. Renaming some histograms and creating subfolders to keep things tidy. --- .../lambdaJetPolarizationIonsDerived.cxx | 171 +++++++++++++----- 1 file changed, 122 insertions(+), 49 deletions(-) diff --git a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx index 39f5b5fa2e5..f5b0a1b6631 100644 --- a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx @@ -95,13 +95,13 @@ enum CentEstimator { /* Additional plots for instant gratification - 1D Profiles */ \ X(FOLDER "/pRingObservableDeltaPhi", deltaPhiJet, ringObservable) \ X(FOLDER "/pRingObservableDeltaTheta", deltaThetaJet, ringObservable) \ - X(FOLDER "/pRingObservableEtaLambda", v0eta, ringObservable) \ - X(FOLDER "/pRingObservableEtaJet", leadingJetEta, ringObservable) \ + X(FOLDER "/EtaDependence/pRingObservableEtaLambda", v0eta, ringObservable) \ + X(FOLDER "/EtaDependence/pRingObservableEtaJet", leadingJetEta, ringObservable) \ X(FOLDER "/pRingObservableIntegrated", 0., ringObservable) \ X(FOLDER "/pRingObservableLambdaPt", v0pt, ringObservable) \ - X(FOLDER "/pRingIntVsPtJet", leadingJetPt, ringObservable) \ - X(FOLDER "/pRingIntVsPtJetVsEtaJet", leadingJetPt, leadingJetEta, ringObservable) \ - X(FOLDER "/pRingIntVsPtJetVsEtaV0", leadingJetPt, v0eta, ringObservable) \ + X(FOLDER "/ProxyPtDependence/pRingVsPtJet", leadingJetPt, ringObservable) \ + X(FOLDER "/ProxyPtDependence/pRingVsPtJetVsEtaJet", leadingJetPt, leadingJetEta, ringObservable) \ + X(FOLDER "/ProxyPtDependence/pRingVsPtJetVsEtaV0", leadingJetPt, v0eta, ringObservable) \ /* 2D Profiles */ \ X(FOLDER "/p2dRingObservableDeltaPhiVsLambdaPt", deltaPhiJet, v0pt, ringObservable) \ X(FOLDER "/p2dRingObservableDeltaThetaVsLambdaPt", deltaThetaJet, v0pt, ringObservable) \ @@ -141,7 +141,7 @@ enum CentEstimator { /* 3D Profiles: Angle vs Mass vs Centrality */ \ X(FOLDER "/p3dRingObservableDeltaPhiVsMassVsCent", deltaPhiJet, v0LambdaLikeMass, centrality, ringObservable) \ X(FOLDER "/p3dRingObservableDeltaThetaVsMassVsCent", deltaThetaJet, v0LambdaLikeMass, centrality, ringObservable) \ - X(FOLDER "/pRingIntVsCentrality", centrality, ringObservable) + X(FOLDER "/pRingVsCentrality", centrality, ringObservable) // (TODO: add counters for regular TH2Ds about centrality) // For leading particle @@ -152,15 +152,53 @@ enum CentEstimator { X(FOLDER "/QA/hCosDeltaThetaLeadP", cosDeltaThetaLeadP) \ X(FOLDER "/pRingObservableLeadPDeltaPhi", deltaPhiLeadP, ringObservableLeadP) \ X(FOLDER "/pRingObservableLeadPDeltaTheta", deltaThetaLeadP, ringObservableLeadP) \ - X(FOLDER "/pRingObservableEtaLambdaLeadP", v0eta, ringObservableLeadP) \ - X(FOLDER "/pRingObservableEtaLeadP", leadPEta, ringObservableLeadP) \ + X(FOLDER "/EtaDependence/pRingObservableEtaLambdaLeadP", v0eta, ringObservableLeadP) \ + X(FOLDER "/EtaDependence/pRingObservableEtaLeadP", leadPEta, ringObservableLeadP) \ X(FOLDER "/pRingObservableLeadPIntegrated", 0., ringObservableLeadP) \ X(FOLDER "/pRingObservableLeadPLambdaPt", v0pt, ringObservableLeadP) \ - X(FOLDER "/pRingIntVsPtLeadP", leadPPt, ringObservableLeadP) \ - X(FOLDER "/pRingIntVsPtLeadPVsEtaLeadP", leadPPt, leadPEta, ringObservableLeadP) \ - X(FOLDER "/pRingIntVsPtLeadPVsEtaV0", leadPPt, v0eta, ringObservableLeadP) \ + X(FOLDER "/ProxyPtDependence/pRingVsPtLeadP", leadPPt, ringObservableLeadP) \ + X(FOLDER "/ProxyPtDependence/pRingVsPtLeadPVsEtaLeadP", leadPPt, leadPEta, ringObservableLeadP) \ + X(FOLDER "/ProxyPtDependence/pRingVsPtLeadPVsEtaV0", leadPPt, v0eta, ringObservableLeadP) \ X(FOLDER "/p2dRingObservableEtaLambdaVsEtaLeadP", v0eta, leadPEta, ringObservableLeadP) +// A macro that encapsulates all eta checks for leading particle and V0s, along with the fills +// Parameters: +// FOLDER -- histogram folder string (compile-time literal) +// LEADP_IS_POS -- bool: leadPEtaPos +// V0_IS_POS -- bool: lambdaEtaPos +#define RING_OBSERVABLE_LEADP_ETA_SPLIT_FILL_LIST(FOLDER, LEADP_IS_POS, V0_IS_POS) \ + do { \ + if (LEADP_IS_POS) { \ + /* leadP marginal: positive side */ \ + APPLY_HISTO_FILL(FOLDER "/ProxyPtDependence/pRingVsPtLeadP_PosEtaLeadP", leadPPt, ringObservableLeadP) \ + if (V0_IS_POS) { \ + /* V0 marginal: positive side */ \ + APPLY_HISTO_FILL(FOLDER "/ProxyPtDependence/pRingVsPtLeadP_PosEtaV0", leadPPt, ringObservableLeadP) \ + /* Joint: (+leadP, +V0) */ \ + APPLY_HISTO_FILL(FOLDER "/ProxyPtDependence/pRingVsPtLeadP_PosEtaLeadP_PosEtaV0", leadPPt, ringObservableLeadP) \ + } else { \ + /* V0 marginal: negative side */ \ + APPLY_HISTO_FILL(FOLDER "/ProxyPtDependence/pRingVsPtLeadP_NegEtaV0", leadPPt, ringObservableLeadP) \ + /* Joint: (+leadP, -V0) */ \ + APPLY_HISTO_FILL(FOLDER "/ProxyPtDependence/pRingVsPtLeadP_PosEtaLeadP_NegEtaV0", leadPPt, ringObservableLeadP) \ + } \ + } else { \ + /* leadP marginal: negative side */ \ + APPLY_HISTO_FILL(FOLDER "/ProxyPtDependence/pRingVsPtLeadP_NegEtaLeadP", leadPPt, ringObservableLeadP) \ + if (V0_IS_POS) { \ + /* V0 marginal: positive side */ \ + APPLY_HISTO_FILL(FOLDER "/ProxyPtDependence/pRingVsPtLeadP_PosEtaV0", leadPPt, ringObservableLeadP) \ + /* Joint: (-leadP, +V0) */ \ + APPLY_HISTO_FILL(FOLDER "/ProxyPtDependence/pRingVsPtLeadP_NegEtaLeadP_PosEtaV0", leadPPt, ringObservableLeadP) \ + } else { \ + /* V0 marginal: negative side */ \ + APPLY_HISTO_FILL(FOLDER "/ProxyPtDependence/pRingVsPtLeadP_NegEtaV0", leadPPt, ringObservableLeadP) \ + /* Joint: (-leadP, -V0) */ \ + APPLY_HISTO_FILL(FOLDER "/ProxyPtDependence/pRingVsPtLeadP_NegEtaLeadP_NegEtaV0", leadPPt, ringObservableLeadP) \ + } \ + } \ + } while (0) + // For subleading jet: #define RING_OBSERVABLE_2NDJET_FILL_LIST(X, FOLDER) \ X(FOLDER "/QA/hDeltaPhi2ndJet", deltaPhi2ndJet) \ @@ -169,13 +207,13 @@ enum CentEstimator { X(FOLDER "/QA/hPt2ndJet", subleadingJetPt) \ X(FOLDER "/pRingObservable2ndJetDeltaPhi", deltaPhi2ndJet, ringObservable2ndJet) \ X(FOLDER "/pRingObservable2ndJetDeltaTheta", deltaTheta2ndJet, ringObservable2ndJet) \ - X(FOLDER "/pRingObservableEtaLambda2ndJet", v0eta, ringObservable2ndJet) \ - X(FOLDER "/pRingObservableEta2ndJet", subleadingJetEta, ringObservable2ndJet) \ + X(FOLDER "/EtaDependence/pRingObservableEtaLambda2ndJet", v0eta, ringObservable2ndJet) \ + X(FOLDER "/EtaDependence/pRingObservableEta2ndJet", subleadingJetEta, ringObservable2ndJet) \ X(FOLDER "/pRingObservable2ndJetIntegrated", 0., ringObservable2ndJet) \ X(FOLDER "/pRingObservable2ndJetLambdaPt", v0pt, ringObservable2ndJet) \ - X(FOLDER "/pRingIntVsPt2ndJet", subleadingJetPt, ringObservable2ndJet) \ - X(FOLDER "/pRingIntVsPt2ndJetVsEta2ndJet", subleadingJetPt, subleadingJetEta, ringObservable2ndJet) \ - X(FOLDER "/pRingIntVsPt2ndJetVsEtaV0", subleadingJetPt, v0eta, ringObservable2ndJet) \ + X(FOLDER "/ProxyPtDependence/pRingVsPt2ndJet", subleadingJetPt, ringObservable2ndJet) \ + X(FOLDER "/ProxyPtDependence/pRingVsPt2ndJetVsEta2ndJet", subleadingJetPt, subleadingJetEta, ringObservable2ndJet) \ + X(FOLDER "/ProxyPtDependence/pRingVsPt2ndJetVsEtaV0", subleadingJetPt, v0eta, ringObservable2ndJet) \ X(FOLDER "/p2dRingObservableEtaLambdaVsEta2ndJet", v0eta, subleadingJetEta, ringObservable2ndJet) #define POLARIZATION_PROFILE_FILL_LIST(X, FOLDER) \ @@ -275,8 +313,17 @@ struct lambdajetpolarizationionsderived { ConfigurableAxis axisLambdaMass{"axisLambdaMass", {450, 1.08f, 1.15f}, "Lambda mass in GeV/c"}; // Default is {200, 1.101f, 1.131f} // Jet axes: - ConfigurableAxis axisLeadingParticlePt{"axisLeadingParticlePt", {100, 0.f, 200.f}, "Leading particle p_{T} (GeV/c)"}; // Simpler version! - ConfigurableAxis axisJetPt{"axisJetPt", {50, 0.f, 200.f}, "Jet p_{t} (GeV)"}; + // ConfigurableAxis axisLeadingParticlePt{"axisLeadingParticlePt", {100, 0.f, 200.f}, "Leading particle p_{T} (GeV/c)"}; // Simpler version! + // ConfigurableAxis axisJetPt{"axisJetPt", {50, 0.f, 200.f}, "Jet p_{t} (GeV)"}; + ConfigurableAxis axisJetPt{ + "axisJetPt", + {VARIABLE_WIDTH, + 0, 2, 4, 6, 8, 10, // 2 GeV bins + 15, 20, 25, 30, // 5 GeV bins + 40, 50, 60, // 10 GeV bins + 80, 120, 160, 200}, // wider bins (20 and 40 GeV) + "Jet p_{T} (GeV)"}; + ConfigurableAxis axisJetPtSigExtract{"axisJetPtSigExtract", {VARIABLE_WIDTH, 0, 5, 10, 12, 16, 20, 25, 30, 35, 40, 60, 100, 200}, "Jet p_{t} (GeV)"}; ConfigurableAxis axisEta{"axisEta", {50, -1.0f, 1.0f}, "#eta"}; ConfigurableAxis axisEtaCoarse{"axisEtaCoarse", {20, -0.9f, 0.9f}, "#eta coarse axis"}; ConfigurableAxis axisDeltaTheta{"axisDeltaTheta", {40, 0, constants::math::PI}, "#Delta #theta_{jet}"}; @@ -290,18 +337,17 @@ struct lambdajetpolarizationionsderived { ConfigurableAxis axisLambdaMassSigExtract{ "axisLambdaMassSigExtract", {VARIABLE_WIDTH, - // Left sideband (7 bins, 0.004 width) - 1.0800, 1.0840, 1.0880, 1.0920, - 1.0960, 1.1000, 1.1040, 1.1080, - // Fine peak region (8 bins, 0.0016 width) - 1.1096, 1.1112, 1.1128, 1.1144, - 1.1160, 1.1176, 1.1192, 1.1208, - // Right sideband (7 bins, 0.004 width) - 1.1248, 1.1288, 1.1328, 1.1368, - 1.1408, 1.1448, 1.1488}, + // Left sideband (7 bins, 0.004 width) + 1.0800, 1.0840, 1.0880, 1.0920, + 1.0960, 1.1000, 1.1040, 1.1080, + // Fine peak region (8 bins, 0.0016 width) + 1.1096, 1.1112, 1.1128, 1.1144, + 1.1160, 1.1176, 1.1192, 1.1208, + // Right sideband (7 bins, 0.004 width) + 1.1248, 1.1288, 1.1328, 1.1368, + 1.1408, 1.1448, 1.1488}, "Lambda mass in GeV/c"}; - ConfigurableAxis axisLeadingParticlePtSigExtract{"axisLeadingParticlePtSigExtract", {VARIABLE_WIDTH, 0, 4, 8, 12, 16, 20, 25, 30, 35, 40, 60, 100, 200}, "Leading particle p_{T} (GeV/c)"}; // Simpler version! - ConfigurableAxis axisJetPtSigExtract{"axisJetPtSigExtract", {VARIABLE_WIDTH, 0, 5, 10, 12, 16, 20, 25, 30, 35, 40, 60, 100, 200}, "Jet p_{t} (GeV)"}; + // ConfigurableAxis axisLeadingParticlePtSigExtract{"axisLeadingParticlePtSigExtract", {VARIABLE_WIDTH, 0, 4, 8, 12, 16, 20, 25, 30, 35, 40, 60, 100, 200}, "Leading particle p_{T} (GeV/c)"}; // Simpler version! // (TODO: add a lambdaPt axis that is pre-selected only on the 0.5 to 1.5 Pt region for the Ring observable with lambda cuts to not store a huge histogram with empty bins by construction) @@ -408,32 +454,45 @@ struct lambdajetpolarizationionsderived { histos.add((folder + "/pRingObservableLambdaPt").c_str(), "pRingObservableLambdaPt;#it{p}_{T}^{#Lambda};<#it{R}>", kTProfile, {axisConfigurations.axisPt}); // Ring vs Jet proxy pT: - histos.add((folder + "/pRingIntVsPtJet").c_str(), "pRingIntVsPtJet; p_{T}^{Jet} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisJetPt}); - histos.add((folder + "/pRingIntVsPtLeadP").c_str(), "pRingIntVsPtLeadP; p_{T}^{LeadP} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisJetPt}); - histos.add((folder + "/pRingIntVsPt2ndJet").c_str(), "pRingIntVsPt2ndJet; p_{T}^{SubJet} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisJetPt}); + histos.add((folder + "/ProxyPtDependence/pRingVsPtJet").c_str(), "pRingVsPtJet; p_{T}^{Jet} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisJetPt}); + histos.add((folder + "/ProxyPtDependence/pRingVsPtLeadP").c_str(), "pRingVsPtLeadP; p_{T}^{LeadP} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisJetPt}); + histos.add((folder + "/ProxyPtDependence/pRingVsPt2ndJet").c_str(), "pRingVsPt2ndJet; p_{T}^{SubJet} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisJetPt}); // And some counters to be aware of the amount of Lambdas (and jets) in each pT interval: histos.add((folder + "/QA/hPtJet").c_str(), "hPtJet", kTH1D, {axisConfigurations.axisJetPt}); histos.add((folder + "/QA/hPtLeadP").c_str(), "hPtLeadP", kTH1D, {axisConfigurations.axisJetPt}); histos.add((folder + "/QA/hPt2ndJet").c_str(), "hPt2ndJet", kTH1D, {axisConfigurations.axisJetPt}); // Splitting into positive and negative eta contributions: - histos.add((folder + "/pRingIntVsPtJetVsEtaJet").c_str(), "pRingIntVsPtJetVsEtaJet; p_{T}^{Jet} (GeV/c);#eta_{Jet};<#it{R}>", kTProfile2D, {axisConfigurations.axisJetPt, {2, -0.9, 0.9}}); - histos.add((folder + "/pRingIntVsPtLeadPVsEtaLeadP").c_str(), "pRingIntVsPtLeadPVsEtaLeadP; p_{T}^{LeadP} (GeV/c);#eta_{LeadP};<#it{R}>", kTProfile2D, {axisConfigurations.axisJetPt, {2, -0.9, 0.9}}); - histos.add((folder + "/pRingIntVsPt2ndJetVsEta2ndJet").c_str(), "pRingIntVsPt2ndJetVsEta2ndJet; p_{T}^{SubJet} (GeV/c);#eta_{SubJet};<#it{R}>", kTProfile2D, {axisConfigurations.axisJetPt, {2, -0.9, 0.9}}); + histos.add((folder + "/ProxyPtDependence/pRingVsPtJetVsEtaJet").c_str(), "Ring, PtJet Vs EtaJet; p_{T}^{Jet} (GeV/c);#eta_{Jet};<#it{R}>", kTProfile2D, {axisConfigurations.axisJetPt, {2, -0.9, 0.9}}); + histos.add((folder + "/ProxyPtDependence/pRingVsPtLeadPVsEtaLeadP").c_str(), "Ring, PtLeadP Vs EtaLeadP; p_{T}^{LeadP} (GeV/c);#eta_{LeadP};<#it{R}>", kTProfile2D, {axisConfigurations.axisJetPt, {2, -0.9, 0.9}}); + histos.add((folder + "/ProxyPtDependence/pRingVsPt2ndJetVsEta2ndJet").c_str(), "Ring, Pt2ndJet Vs Eta2ndJet; p_{T}^{SubJet} (GeV/c);#eta_{SubJet};<#it{R}>", kTProfile2D, {axisConfigurations.axisJetPt, {2, -0.9, 0.9}}); // For each Lambda's eta: - histos.add((folder + "/pRingIntVsPtJetVsEtaV0").c_str(), "pRingIntVsPtJetVsEtaV0; p_{T}^{Jet} (GeV/c);#eta_{V0};<#it{R}>", kTProfile2D, {axisConfigurations.axisJetPt, {2, -0.9, 0.9}}); - histos.add((folder + "/pRingIntVsPtLeadPVsEtaV0").c_str(), "pRingIntVsPtLeadPVsEtaV0; p_{T}^{LeadP} (GeV/c);#eta_{V0};<#it{R}>", kTProfile2D, {axisConfigurations.axisJetPt, {2, -0.9, 0.9}}); - histos.add((folder + "/pRingIntVsPt2ndJetVsEtaV0").c_str(), "pRingIntVsPt2ndJetVsEtaV0; p_{T}^{SubJet} (GeV/c);#eta_{V0};<#it{R}>", kTProfile2D, {axisConfigurations.axisJetPt, {2, -0.9, 0.9}}); + histos.add((folder + "/ProxyPtDependence/pRingVsPtJetVsEtaV0").c_str(), "Ring, PtJetVs Vs EtaV0; p_{T}^{Jet} (GeV/c);#eta_{V0};<#it{R}>", kTProfile2D, {axisConfigurations.axisJetPt, {2, -0.9, 0.9}}); + histos.add((folder + "/ProxyPtDependence/pRingVsPtLeadPVsEtaV0").c_str(), "Ring, PtLeadPVs Vs EtaV0; p_{T}^{LeadP} (GeV/c);#eta_{V0};<#it{R}>", kTProfile2D, {axisConfigurations.axisJetPt, {2, -0.9, 0.9}}); + histos.add((folder + "/ProxyPtDependence/pRingVsPt2ndJetVsEtaV0").c_str(), "Ring, Pt2ndJetVs Vs EtaV0; p_{T}^{SubJet} (GeV/c);#eta_{V0};<#it{R}>", kTProfile2D, {axisConfigurations.axisJetPt, {2, -0.9, 0.9}}); + + // Rasterizing, only for LeadP the TProfile2D into two TProfile 1Ds (easier to draw with "same") + histos.add((folder + "/ProxyPtDependence/pRingVsPtLeadP_PosEtaLeadP").c_str(), "Ring Vs PtLeadP, #eta_{LeadP}>0; p_{T}^{LeadP} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisJetPt}); + histos.add((folder + "/ProxyPtDependence/pRingVsPtLeadP_NegEtaLeadP").c_str(), "Ring Vs PtLeadP, #eta_{LeadP}<0; p_{T}^{LeadP} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisJetPt}); + // V0 eta: + histos.add((folder + "/ProxyPtDependence/pRingVsPtLeadP_PosEtaV0").c_str(), "Ring Vs PtLeadP, #eta_{V0}>0; p_{T}^{LeadP} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisJetPt}); + histos.add((folder + "/ProxyPtDependence/pRingVsPtLeadP_NegEtaV0").c_str(), "Ring Vs PtLeadP, #eta_{V0}<0; p_{T}^{LeadP} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisJetPt}); + + // Looking at V0Eta and JetEta combinations (only for LeadP): + histos.add((folder + "/ProxyPtDependence/pRingVsPtLeadP_PosEtaLeadP_PosEtaV0").c_str(), "Ring Vs PtLeadP, #eta_{LeadP}>0,#eta_{V0}>0; p_{T}^{LeadP} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisJetPt}); + histos.add((folder + "/ProxyPtDependence/pRingVsPtLeadP_NegEtaLeadP_PosEtaV0").c_str(), "Ring Vs PtLeadP, #eta_{LeadP}<0,#eta_{V0}>0; p_{T}^{LeadP} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisJetPt}); + histos.add((folder + "/ProxyPtDependence/pRingVsPtLeadP_PosEtaLeadP_NegEtaV0").c_str(), "Ring Vs PtLeadP, #eta_{LeadP}>0,#eta_{V0}<0; p_{T}^{LeadP} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisJetPt}); + histos.add((folder + "/ProxyPtDependence/pRingVsPtLeadP_NegEtaLeadP_NegEtaV0").c_str(), "Ring Vs PtLeadP, #eta_{LeadP}<0,#eta_{V0}<0; p_{T}^{LeadP} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisJetPt}); // Understanding eta dependence seen in pRingEtaCuts: - histos.add((folder + "/pRingObservableEtaLambda").c_str(), "pRingObservableEtaLambda;#eta_{#Lambda};<#it{R}>", kTProfile, {axisConfigurations.axisEtaCoarse}); - histos.add((folder + "/pRingObservableEtaJet").c_str(), "pRingObservableEtaJet;#eta_{Jet};<#it{R}>", kTProfile, {axisConfigurations.axisEtaCoarse}); + histos.add((folder + "/EtaDependence/pRingObservableEtaLambda").c_str(), "pRingObservableEtaLambda;#eta_{#Lambda};<#it{R}>", kTProfile, {axisConfigurations.axisEtaCoarse}); + histos.add((folder + "/EtaDependence/pRingObservableEtaJet").c_str(), "pRingObservableEtaJet;#eta_{Jet};<#it{R}>", kTProfile, {axisConfigurations.axisEtaCoarse}); - histos.add((folder + "/pRingObservableEtaLambda2ndJet").c_str(), "pRingObservableEtaLambda2ndJet;#eta_{#Lambda};<#it{R}>", kTProfile, {axisConfigurations.axisEtaCoarse}); - histos.add((folder + "/pRingObservableEta2ndJet").c_str(), "pRingObservableEta2ndJet;#eta_{2ndJet};<#it{R}>", kTProfile, {axisConfigurations.axisEtaCoarse}); + histos.add((folder + "/EtaDependence/pRingObservableEtaLambda2ndJet").c_str(), "pRingObservableEtaLambda2ndJet;#eta_{#Lambda};<#it{R}>", kTProfile, {axisConfigurations.axisEtaCoarse}); + histos.add((folder + "/EtaDependence/pRingObservableEta2ndJet").c_str(), "pRingObservableEta2ndJet;#eta_{2ndJet};<#it{R}>", kTProfile, {axisConfigurations.axisEtaCoarse}); - histos.add((folder + "/pRingObservableEtaLambdaLeadP").c_str(), "pRingObservableEtaLambdaLeadP;#eta_{#Lambda};<#it{R}>", kTProfile, {axisConfigurations.axisEtaCoarse}); - histos.add((folder + "/pRingObservableEtaLeadP").c_str(), "pRingObservableEtaLeadP;#eta_{LeadP};<#it{R}>", kTProfile, {axisConfigurations.axisEtaCoarse}); + histos.add((folder + "/EtaDependence/pRingObservableEtaLambdaLeadP").c_str(), "pRingObservableEtaLambdaLeadP;#eta_{#Lambda};<#it{R}>", kTProfile, {axisConfigurations.axisEtaCoarse}); + histos.add((folder + "/EtaDependence/pRingObservableEtaLeadP").c_str(), "pRingObservableEtaLeadP;#eta_{LeadP};<#it{R}>", kTProfile, {axisConfigurations.axisEtaCoarse}); // For the leading particle: histos.add((folder + "/pRingObservableLeadPDeltaPhi").c_str(), "pRingObservableLeadPDeltaPhi;#Delta#varphi_{leadP};<#it{R}>", kTProfile, {axisConfigurations.axisDeltaPhi}); histos.add((folder + "/pRingObservableLeadPDeltaTheta").c_str(), "pRingObservableLeadPDeltaTheta;#Delta#theta_{leadP};<#it{R}>", kTProfile, {axisConfigurations.axisDeltaTheta}); @@ -506,7 +565,7 @@ struct lambdajetpolarizationionsderived { histos.add((folder + "/QA/h3dDeltaThetaVsMassVsCent").c_str(), "h3dDeltaThetaVsMassVsCent", kTH3D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisCentrality}); // Useful TProfiles: // --- TProfile1D: Integrated vs Centrality: - histos.add((folder + "/pRingIntVsCentrality").c_str(), "pRingIntVsCentrality; Centrality (%);<#it{R}>", kTProfile, {axisConfigurations.axisCentrality}); + histos.add((folder + "/pRingVsCentrality").c_str(), "pRingVsCentrality; Centrality (%);<#it{R}>", kTProfile, {axisConfigurations.axisCentrality}); // --- TProfile2D: vs Mass vs Centrality --- histos.add((folder + "/p2dRingObservableMassVsCent").c_str(), "p2dRingObservableMassVsCent;m_{p#pi};Centrality;<#it{R}>", kTProfile2D, {axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisCentrality}); // --- TProfile3D: vs DeltaPhi vs Mass vs Centrality --- @@ -1043,6 +1102,12 @@ struct lambdajetpolarizationionsderived { histos.fill(HIST("HelicityEfficiencyQA/hAntiLambdaMassDecayGeomLeft"), v0LambdaLikeMass); } + // Useful kinematic bools: + const bool lambdaEtaPos = v0eta >= 0.; + const bool pTLambdaCheck = v0pt > 0.5 && v0pt < 1.5; + const bool rapidityLambdaCheck = std::abs(lambdaRapidity) < 0.5; + const bool kinematicLambdaCheck = pTLambdaCheck && rapidityLambdaCheck; + //////////////////////////////////////////// // Ring observable: Leading particle proxy // Only computed when a valid leading particle exists (pT > minLeadParticlePt) @@ -1134,6 +1199,9 @@ struct lambdajetpolarizationionsderived { // No, there should NOT be any ";" here! Read the macro definition for an explanation histos.fill(HIST("IntegratedCuts/pRingCutsLeadingP"), 0, ringObservableLeadP); // First bin of comparison histos.fill(HIST("IntegratedCuts/hCountCutsLeadingP"), 0); + + // Filling checks that rely on Eta>0 or Eta<0 checks for V0 and LeadingP eta: + RING_OBSERVABLE_LEADP_ETA_SPLIT_FILL_LIST("Ring", leadPEtaPos, lambdaEtaPos); } POLARIZATION_PROFILE_FILL_LIST(APPLY_HISTO_FILL, "Ring") @@ -1160,10 +1228,6 @@ struct lambdajetpolarizationionsderived { } // Filling eta dependence QAs of the result (both for V0 and jet proxy): - const bool lambdaEtaPos = v0eta >= 0.; - const bool pTLambdaCheck = v0pt > 0.5 && v0pt < 1.5; - const bool rapidityLambdaCheck = std::abs(lambdaRapidity) < 0.5; - const bool kinematicLambdaCheck = pTLambdaCheck && rapidityLambdaCheck; if (hasValidLeadingJet) { histos.fill(HIST("ProxyEta/pRingEtaCuts"), 0, ringObservable); histos.fill(HIST("ProxyEta/pRingEtaCuts"), lambdaEtaPos ? 3 : 4, ringObservable); @@ -1283,6 +1347,9 @@ struct lambdajetpolarizationionsderived { RING_OBSERVABLE_LEADP_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts") histos.fill(HIST("IntegratedCuts/pRingCutsLeadingP"), 1, ringObservableLeadP); histos.fill(HIST("IntegratedCuts/hCountCutsLeadingP"), 1); + + // Filling checks that rely on Eta>0 or Eta<0 checks for V0 and LeadingP eta: + RING_OBSERVABLE_LEADP_ETA_SPLIT_FILL_LIST("RingKinematicCuts", leadPEtaPos, lambdaEtaPos); } POLARIZATION_PROFILE_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts") if (hasValidLeadingJet) { @@ -1323,6 +1390,9 @@ struct lambdajetpolarizationionsderived { RING_OBSERVABLE_LEADP_FILL_LIST(APPLY_HISTO_FILL, "JetKinematicCuts") histos.fill(HIST("IntegratedCuts/pRingCutsLeadingP"), 2, ringObservableLeadP); histos.fill(HIST("IntegratedCuts/hCountCutsLeadingP"), 2); + + // Filling checks that rely on Eta>0 or Eta<0 checks for V0 and LeadingP eta: + RING_OBSERVABLE_LEADP_ETA_SPLIT_FILL_LIST("JetKinematicCuts", leadPEtaPos, lambdaEtaPos); } if (kinematic2ndJetCheck) { RING_OBSERVABLE_2NDJET_FILL_LIST(APPLY_HISTO_FILL, "JetKinematicCuts") @@ -1333,6 +1403,9 @@ struct lambdajetpolarizationionsderived { RING_OBSERVABLE_LEADP_FILL_LIST(APPLY_HISTO_FILL, "JetAndLambdaKinematicCuts") histos.fill(HIST("IntegratedCuts/pRingCutsLeadingP"), 3, ringObservableLeadP); histos.fill(HIST("IntegratedCuts/hCountCutsLeadingP"), 3); + + // Filling checks that rely on Eta>0 or Eta<0 checks for V0 and LeadingP eta: + RING_OBSERVABLE_LEADP_ETA_SPLIT_FILL_LIST("JetAndLambdaKinematicCuts", leadPEtaPos, lambdaEtaPos); } if (kinematicLambdaCheck && kinematic2ndJetCheck) { RING_OBSERVABLE_2NDJET_FILL_LIST(APPLY_HISTO_FILL, "JetAndLambdaKinematicCuts") From 55f65252cef84b18a6b58a8312861b77a7775dc0 Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Fri, 1 May 2026 00:59:09 +0000 Subject: [PATCH 08/31] Producer: improving DCA cuts to minimize AEE and HEE impact (fake polarization sources from efficiency effects). Consumer: better titles for histograms, new plots to probe AEE and HEE effects. --- .../Strangeness/lambdaJetPolarizationIons.cxx | 6 +- .../lambdaJetPolarizationIonsDerived.cxx | 289 +++++++++++------- 2 files changed, 190 insertions(+), 105 deletions(-) diff --git a/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx b/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx index 1bfa7fefaa9..aba0f72bceb 100644 --- a/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx +++ b/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx @@ -235,13 +235,13 @@ struct lambdajetpolarizationions { // Standard 5 topological criteria -- Closed a bit more for the Lambda analysis Configurable v0cospa{"v0cospa", 0.995, "min V0 CosPA"}; // Default is 0.97 - Configurable dcav0dau{"dcav0dau", 1.0, "max DCA V0 Daughters (cm)"}; // Default is 1.0 + Configurable dcav0dau{"dcav0dau", 1.2, "max DCA V0 Daughters (cm)"}; // Default is 1.0 // Configurable dcanegtopv{"dcanegtopv", .2, "min DCA Neg To PV (cm)"}; // Default is .05 // Configurable dcapostopv{"dcapostopv", .05, "min DCA Pos To PV (cm)"}; // Default is .05 // Renamed for better consistency of candidate selection (the cut is not determined by charge, but by mass and how deflected the daughter is): - Configurable dcaPionToPV{"dcaPionToPV", .2, "min DCA pion-like daughter To PV (cm)"}; // Default is .05. Suppresses pion background. + Configurable dcaPionToPV{"dcaPionToPV", .05, "min DCA pion-like daughter To PV (cm)"}; // .2 would suppress primary pion background, but we need to reduce fake-polarization signals enhanced by DCA-to-PV cuts Configurable dcaProtonToPV{"dcaProtonToPV", .05, "min DCA proton-like daughter To PV (cm)"}; // Default is .05 - Configurable v0radius{"v0radius", 1.2, "minimum V0 radius (cm)"}; // Default is 1.2 + Configurable v0radius{"v0radius", 1.0, "minimum V0 radius (cm)"}; // Default is 1.2 Configurable v0radiusMax{"v0radiusMax", 1E5, "maximum V0 radius (cm)"}; Configurable lambdaLifetimeCut{"lambdaLifetimeCut", 30., "lifetime cut (c*tau) for Lambda (cm)"}; diff --git a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx index f5b0a1b6631..92c43adb4c5 100644 --- a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx @@ -129,7 +129,10 @@ enum CentEstimator { X(FOLDER "/p2dRingObservableDeltaPhiVsMass", deltaPhiJet, v0LambdaLikeMass, ringObservable) \ X(FOLDER "/p2dRingObservableDeltaThetaVsMass", deltaThetaJet, v0LambdaLikeMass, ringObservable) \ /* 2D Profile: Ring vs Eta variables */ \ - X(FOLDER "/p2dRingObservableEtaLambdaVsEtaJet", v0eta, leadingJetEta, ringObservable) \ + X(FOLDER "/EtaDependence/p2dRingObservableEtaLambdaVsEtaJet", v0eta, leadingJetEta, ringObservable) \ + X(FOLDER "/EtaDependence/h2dCounterEtaLambdaVsEtaJet", v0eta, leadingJetEta) \ + X(FOLDER "/EtaDependence/p2dRingObservableEtaLambdaVsEtaJet_FineBins", v0eta, leadingJetEta, ringObservable) \ + X(FOLDER "/EtaDependence/h2dCounterEtaLambdaVsEtaJet_FineBins", v0eta, leadingJetEta) \ /* 3D Profiles: Angle vs Mass vs Lambda pT */ \ X(FOLDER "/p3dRingObservableDeltaPhiVsMassVsLambdaPt", deltaPhiJet, v0LambdaLikeMass, v0pt, ringObservable) \ X(FOLDER "/p3dRingObservableDeltaThetaVsMassVsLambdaPt", deltaThetaJet, v0LambdaLikeMass, v0pt, ringObservable) \ @@ -159,7 +162,8 @@ enum CentEstimator { X(FOLDER "/ProxyPtDependence/pRingVsPtLeadP", leadPPt, ringObservableLeadP) \ X(FOLDER "/ProxyPtDependence/pRingVsPtLeadPVsEtaLeadP", leadPPt, leadPEta, ringObservableLeadP) \ X(FOLDER "/ProxyPtDependence/pRingVsPtLeadPVsEtaV0", leadPPt, v0eta, ringObservableLeadP) \ - X(FOLDER "/p2dRingObservableEtaLambdaVsEtaLeadP", v0eta, leadPEta, ringObservableLeadP) + X(FOLDER "/EtaDependence/p2dRingObservableEtaLambdaVsEtaLeadP", v0eta, leadPEta, ringObservableLeadP) \ + X(FOLDER "/EtaDependence/h2dCounterEtaLambdaVsEtaLeadP", v0eta, leadPEta) // A macro that encapsulates all eta checks for leading particle and V0s, along with the fills // Parameters: @@ -214,7 +218,8 @@ enum CentEstimator { X(FOLDER "/ProxyPtDependence/pRingVsPt2ndJet", subleadingJetPt, ringObservable2ndJet) \ X(FOLDER "/ProxyPtDependence/pRingVsPt2ndJetVsEta2ndJet", subleadingJetPt, subleadingJetEta, ringObservable2ndJet) \ X(FOLDER "/ProxyPtDependence/pRingVsPt2ndJetVsEtaV0", subleadingJetPt, v0eta, ringObservable2ndJet) \ - X(FOLDER "/p2dRingObservableEtaLambdaVsEta2ndJet", v0eta, subleadingJetEta, ringObservable2ndJet) + X(FOLDER "/EtaDependence/p2dRingObservableEtaLambdaVsEta2ndJet", v0eta, subleadingJetEta, ringObservable2ndJet) \ + X(FOLDER "/EtaDependence/h2dCounterEtaLambdaVsEta2ndJet", v0eta, subleadingJetEta) \ #define POLARIZATION_PROFILE_FILL_LIST(X, FOLDER) \ /* =============================== */ \ @@ -319,9 +324,10 @@ struct lambdajetpolarizationionsderived { "axisJetPt", {VARIABLE_WIDTH, 0, 2, 4, 6, 8, 10, // 2 GeV bins - 15, 20, 25, 30, // 5 GeV bins - 40, 50, 60, // 10 GeV bins - 80, 120, 160, 200}, // wider bins (20 and 40 GeV) + 15, 20, // 5 GeV bins + 30, 40, // 10 GeV bins + 60, 80, // 20 GeV bins + 120, 160, 200}, // 40 GeV bins "Jet p_{T} (GeV)"}; ConfigurableAxis axisJetPtSigExtract{"axisJetPtSigExtract", {VARIABLE_WIDTH, 0, 5, 10, 12, 16, 20, 25, 30, 35, 40, 60, 100, 200}, "Jet p_{t} (GeV)"}; ConfigurableAxis axisEta{"axisEta", {50, -1.0f, 1.0f}, "#eta"}; @@ -398,24 +404,24 @@ struct lambdajetpolarizationionsderived { // QA histograms: angle and pT distributions // (No mass dependency -- useful to check kinematic sculpting from cuts) // =============================== - histos.add((folder + "/QA/hDeltaPhi").c_str(), "hDeltaPhi", kTH1D, {axisConfigurations.axisDeltaPhi}); - histos.add((folder + "/QA/hDeltaTheta").c_str(), "hDeltaTheta", kTH1D, {axisConfigurations.axisDeltaTheta}); - histos.add((folder + "/QA/hCosDeltaTheta").c_str(), "hCosDeltaTheta", kTH1D, {axisConfigurations.axisCosTheta}); // Should actually be flat due to the geometry - histos.add((folder + "/QA/hIntegrated").c_str(), "hIntegrated", kTH1D, {{1, -0.5, 0.5}}); - - histos.add((folder + "/QA/hDeltaPhiLeadP").c_str(), "hDeltaPhiLeadP", kTH1D, {axisConfigurations.axisDeltaPhi}); - histos.add((folder + "/QA/hDeltaThetaLeadP").c_str(), "hDeltaThetaLeadP", kTH1D, {axisConfigurations.axisDeltaTheta}); - histos.add((folder + "/QA/hCosDeltaThetaLeadP").c_str(), "hCosDeltaThetaLeadP", kTH1D, {axisConfigurations.axisCosTheta}); // Should actually be flat due to the geometry - histos.add((folder + "/QA/hDeltaPhi2ndJet").c_str(), "hDeltaPhi2ndJet", kTH1D, {axisConfigurations.axisDeltaPhi}); - histos.add((folder + "/QA/hDeltaTheta2ndJet").c_str(), "hDeltaTheta2ndJet", kTH1D, {axisConfigurations.axisDeltaTheta}); - histos.add((folder + "/QA/hCosDeltaTheta2ndJet").c_str(), "hCosDeltaTheta2ndJet", kTH1D, {axisConfigurations.axisCosTheta}); // Should actually be flat due to the geometry + histos.add((folder + "/QA/hDeltaPhi").c_str(), "#Delta#varphi_{jet};#Delta#varphi_{jet};Counts", kTH1D, {axisConfigurations.axisDeltaPhi}); + histos.add((folder + "/QA/hDeltaTheta").c_str(), "#Delta#theta_{jet};#Delta#theta_{jet};Counts", kTH1D, {axisConfigurations.axisDeltaTheta}); + histos.add((folder + "/QA/hCosDeltaTheta").c_str(), "cos(#Delta#theta_{jet});cos(#Delta#theta_{jet});Counts", kTH1D, {axisConfigurations.axisCosTheta}); // Should actually be flat due to the geometry + histos.add((folder + "/QA/hIntegrated").c_str(), "Integrated counts; ;Counts", kTH1D, {{1, -0.5, 0.5}}); + + histos.add((folder + "/QA/hDeltaPhiLeadP").c_str(), "#Delta#varphi_{LeadP};#Delta#varphi_{LeadP};Counts", kTH1D, {axisConfigurations.axisDeltaPhi}); + histos.add((folder + "/QA/hDeltaThetaLeadP").c_str(), "#Delta#theta_{LeadP};#Delta#theta_{LeadP};Counts", kTH1D, {axisConfigurations.axisDeltaTheta}); + histos.add((folder + "/QA/hCosDeltaThetaLeadP").c_str(), "cos(#Delta#theta_{LeadP});cos(#Delta#theta_{LeadP});Counts", kTH1D, {axisConfigurations.axisCosTheta}); // Should actually be flat due to the geometry + histos.add((folder + "/QA/hDeltaPhi2ndJet").c_str(), "#Delta#varphi_{SubJet};#Delta#varphi_{SubJet};Counts", kTH1D, {axisConfigurations.axisDeltaPhi}); + histos.add((folder + "/QA/hDeltaTheta2ndJet").c_str(), "#Delta#theta_{SubJet};#Delta#theta_{SubJet};Counts", kTH1D, {axisConfigurations.axisDeltaTheta}); + histos.add((folder + "/QA/hCosDeltaTheta2ndJet").c_str(), "cos(#Delta#theta_{SubJet});cos(#Delta#theta_{SubJet});Counts", kTH1D, {axisConfigurations.axisCosTheta}); // Should actually be flat due to the geometry // =============================== // Lambda pT dependence // =============================== - histos.add((folder + "/QA/hLambdaPt").c_str(), "hLambdaPt", kTH1D, {axisConfigurations.axisPt}); - histos.add((folder + "/QA/h2dDeltaPhiVsLambdaPt").c_str(), "h2dDeltaPhiVsLambdaPt", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisPt}); - histos.add((folder + "/QA/h2dDeltaThetaVsLambdaPt").c_str(), "h2dDeltaThetaVsLambdaPt", kTH2D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisPt}); + histos.add((folder + "/QA/hLambdaPt").c_str(), "#Lambda #it{p}_{T};#it{p}_{T}^{#Lambda} (GeV/c);Counts", kTH1D, {axisConfigurations.axisPt}); + histos.add((folder + "/QA/h2dDeltaPhiVsLambdaPt").c_str(), "#Delta#varphi_{jet} vs #Lambda #it{p}_{T};#Delta#varphi_{jet};#it{p}_{T}^{#Lambda} (GeV/c)", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisPt}); + histos.add((folder + "/QA/h2dDeltaThetaVsLambdaPt").c_str(), "#Delta#theta_{jet} vs #Lambda #it{p}_{T};#Delta#theta_{jet};#it{p}_{T}^{#Lambda} (GeV/c)", kTH2D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisPt}); // =============================== // Polarization observable QAs // (not Ring: actual polarization!) @@ -424,18 +430,18 @@ struct lambdajetpolarizationionsderived { // =============================== // 1D TProfiles // =============================== - histos.add((folder + "/QA/pPxStarPhi").c_str(), "pPxStarPhi;#varphi_{#Lambda};_{x}", kTProfile, {axisConfigurations.axisDeltaPhi}); - histos.add((folder + "/QA/pPyStarPhi").c_str(), "pPyStarPhi;#varphi_{#Lambda};_{y}", kTProfile, {axisConfigurations.axisDeltaPhi}); - histos.add((folder + "/QA/pPzStarPhi").c_str(), "pPzStarPhi;#varphi_{#Lambda};_{z}", kTProfile, {axisConfigurations.axisDeltaPhi}); - histos.add((folder + "/QA/pPxStarDeltaPhi").c_str(), "pPxStarDeltaPhi;#Delta#varphi_{jet};_{x}", kTProfile, {axisConfigurations.axisDeltaPhi}); - histos.add((folder + "/QA/pPyStarDeltaPhi").c_str(), "pPyStarDeltaPhi;#Delta#varphi_{jet};_{y}", kTProfile, {axisConfigurations.axisDeltaPhi}); - histos.add((folder + "/QA/pPzStarDeltaPhi").c_str(), "pPzStarDeltaPhi;#Delta#varphi_{jet};_{z}", kTProfile, {axisConfigurations.axisDeltaPhi}); + histos.add((folder + "/QA/pPxStarPhi").c_str(), "_{x} vs #varphi_{#Lambda};#varphi_{#Lambda};_{x}", kTProfile, {axisConfigurations.axisDeltaPhi}); + histos.add((folder + "/QA/pPyStarPhi").c_str(), "_{y} vs #varphi_{#Lambda};#varphi_{#Lambda};_{y}", kTProfile, {axisConfigurations.axisDeltaPhi}); + histos.add((folder + "/QA/pPzStarPhi").c_str(), "_{z} vs #varphi_{#Lambda};#varphi_{#Lambda};_{z}", kTProfile, {axisConfigurations.axisDeltaPhi}); + histos.add((folder + "/QA/pPxStarDeltaPhi").c_str(), "_{x} vs #Delta#varphi_{jet};#Delta#varphi_{jet};_{x}", kTProfile, {axisConfigurations.axisDeltaPhi}); + histos.add((folder + "/QA/pPyStarDeltaPhi").c_str(), "_{y} vs #Delta#varphi_{jet};#Delta#varphi_{jet};_{y}", kTProfile, {axisConfigurations.axisDeltaPhi}); + histos.add((folder + "/QA/pPzStarDeltaPhi").c_str(), "_{z} vs #Delta#varphi_{jet};#Delta#varphi_{jet};_{z}", kTProfile, {axisConfigurations.axisDeltaPhi}); // =============================== // 2D TProfiles (Lambda correlations) // =============================== - histos.add((folder + "/QA/p2dPxStarDeltaPhiVsLambdaPt").c_str(), "p2dPxStarDeltaPhiVsLambdaPt;#Delta#varphi_{jet};#it{p}_{T}^{#Lambda};_{x}", kTProfile2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisPtSigExtract}); - histos.add((folder + "/QA/p2dPyStarDeltaPhiVsLambdaPt").c_str(), "p2dPyStarDeltaPhiVsLambdaPt;#Delta#varphi_{jet};#it{p}_{T}^{#Lambda};_{y}", kTProfile2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisPtSigExtract}); - histos.add((folder + "/QA/p2dPzStarDeltaPhiVsLambdaPt").c_str(), "p2dPzStarDeltaPhiVsLambdaPt;#Delta#varphi_{jet};#it{p}_{T}^{#Lambda};_{z}", kTProfile2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisPtSigExtract}); + histos.add((folder + "/QA/p2dPxStarDeltaPhiVsLambdaPt").c_str(), "_{x} vs #Delta#varphi_{jet} vs #it{p}_{T}^{#Lambda};#Delta#varphi_{jet};#it{p}_{T}^{#Lambda} (GeV/c);_{x}", kTProfile2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisPtSigExtract}); + histos.add((folder + "/QA/p2dPyStarDeltaPhiVsLambdaPt").c_str(), "_{y} vs #Delta#varphi_{jet} vs #it{p}_{T}^{#Lambda};#Delta#varphi_{jet};#it{p}_{T}^{#Lambda} (GeV/c);_{y}", kTProfile2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisPtSigExtract}); + histos.add((folder + "/QA/p2dPzStarDeltaPhiVsLambdaPt").c_str(), "_{z} vs #Delta#varphi_{jet} vs #it{p}_{T}^{#Lambda};#Delta#varphi_{jet};#it{p}_{T}^{#Lambda} (GeV/c);_{z}", kTProfile2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisPtSigExtract}); // TProfiles with correct error bars:: // -- TProfiles will handle the error estimate of the Ring Observable via the variance, even though @@ -448,130 +454,136 @@ struct lambdajetpolarizationionsderived { // =============================== // 1D TProfiles // =============================== - histos.add((folder + "/pRingObservableDeltaPhi").c_str(), "pRingObservableDeltaPhi;#Delta#varphi_{jet};<#it{R}>", kTProfile, {axisConfigurations.axisDeltaPhi}); - histos.add((folder + "/pRingObservableDeltaTheta").c_str(), "pRingObservableDeltaTheta;#Delta#theta_{jet};<#it{R}>", kTProfile, {axisConfigurations.axisDeltaTheta}); - histos.add((folder + "/pRingObservableIntegrated").c_str(), "pRingObservableIntegrated; ;<#it{R}>", kTProfile, {{1, -0.5, 0.5}}); - histos.add((folder + "/pRingObservableLambdaPt").c_str(), "pRingObservableLambdaPt;#it{p}_{T}^{#Lambda};<#it{R}>", kTProfile, {axisConfigurations.axisPt}); + histos.add((folder + "/pRingObservableDeltaPhi").c_str(), "<#it{R}> vs #Delta#varphi_{jet};#Delta#varphi_{jet};<#it{R}>", kTProfile, {axisConfigurations.axisDeltaPhi}); + histos.add((folder + "/pRingObservableDeltaTheta").c_str(), "<#it{R}> vs #Delta#theta_{jet};#Delta#theta_{jet};<#it{R}>", kTProfile, {axisConfigurations.axisDeltaTheta}); + histos.add((folder + "/pRingObservableIntegrated").c_str(), "Integrated <#it{R}>; ;<#it{R}>", kTProfile, {{1, -0.5, 0.5}}); + histos.add((folder + "/pRingObservableLambdaPt").c_str(), "<#it{R}> vs #it{p}_{T}^{#Lambda};#it{p}_{T}^{#Lambda} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisPt}); // Ring vs Jet proxy pT: - histos.add((folder + "/ProxyPtDependence/pRingVsPtJet").c_str(), "pRingVsPtJet; p_{T}^{Jet} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisJetPt}); - histos.add((folder + "/ProxyPtDependence/pRingVsPtLeadP").c_str(), "pRingVsPtLeadP; p_{T}^{LeadP} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisJetPt}); - histos.add((folder + "/ProxyPtDependence/pRingVsPt2ndJet").c_str(), "pRingVsPt2ndJet; p_{T}^{SubJet} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisJetPt}); + histos.add((folder + "/ProxyPtDependence/pRingVsPtJet").c_str(), "<#it{R}> vs Jet #it{p}_{T};#it{p}_{T}^{Jet} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisJetPt}); + histos.add((folder + "/ProxyPtDependence/pRingVsPtLeadP").c_str(), "<#it{R}> vs LeadP #it{p}_{T};#it{p}_{T}^{LeadP} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisJetPt}); + histos.add((folder + "/ProxyPtDependence/pRingVsPt2ndJet").c_str(), "<#it{R}> vs SubJet #it{p}_{T};#it{p}_{T}^{SubJet} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisJetPt}); // And some counters to be aware of the amount of Lambdas (and jets) in each pT interval: - histos.add((folder + "/QA/hPtJet").c_str(), "hPtJet", kTH1D, {axisConfigurations.axisJetPt}); - histos.add((folder + "/QA/hPtLeadP").c_str(), "hPtLeadP", kTH1D, {axisConfigurations.axisJetPt}); - histos.add((folder + "/QA/hPt2ndJet").c_str(), "hPt2ndJet", kTH1D, {axisConfigurations.axisJetPt}); + histos.add((folder + "/QA/hPtJet").c_str(), "Jet #it{p}_{T};#it{p}_{T}^{Jet} (GeV/c);Counts", kTH1D, {axisConfigurations.axisJetPt}); + histos.add((folder + "/QA/hPtLeadP").c_str(), "LeadP #it{p}_{T};#it{p}_{T}^{LeadP} (GeV/c);Counts", kTH1D, {axisConfigurations.axisJetPt}); + histos.add((folder + "/QA/hPt2ndJet").c_str(), "SubJet #it{p}_{T};#it{p}_{T}^{SubJet} (GeV/c);Counts", kTH1D, {axisConfigurations.axisJetPt}); // Splitting into positive and negative eta contributions: - histos.add((folder + "/ProxyPtDependence/pRingVsPtJetVsEtaJet").c_str(), "Ring, PtJet Vs EtaJet; p_{T}^{Jet} (GeV/c);#eta_{Jet};<#it{R}>", kTProfile2D, {axisConfigurations.axisJetPt, {2, -0.9, 0.9}}); - histos.add((folder + "/ProxyPtDependence/pRingVsPtLeadPVsEtaLeadP").c_str(), "Ring, PtLeadP Vs EtaLeadP; p_{T}^{LeadP} (GeV/c);#eta_{LeadP};<#it{R}>", kTProfile2D, {axisConfigurations.axisJetPt, {2, -0.9, 0.9}}); - histos.add((folder + "/ProxyPtDependence/pRingVsPt2ndJetVsEta2ndJet").c_str(), "Ring, Pt2ndJet Vs Eta2ndJet; p_{T}^{SubJet} (GeV/c);#eta_{SubJet};<#it{R}>", kTProfile2D, {axisConfigurations.axisJetPt, {2, -0.9, 0.9}}); + histos.add((folder + "/ProxyPtDependence/pRingVsPtJetVsEtaJet").c_str(), "<#it{R}> vs Jet #it{p}_{T} vs #eta_{Jet};#it{p}_{T}^{Jet} (GeV/c);#eta_{Jet};<#it{R}>", kTProfile2D, {axisConfigurations.axisJetPt, {2, -0.9, 0.9}}); + histos.add((folder + "/ProxyPtDependence/pRingVsPtLeadPVsEtaLeadP").c_str(), "<#it{R}> vs LeadP #it{p}_{T} vs #eta_{LeadP};#it{p}_{T}^{LeadP} (GeV/c);#eta_{LeadP};<#it{R}>", kTProfile2D, {axisConfigurations.axisJetPt, {2, -0.9, 0.9}}); + histos.add((folder + "/ProxyPtDependence/pRingVsPt2ndJetVsEta2ndJet").c_str(), "<#it{R}> vs SubJet #it{p}_{T} vs #eta_{SubJet};#it{p}_{T}^{SubJet} (GeV/c);#eta_{SubJet};<#it{R}>", kTProfile2D, {axisConfigurations.axisJetPt, {2, -0.9, 0.9}}); // For each Lambda's eta: - histos.add((folder + "/ProxyPtDependence/pRingVsPtJetVsEtaV0").c_str(), "Ring, PtJetVs Vs EtaV0; p_{T}^{Jet} (GeV/c);#eta_{V0};<#it{R}>", kTProfile2D, {axisConfigurations.axisJetPt, {2, -0.9, 0.9}}); - histos.add((folder + "/ProxyPtDependence/pRingVsPtLeadPVsEtaV0").c_str(), "Ring, PtLeadPVs Vs EtaV0; p_{T}^{LeadP} (GeV/c);#eta_{V0};<#it{R}>", kTProfile2D, {axisConfigurations.axisJetPt, {2, -0.9, 0.9}}); - histos.add((folder + "/ProxyPtDependence/pRingVsPt2ndJetVsEtaV0").c_str(), "Ring, Pt2ndJetVs Vs EtaV0; p_{T}^{SubJet} (GeV/c);#eta_{V0};<#it{R}>", kTProfile2D, {axisConfigurations.axisJetPt, {2, -0.9, 0.9}}); + histos.add((folder + "/ProxyPtDependence/pRingVsPtJetVsEtaV0").c_str(), "<#it{R}> vs Jet #it{p}_{T} vs #eta_{V0};#it{p}_{T}^{Jet} (GeV/c);#eta_{V0};<#it{R}>", kTProfile2D, {axisConfigurations.axisJetPt, {2, -0.9, 0.9}}); + histos.add((folder + "/ProxyPtDependence/pRingVsPtLeadPVsEtaV0").c_str(), "<#it{R}> vs LeadP #it{p}_{T} vs #eta_{V0};#it{p}_{T}^{LeadP} (GeV/c);#eta_{V0};<#it{R}>", kTProfile2D, {axisConfigurations.axisJetPt, {2, -0.9, 0.9}}); + histos.add((folder + "/ProxyPtDependence/pRingVsPt2ndJetVsEtaV0").c_str(), "<#it{R}> vs SubJet #it{p}_{T} vs #eta_{V0};#it{p}_{T}^{SubJet} (GeV/c);#eta_{V0};<#it{R}>", kTProfile2D, {axisConfigurations.axisJetPt, {2, -0.9, 0.9}}); // Rasterizing, only for LeadP the TProfile2D into two TProfile 1Ds (easier to draw with "same") - histos.add((folder + "/ProxyPtDependence/pRingVsPtLeadP_PosEtaLeadP").c_str(), "Ring Vs PtLeadP, #eta_{LeadP}>0; p_{T}^{LeadP} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisJetPt}); - histos.add((folder + "/ProxyPtDependence/pRingVsPtLeadP_NegEtaLeadP").c_str(), "Ring Vs PtLeadP, #eta_{LeadP}<0; p_{T}^{LeadP} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisJetPt}); + histos.add((folder + "/ProxyPtDependence/pRingVsPtLeadP_PosEtaLeadP").c_str(), "<#it{R}> vs LeadP #it{p}_{T} (#eta_{LeadP}>0);#it{p}_{T}^{LeadP} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisJetPt}); + histos.add((folder + "/ProxyPtDependence/pRingVsPtLeadP_NegEtaLeadP").c_str(), "<#it{R}> vs LeadP #it{p}_{T} (#eta_{LeadP}<0);#it{p}_{T}^{LeadP} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisJetPt}); // V0 eta: - histos.add((folder + "/ProxyPtDependence/pRingVsPtLeadP_PosEtaV0").c_str(), "Ring Vs PtLeadP, #eta_{V0}>0; p_{T}^{LeadP} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisJetPt}); - histos.add((folder + "/ProxyPtDependence/pRingVsPtLeadP_NegEtaV0").c_str(), "Ring Vs PtLeadP, #eta_{V0}<0; p_{T}^{LeadP} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisJetPt}); + histos.add((folder + "/ProxyPtDependence/pRingVsPtLeadP_PosEtaV0").c_str(), "<#it{R}> vs LeadP #it{p}_{T} (#eta_{V0}>0);#it{p}_{T}^{LeadP} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisJetPt}); + histos.add((folder + "/ProxyPtDependence/pRingVsPtLeadP_NegEtaV0").c_str(), "<#it{R}> vs LeadP #it{p}_{T} (#eta_{V0}<0);#it{p}_{T}^{LeadP} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisJetPt}); // Looking at V0Eta and JetEta combinations (only for LeadP): - histos.add((folder + "/ProxyPtDependence/pRingVsPtLeadP_PosEtaLeadP_PosEtaV0").c_str(), "Ring Vs PtLeadP, #eta_{LeadP}>0,#eta_{V0}>0; p_{T}^{LeadP} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisJetPt}); - histos.add((folder + "/ProxyPtDependence/pRingVsPtLeadP_NegEtaLeadP_PosEtaV0").c_str(), "Ring Vs PtLeadP, #eta_{LeadP}<0,#eta_{V0}>0; p_{T}^{LeadP} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisJetPt}); - histos.add((folder + "/ProxyPtDependence/pRingVsPtLeadP_PosEtaLeadP_NegEtaV0").c_str(), "Ring Vs PtLeadP, #eta_{LeadP}>0,#eta_{V0}<0; p_{T}^{LeadP} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisJetPt}); - histos.add((folder + "/ProxyPtDependence/pRingVsPtLeadP_NegEtaLeadP_NegEtaV0").c_str(), "Ring Vs PtLeadP, #eta_{LeadP}<0,#eta_{V0}<0; p_{T}^{LeadP} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisJetPt}); + histos.add((folder + "/ProxyPtDependence/pRingVsPtLeadP_PosEtaLeadP_PosEtaV0").c_str(), "<#it{R}> vs LeadP #it{p}_{T} (#eta_{LeadP}>0, #eta_{V0}>0);#it{p}_{T}^{LeadP} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisJetPt}); + histos.add((folder + "/ProxyPtDependence/pRingVsPtLeadP_NegEtaLeadP_PosEtaV0").c_str(), "<#it{R}> vs LeadP #it{p}_{T} (#eta_{LeadP}<0, #eta_{V0}>0);#it{p}_{T}^{LeadP} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisJetPt}); + histos.add((folder + "/ProxyPtDependence/pRingVsPtLeadP_PosEtaLeadP_NegEtaV0").c_str(), "<#it{R}> vs LeadP #it{p}_{T} (#eta_{LeadP}>0, #eta_{V0}<0);#it{p}_{T}^{LeadP} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisJetPt}); + histos.add((folder + "/ProxyPtDependence/pRingVsPtLeadP_NegEtaLeadP_NegEtaV0").c_str(), "<#it{R}> vs LeadP #it{p}_{T} (#eta_{LeadP}<0, #eta_{V0}<0);#it{p}_{T}^{LeadP} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisJetPt}); // Understanding eta dependence seen in pRingEtaCuts: - histos.add((folder + "/EtaDependence/pRingObservableEtaLambda").c_str(), "pRingObservableEtaLambda;#eta_{#Lambda};<#it{R}>", kTProfile, {axisConfigurations.axisEtaCoarse}); - histos.add((folder + "/EtaDependence/pRingObservableEtaJet").c_str(), "pRingObservableEtaJet;#eta_{Jet};<#it{R}>", kTProfile, {axisConfigurations.axisEtaCoarse}); + histos.add((folder + "/EtaDependence/pRingObservableEtaLambda").c_str(), "<#it{R}> vs #eta_{#Lambda};#eta_{#Lambda};<#it{R}>", kTProfile, {axisConfigurations.axisEtaCoarse}); + histos.add((folder + "/EtaDependence/pRingObservableEtaJet").c_str(), "<#it{R}> vs #eta_{Jet};#eta_{Jet};<#it{R}>", kTProfile, {axisConfigurations.axisEtaCoarse}); - histos.add((folder + "/EtaDependence/pRingObservableEtaLambda2ndJet").c_str(), "pRingObservableEtaLambda2ndJet;#eta_{#Lambda};<#it{R}>", kTProfile, {axisConfigurations.axisEtaCoarse}); - histos.add((folder + "/EtaDependence/pRingObservableEta2ndJet").c_str(), "pRingObservableEta2ndJet;#eta_{2ndJet};<#it{R}>", kTProfile, {axisConfigurations.axisEtaCoarse}); + histos.add((folder + "/EtaDependence/pRingObservableEtaLambda2ndJet").c_str(), "<#it{R}> vs #eta_{#Lambda} (SubJet);#eta_{#Lambda};<#it{R}>", kTProfile, {axisConfigurations.axisEtaCoarse}); + histos.add((folder + "/EtaDependence/pRingObservableEta2ndJet").c_str(), "<#it{R}> vs #eta_{SubJet};#eta_{SubJet};<#it{R}>", kTProfile, {axisConfigurations.axisEtaCoarse}); - histos.add((folder + "/EtaDependence/pRingObservableEtaLambdaLeadP").c_str(), "pRingObservableEtaLambdaLeadP;#eta_{#Lambda};<#it{R}>", kTProfile, {axisConfigurations.axisEtaCoarse}); - histos.add((folder + "/EtaDependence/pRingObservableEtaLeadP").c_str(), "pRingObservableEtaLeadP;#eta_{LeadP};<#it{R}>", kTProfile, {axisConfigurations.axisEtaCoarse}); + histos.add((folder + "/EtaDependence/pRingObservableEtaLambdaLeadP").c_str(), "<#it{R}> vs #eta_{#Lambda} (LeadP);#eta_{#Lambda};<#it{R}>", kTProfile, {axisConfigurations.axisEtaCoarse}); + histos.add((folder + "/EtaDependence/pRingObservableEtaLeadP").c_str(), "<#it{R}> vs #eta_{LeadP};#eta_{LeadP};<#it{R}>", kTProfile, {axisConfigurations.axisEtaCoarse}); // For the leading particle: - histos.add((folder + "/pRingObservableLeadPDeltaPhi").c_str(), "pRingObservableLeadPDeltaPhi;#Delta#varphi_{leadP};<#it{R}>", kTProfile, {axisConfigurations.axisDeltaPhi}); - histos.add((folder + "/pRingObservableLeadPDeltaTheta").c_str(), "pRingObservableLeadPDeltaTheta;#Delta#theta_{leadP};<#it{R}>", kTProfile, {axisConfigurations.axisDeltaTheta}); - histos.add((folder + "/pRingObservableLeadPIntegrated").c_str(), "pRingObservableLeadPIntegrated; ;<#it{R}>", kTProfile, {{1, -0.5, 0.5}}); - histos.add((folder + "/pRingObservableLeadPLambdaPt").c_str(), "pRingObservableLeadPLambdaPt;#it{p}_{T}^{#Lambda};<#it{R}>", kTProfile, {axisConfigurations.axisPt}); + histos.add((folder + "/pRingObservableLeadPDeltaPhi").c_str(), "<#it{R}> vs #Delta#varphi_{LeadP};#Delta#varphi_{LeadP};<#it{R}>", kTProfile, {axisConfigurations.axisDeltaPhi}); + histos.add((folder + "/pRingObservableLeadPDeltaTheta").c_str(), "<#it{R}> vs #Delta#theta_{LeadP};#Delta#theta_{LeadP};<#it{R}>", kTProfile, {axisConfigurations.axisDeltaTheta}); + histos.add((folder + "/pRingObservableLeadPIntegrated").c_str(), "Integrated <#it{R}> (LeadP); ;<#it{R}>", kTProfile, {{1, -0.5, 0.5}}); + histos.add((folder + "/pRingObservableLeadPLambdaPt").c_str(), "<#it{R}> vs #it{p}_{T}^{#Lambda} (LeadP);#it{p}_{T}^{#Lambda} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisPt}); // For the second-to-leading jet: - histos.add((folder + "/pRingObservable2ndJetDeltaPhi").c_str(), "pRingObservable2ndJetDeltaPhi;#Delta#varphi_{2ndJet};<#it{R}>", kTProfile, {axisConfigurations.axisDeltaPhi}); - histos.add((folder + "/pRingObservable2ndJetDeltaTheta").c_str(), "pRingObservable2ndJetDeltaTheta;#Delta#theta_{2ndJet};<#it{R}>", kTProfile, {axisConfigurations.axisDeltaTheta}); - histos.add((folder + "/pRingObservable2ndJetIntegrated").c_str(), "pRingObservable2ndJetIntegrated; ;<#it{R}>", kTProfile, {{1, -0.5, 0.5}}); - histos.add((folder + "/pRingObservable2ndJetLambdaPt").c_str(), "pRingObservable2ndJetLambdaPt;#it{p}_{T}^{#Lambda};<#it{R}>", kTProfile, {axisConfigurations.axisPt}); + histos.add((folder + "/pRingObservable2ndJetDeltaPhi").c_str(), "<#it{R}> vs #Delta#varphi_{SubJet};#Delta#varphi_{SubJet};<#it{R}>", kTProfile, {axisConfigurations.axisDeltaPhi}); + histos.add((folder + "/pRingObservable2ndJetDeltaTheta").c_str(), "<#it{R}> vs #Delta#theta_{SubJet};#Delta#theta_{SubJet};<#it{R}>", kTProfile, {axisConfigurations.axisDeltaTheta}); + histos.add((folder + "/pRingObservable2ndJetIntegrated").c_str(), "Integrated <#it{R}> (SubJet); ;<#it{R}>", kTProfile, {{1, -0.5, 0.5}}); + histos.add((folder + "/pRingObservable2ndJetLambdaPt").c_str(), "<#it{R}> vs #it{p}_{T}^{#Lambda} (SubJet);#it{p}_{T}^{#Lambda} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisPt}); // =============================== // 2D TProfiles (Lambda correlations) // =============================== - histos.add((folder + "/p2dRingObservableDeltaPhiVsLambdaPt").c_str(), "p2dRingObservableDeltaPhiVsLambdaPt;#Delta#varphi_{jet};#it{p}_{T}^{#Lambda};<#it{R}>", kTProfile2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisPt}); - histos.add((folder + "/p2dRingObservableDeltaThetaVsLambdaPt").c_str(), "p2dRingObservableDeltaThetaVsLambdaPt;#Delta#theta_{jet};#it{p}_{T}^{#Lambda};<#it{R}>", kTProfile2D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisPt}); + histos.add((folder + "/p2dRingObservableDeltaPhiVsLambdaPt").c_str(), "<#it{R}> vs #Delta#varphi_{jet} vs #it{p}_{T}^{#Lambda};#Delta#varphi_{jet};#it{p}_{T}^{#Lambda} (GeV/c);<#it{R}>", kTProfile2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisPt}); + histos.add((folder + "/p2dRingObservableDeltaThetaVsLambdaPt").c_str(), "<#it{R}> vs #Delta#theta_{jet} vs #it{p}_{T}^{#Lambda};#Delta#theta_{jet};#it{p}_{T}^{#Lambda} (GeV/c);<#it{R}>", kTProfile2D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisPt}); // =============================== // 2D TProfiles (Jet correlations) // =============================== - histos.add((folder + "/p2dRingObservableDeltaPhiVsLeadJetPt").c_str(), "p2dRingObservableDeltaPhiVsLeadJetPt;#Delta#varphi_{jet};#it{p}_{T}^{lead jet};<#it{R}>", kTProfile2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisJetPt}); - histos.add((folder + "/p2dRingObservableDeltaThetaVsLeadJetPt").c_str(), "p2dRingObservableDeltaThetaVsLeadJetPt;#Delta#theta_{jet};#it{p}_{T}^{lead jet};<#it{R}>", kTProfile2D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisJetPt}); + histos.add((folder + "/p2dRingObservableDeltaPhiVsLeadJetPt").c_str(), "<#it{R}> vs #Delta#varphi_{jet} vs Lead Jet #it{p}_{T};#Delta#varphi_{jet};#it{p}_{T}^{LeadJet} (GeV/c);<#it{R}>", kTProfile2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisJetPt}); + histos.add((folder + "/p2dRingObservableDeltaThetaVsLeadJetPt").c_str(), "<#it{R}> vs #Delta#theta_{jet} vs Lead Jet #it{p}_{T};#Delta#theta_{jet};#it{p}_{T}^{LeadJet} (GeV/c);<#it{R}>", kTProfile2D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisJetPt}); // =============================== // Multi-dimensional histograms for signal extraction // (Mass-dependent polarization extraction) // =============================== // Simple invariant mass plot for QA: - histos.add((folder + "/QA/hMass").c_str(), "hMass", kTH1D, {axisConfigurations.axisLambdaMass}); - histos.add((folder + "/hMassSigExtract").c_str(), "hMassSigExtract", kTH1D, {axisConfigurations.axisLambdaMassSigExtract}); + histos.add((folder + "/QA/hMass").c_str(), "#Lambda Mass;m_{p#pi} (GeV/c^{2});Counts", kTH1D, {axisConfigurations.axisLambdaMass}); + histos.add((folder + "/hMassSigExtract").c_str(), "#Lambda Mass (Sig Extract);m_{p#pi} (GeV/c^{2});Counts", kTH1D, {axisConfigurations.axisLambdaMassSigExtract}); // 1D Mass dependence of observable numerator: - histos.add((folder + "/QA/hRingObservableNumMass").c_str(), "hRingObservableNumMass", kTH1D, {axisConfigurations.axisLambdaMassSigExtract}); + histos.add((folder + "/QA/hRingObservableNumMass").c_str(), "Ring Observable Numerator vs Mass;m_{p#pi} (GeV/c^{2});Counts", kTH1D, {axisConfigurations.axisLambdaMassSigExtract}); // --- 2D counters: Angle vs Mass vs --- - histos.add((folder + "/QA/h2dDeltaPhiVsMass").c_str(), "h2dDeltaPhiVsMass", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract}); - histos.add((folder + "/QA/h2dDeltaThetaVsMass").c_str(), "h2dDeltaThetaVsMass", kTH2D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract}); + histos.add((folder + "/QA/h2dDeltaPhiVsMass").c_str(), "#Delta#varphi_{jet} vs Mass;#Delta#varphi_{jet};m_{p#pi} (GeV/c^{2})", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract}); + histos.add((folder + "/QA/h2dDeltaThetaVsMass").c_str(), "#Delta#theta_{jet} vs Mass;#Delta#theta_{jet};m_{p#pi} (GeV/c^{2})", kTH2D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract}); // --- 3D counters: Angle vs Mass vs Lambda pT --- - histos.add((folder + "/QA/h3dDeltaPhiVsMassVsLambdaPt").c_str(), "h3dDeltaPhiVsMassVsLambdaPt", kTH3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisPtSigExtract}); - histos.add((folder + "/QA/h3dDeltaThetaVsMassVsLambdaPt").c_str(), "h3dDeltaThetaVsMassVsLambdaPt", kTH3D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisPtSigExtract}); + histos.add((folder + "/QA/h3dDeltaPhiVsMassVsLambdaPt").c_str(), "#Delta#varphi_{jet} vs Mass vs #it{p}_{T}^{#Lambda};#Delta#varphi_{jet};m_{p#pi} (GeV/c^{2});#it{p}_{T}^{#Lambda} (GeV/c)", kTH3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisPtSigExtract}); + histos.add((folder + "/QA/h3dDeltaThetaVsMassVsLambdaPt").c_str(), "#Delta#theta_{jet} vs Mass vs #it{p}_{T}^{#Lambda};#Delta#theta_{jet};m_{p#pi} (GeV/c^{2});#it{p}_{T}^{#Lambda} (GeV/c)", kTH3D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisPtSigExtract}); // --- 3D counters: Angle vs Mass vs Lead Jet pT --- - histos.add((folder + "/QA/h3dDeltaPhiVsMassVsLeadJetPt").c_str(), "h3dDeltaPhiVsMassVsLeadJetPt", kTH3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisJetPtSigExtract}); - histos.add((folder + "/QA/h3dDeltaThetaVsMassVsLeadJetPt").c_str(), "h3dDeltaThetaVsMassVsLeadJetPt", kTH3D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisJetPtSigExtract}); + histos.add((folder + "/QA/h3dDeltaPhiVsMassVsLeadJetPt").c_str(), "#Delta#varphi_{jet} vs Mass vs Lead Jet #it{p}_{T};#Delta#varphi_{jet};m_{p#pi} (GeV/c^{2});#it{p}_{T}^{LeadJet} (GeV/c)", kTH3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisJetPtSigExtract}); + histos.add((folder + "/QA/h3dDeltaThetaVsMassVsLeadJetPt").c_str(), "#Delta#theta_{jet} vs Mass vs Lead Jet #it{p}_{T};#Delta#theta_{jet};m_{p#pi} (GeV/c^{2});#it{p}_{T}^{LeadJet} (GeV/c)", kTH3D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisJetPtSigExtract}); // =============================== // TProfiles vs Mass: quick glancing before signal extraction // =============================== // TProfile of ring vs mass (integrated in all phi, and properly normalized by N_\Lambda): - histos.add((folder + "/pRingObservableMass").c_str(), "pRingObservableMass;m_{p#pi};<#it{R}>", kTProfile, {axisConfigurations.axisLambdaMassSigExtract}); - histos.add((folder + "/pRingObservableLeadPMass").c_str(), "pRingObservableLeadPMass;m_{p#pi};<#it{R}>", kTProfile, {axisConfigurations.axisLambdaMassSigExtract}); - histos.add((folder + "/pRingObservable2ndJetMass").c_str(), "pRingObservable2ndJetMass;m_{p#pi};<#it{R}>", kTProfile, {axisConfigurations.axisLambdaMassSigExtract}); + histos.add((folder + "/pRingObservableMass").c_str(), "<#it{R}> vs Mass;m_{p#pi} (GeV/c^{2});<#it{R}>", kTProfile, {axisConfigurations.axisLambdaMassSigExtract}); + histos.add((folder + "/pRingObservableLeadPMass").c_str(), "<#it{R}> vs Mass (LeadP);m_{p#pi} (GeV/c^{2});<#it{R}>", kTProfile, {axisConfigurations.axisLambdaMassSigExtract}); + histos.add((folder + "/pRingObservable2ndJetMass").c_str(), "<#it{R}> vs Mass (SubJet);m_{p#pi} (GeV/c^{2});<#it{R}>", kTProfile, {axisConfigurations.axisLambdaMassSigExtract}); // TProfile2D: vs Mass (DeltaPhi) - histos.add((folder + "/p2dRingObservableDeltaPhiVsMass").c_str(), "p2dRingObservableDeltaPhiVsMass;#Delta#varphi;m_{p#pi};<#it{R}>", kTProfile2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract}); + histos.add((folder + "/p2dRingObservableDeltaPhiVsMass").c_str(), "<#it{R}> vs #Delta#varphi_{jet} vs Mass;#Delta#varphi_{jet};m_{p#pi} (GeV/c^{2});<#it{R}>", kTProfile2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract}); // TProfile2D: vs Mass (DeltaTheta) - histos.add((folder + "/p2dRingObservableDeltaThetaVsMass").c_str(), "p2dRingObservableDeltaThetaVsMass;#Delta#theta;m_{p#pi};<#it{R}>", kTProfile2D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract}); + histos.add((folder + "/p2dRingObservableDeltaThetaVsMass").c_str(), "<#it{R}> vs #Delta#theta_{jet} vs Mass;#Delta#theta_{jet};m_{p#pi} (GeV/c^{2});<#it{R}>", kTProfile2D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract}); // TProfile2D: vs Eta Lambda vs Eta Jet (Understanding eta dependence seen in pRingEtaCuts) - histos.add((folder + "/p2dRingObservableEtaLambdaVsEtaJet").c_str(), "p2dRingObservableEtaLambdaVsEtaJet;#eta_{#Lambda};#eta_{Jet};<#it{R}>", kTProfile2D, {axisConfigurations.axisEtaCoarse, axisConfigurations.axisEtaCoarse}); - histos.add((folder + "/p2dRingObservableEtaLambdaVsEtaLeadP").c_str(), "p2dRingObservableEtaLambdaVsEtaLeadP;#eta_{#Lambda};#eta_{LeadP};<#it{R}>", kTProfile2D, {axisConfigurations.axisEtaCoarse, axisConfigurations.axisEtaCoarse}); - histos.add((folder + "/p2dRingObservableEtaLambdaVsEta2ndJet").c_str(), "p2dRingObservableEtaLambdaVsEta2ndJet;#eta_{#Lambda};#eta_{2ndJet};<#it{R}>", kTProfile2D, {axisConfigurations.axisEtaCoarse, axisConfigurations.axisEtaCoarse}); + histos.add((folder + "/EtaDependence/p2dRingObservableEtaLambdaVsEtaJet").c_str(), "<#it{R}> vs #eta_{#Lambda} vs #eta_{Jet};#eta_{#Lambda};#eta_{Jet};<#it{R}>", kTProfile2D, {axisConfigurations.axisEtaCoarse, axisConfigurations.axisEtaCoarse}); + histos.add((folder + "/EtaDependence/p2dRingObservableEtaLambdaVsEtaJet_FineBins").c_str(), "<#it{R}> vs #eta_{#Lambda} vs #eta_{Jet} (fine bins);#eta_{#Lambda};#eta_{Jet};<#it{R}>", kTProfile2D, {axisConfigurations.axisEta, axisConfigurations.axisEta}); + histos.add((folder + "/EtaDependence/p2dRingObservableEtaLambdaVsEtaLeadP").c_str(), "<#it{R}> vs #eta_{#Lambda} vs #eta_{LeadP};#eta_{#Lambda};#eta_{LeadP};<#it{R}>", kTProfile2D, {axisConfigurations.axisEtaCoarse, axisConfigurations.axisEtaCoarse}); + histos.add((folder + "/EtaDependence/p2dRingObservableEtaLambdaVsEta2ndJet").c_str(), "<#it{R}> vs #eta_{#Lambda} vs #eta_{SubJet};#eta_{#Lambda};#eta_{SubJet};<#it{R}>", kTProfile2D, {axisConfigurations.axisEtaCoarse, axisConfigurations.axisEtaCoarse}); + // Counters for these histograms, instead of only TProfile2Ds: + histos.add((folder + "/EtaDependence/h2dCounterEtaLambdaVsEtaJet").c_str(), "Counts, #eta_{#Lambda} vs #eta_{Jet};#eta_{#Lambda};#eta_{Jet};Counts", kTH2D, {axisConfigurations.axisEtaCoarse, axisConfigurations.axisEtaCoarse}); + histos.add((folder + "/EtaDependence/h2dCounterEtaLambdaVsEtaJet_FineBins").c_str(), "Counts (fine bins), #eta_{#Lambda} vs #eta_{Jet};#eta_{#Lambda};#eta_{Jet};Counts", kTProfile2D, {axisConfigurations.axisEta, axisConfigurations.axisEta}); + histos.add((folder + "/EtaDependence/h2dCounterEtaLambdaVsEtaLeadP").c_str(), "Counts, #eta_{#Lambda} vs #eta_{LeadP};#eta_{#Lambda};#eta_{LeadP};Counts", kTH2D, {axisConfigurations.axisEtaCoarse, axisConfigurations.axisEtaCoarse}); + histos.add((folder + "/EtaDependence/h2dCounterEtaLambdaVsEta2ndJet").c_str(), "Counts, #eta_{#Lambda} vs #eta_{SubJet};#eta_{#Lambda};#eta_{SubJet};Counts", kTH2D, {axisConfigurations.axisEtaCoarse, axisConfigurations.axisEtaCoarse}); // --- TProfile3D: vs DeltaPhi vs Mass vs LambdaPt --- - histos.add((folder + "/p3dRingObservableDeltaPhiVsMassVsLambdaPt").c_str(), "p3dRingObservableDeltaPhiVsMassVsLambdaPt;#Delta#varphi;m_{p#pi};p_{T}^{#Lambda};<#it{R}>", kTProfile3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisPtSigExtract}); + histos.add((folder + "/p3dRingObservableDeltaPhiVsMassVsLambdaPt").c_str(), "<#it{R}> vs #Delta#varphi_{jet} vs Mass vs #it{p}_{T}^{#Lambda};#Delta#varphi_{jet};m_{p#pi} (GeV/c^{2});#it{p}_{T}^{#Lambda} (GeV/c)", kTProfile3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisPtSigExtract}); // --- TProfile3D: vs DeltaTheta vs Mass vs LambdaPt --- - histos.add((folder + "/p3dRingObservableDeltaThetaVsMassVsLambdaPt").c_str(), "p3dRingObservableDeltaThetaVsMassVsLambdaPt;#Delta#theta;m_{p#pi};p_{T}^{#Lambda};<#it{R}>", kTProfile3D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisPtSigExtract}); + histos.add((folder + "/p3dRingObservableDeltaThetaVsMassVsLambdaPt").c_str(), "<#it{R}> vs #Delta#theta_{jet} vs Mass vs #it{p}_{T}^{#Lambda};#Delta#theta_{jet};m_{p#pi} (GeV/c^{2});#it{p}_{T}^{#Lambda} (GeV/c)", kTProfile3D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisPtSigExtract}); // --- TProfile3D: vs DeltaPhi vs Mass vs LeadJetPt --- - histos.add((folder + "/p3dRingObservableDeltaPhiVsMassVsLeadJetPt").c_str(), "p3dRingObservableDeltaPhiVsMassVsLeadJetPt;#Delta#varphi;m_{p#pi};p_{T}^{jet};<#it{R}>", kTProfile3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisJetPtSigExtract}); + histos.add((folder + "/p3dRingObservableDeltaPhiVsMassVsLeadJetPt").c_str(), "<#it{R}> vs #Delta#varphi_{jet} vs Mass vs Lead Jet #it{p}_{T};#Delta#varphi_{jet};m_{p#pi} (GeV/c^{2});#it{p}_{T}^{LeadJet} (GeV/c)", kTProfile3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisJetPtSigExtract}); // --- TProfile3D: vs DeltaTheta vs Mass vs LeadJetPt --- - histos.add((folder + "/p3dRingObservableDeltaThetaVsMassVsLeadJetPt").c_str(), "p3dRingObservableDeltaThetaVsMassVsLeadJetPt;#Delta#theta;m_{p#pi};p_{T}^{jet};<#it{R}>", kTProfile3D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisJetPtSigExtract}); + histos.add((folder + "/p3dRingObservableDeltaThetaVsMassVsLeadJetPt").c_str(), "<#it{R}> vs #Delta#theta_{jet} vs Mass vs Lead Jet #it{p}_{T};#Delta#theta_{jet};m_{p#pi} (GeV/c^{2});#it{p}_{T}^{LeadJet} (GeV/c)", kTProfile3D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisJetPtSigExtract}); // =============================== // Mass histograms with centrality // =============================== // Counters - histos.add((folder + "/QA/h3dDeltaPhiVsMassVsCent").c_str(), "h3dDeltaPhiVsMassVsCent", kTH3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisCentrality}); - histos.add((folder + "/QA/h3dDeltaThetaVsMassVsCent").c_str(), "h3dDeltaThetaVsMassVsCent", kTH3D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisCentrality}); + histos.add((folder + "/QA/h3dDeltaPhiVsMassVsCent").c_str(), "#Delta#varphi_{jet} vs Mass vs Centrality;#Delta#varphi_{jet};m_{p#pi} (GeV/c^{2});Centrality (%)", kTH3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisCentrality}); + histos.add((folder + "/QA/h3dDeltaThetaVsMassVsCent").c_str(), "#Delta#theta_{jet} vs Mass vs Centrality;#Delta#theta_{jet};m_{p#pi} (GeV/c^{2});Centrality (%)", kTH3D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisCentrality}); // Useful TProfiles: // --- TProfile1D: Integrated vs Centrality: - histos.add((folder + "/pRingVsCentrality").c_str(), "pRingVsCentrality; Centrality (%);<#it{R}>", kTProfile, {axisConfigurations.axisCentrality}); + histos.add((folder + "/pRingVsCentrality").c_str(), "<#it{R}> vs Centrality;Centrality (%);<#it{R}>", kTProfile, {axisConfigurations.axisCentrality}); // --- TProfile2D: vs Mass vs Centrality --- - histos.add((folder + "/p2dRingObservableMassVsCent").c_str(), "p2dRingObservableMassVsCent;m_{p#pi};Centrality;<#it{R}>", kTProfile2D, {axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisCentrality}); + histos.add((folder + "/p2dRingObservableMassVsCent").c_str(), "<#it{R}> vs Mass vs Centrality;m_{p#pi} (GeV/c^{2});Centrality (%);<#it{R}>", kTProfile2D, {axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisCentrality}); // --- TProfile3D: vs DeltaPhi vs Mass vs Centrality --- - histos.add((folder + "/p3dRingObservableDeltaPhiVsMassVsCent").c_str(), "p3dRingObservableDeltaPhiVsMassVsCent;#Delta#varphi;m_{p#pi};Centrality;<#it{R}>", kTProfile3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisCentrality}); + histos.add((folder + "/p3dRingObservableDeltaPhiVsMassVsCent").c_str(), "<#it{R}> vs #Delta#varphi_{jet} vs Mass vs Centrality;#Delta#varphi_{jet};m_{p#pi} (GeV/c^{2});Centrality (%)", kTProfile3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisCentrality}); // --- TProfile3D: vs DeltaTheta vs Mass vs Centrality --- - histos.add((folder + "/p3dRingObservableDeltaThetaVsMassVsCent").c_str(), "p3dRingObservableDeltaThetaVsMassVsCent;#Delta#theta;m_{p#pi};Centrality;<#it{R}>", kTProfile3D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisCentrality}); + histos.add((folder + "/p3dRingObservableDeltaThetaVsMassVsCent").c_str(), "<#it{R}> vs #Delta#theta_{jet} vs Mass vs Centrality;#Delta#theta_{jet};m_{p#pi} (GeV/c^{2});Centrality (%)", kTProfile3D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisCentrality}); // =============================== // QA histograms - Useful numbers @@ -585,12 +597,12 @@ struct lambdajetpolarizationionsderived { // Estimating error bars with the Delta Method for = A/B: // 1D Delta Method for Integrated observable: - histos.add((folder + "/DeltaMethod/hIntegrated").c_str(), "Delta Method Accumulators Integrated", kTH1D, {axisConfigurations.axisDeltaComponents}); + histos.add((folder + "/DeltaMethod/hIntegrated").c_str(), "Delta Method Accumulators Integrated;Component;Counts", kTH1D, {axisConfigurations.axisDeltaComponents}); // 2D Delta Method for Differentials - histos.add((folder + "/DeltaMethod/h2dDeltaThetaVsDeltaComp").c_str(), "Delta Method vs DeltaTheta", kTH2D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisDeltaComponents}); - histos.add((folder + "/DeltaMethod/h2dLambdaPtVsDeltaComp").c_str(), "Delta Method vs Lambda pT", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisDeltaComponents}); - histos.add((folder + "/DeltaMethod/h2dMassVsDeltaComp").c_str(), "Delta Method vs Mass", kTH2D, {axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisDeltaComponents}); + histos.add((folder + "/DeltaMethod/h2dDeltaThetaVsDeltaComp").c_str(), "Delta Method vs #Delta#theta_{jet};#Delta#theta_{jet};Component", kTH2D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisDeltaComponents}); + histos.add((folder + "/DeltaMethod/h2dLambdaPtVsDeltaComp").c_str(), "Delta Method vs #Lambda #it{p}_{T};#it{p}_{T}^{#Lambda} (GeV/c);Component", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisDeltaComponents}); + histos.add((folder + "/DeltaMethod/h2dMassVsDeltaComp").c_str(), "Delta Method vs Mass;m_{p#pi} (GeV/c^{2});Component", kTH2D, {axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisDeltaComponents}); }; // Execute local lambda to register histogram families: addRingObservableFamily("Ring"); @@ -700,6 +712,28 @@ struct lambdajetpolarizationionsderived { histos.get(HIST("HelicityEfficiencyQA/hFakePolCounts"))->GetYaxis()->SetBinLabel(8, "#eta_{Jet} < 0, #eta_{#Lambda} #geq 0"); histos.get(HIST("HelicityEfficiencyQA/hFakePolCounts"))->GetYaxis()->SetBinLabel(9, "#eta_{Jet} < 0, #eta_{#Lambda} < 0"); + // The same, but for actual signal instead of counts: + histos.add("HelicityEfficiencyQA/pFakePolSignalVsCosTheta", "FakePolSignal; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda};", kTProfile2D, {axisConfigurations.axisCosTheta, {9, 0, 9}}); + histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalVsCosTheta"))->GetZaxis()->SetTitle(""); + histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalVsCosTheta"))->GetYaxis()->SetBinLabel(1, "All #Lambda"); + histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalVsCosTheta"))->GetYaxis()->SetBinLabel(2, "#eta_{Jet} #geq 0"); + histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalVsCosTheta"))->GetYaxis()->SetBinLabel(3, "#eta_{Jet} < 0"); + histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalVsCosTheta"))->GetYaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); + histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalVsCosTheta"))->GetYaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); + histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalVsCosTheta"))->GetYaxis()->SetBinLabel(6, "#eta_{Jet} #geq 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalVsCosTheta"))->GetYaxis()->SetBinLabel(7, "#eta_{Jet} #geq 0, #eta_{#Lambda} < 0"); + histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalVsCosTheta"))->GetYaxis()->SetBinLabel(8, "#eta_{Jet} < 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalVsCosTheta"))->GetYaxis()->SetBinLabel(9, "#eta_{Jet} < 0, #eta_{#Lambda} < 0"); + + // Seeing the dependence between phi* = atan2(p_p_star \cdot (p_Lambda_hat \times (z_hat \cross p_Lambda)), p_p_star \cdot (z_hat \cross p_Lambda)) + // e_z = p_Lambda_hat; // e_x = normalize(z_hat cross p_Lambda); // e_y = e_z cross e_x; + // phi_star = atan2(p_p_star dot e_y, p_p_star dot e_x); + histos.add("HelicityEfficiencyQA/hFakePolCounts_CosThetaVsPhiStar", "FakePolCounts; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; #phi^*", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisDeltaPhi}); + histos.add("HelicityEfficiencyQA/pFakePolSignal_CosThetaVsPhiStar", "FakePolSignal; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; #phi^*", kTProfile2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisDeltaPhi}); + // Specific counter for when we have leading jets (relates directly to pFakePolSignal_CosThetaVsPhiStar): + histos.add("HelicityEfficiencyQA/hFakePolCountsJet_CosThetaVsPhiStar", "FakePolCounts - HasValidLeadJet OK; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; #phi^*", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisDeltaPhi}); + + // Doing the same for leading particles: // (eta_{Jet} may be a bad estimator!) histos.add("HelicityEfficiencyQA/hFakePolCountsLeadP", "FakePolCounts; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda};", kTH2D, {axisConfigurations.axisCosTheta, {9, 0, 9}}); @@ -761,6 +795,13 @@ struct lambdajetpolarizationionsderived { histos.add("HelicityEfficiencyQA/hAntiLambdaMassDecayGeomLeft", "hAntiLambdaMassDecayGeomLeft; m_{Inv}; Counts", kTH1D, {axisConfigurations.axisLambdaMass}); } + // Also including an observable that probes spectrum broadening due to the "Azimuthal Efficiency Effect": + histos.add("HelicityEfficiencyQA/hLambdaMassVsPhiLambdaMinusPhiProtonStar", "m_{#Lambda}, AEE probe; m_{Inv}; #phi_{#Lambda} - #phi_{p}^{*} ; Counts", kTH2D, {axisConfigurations.axisLambdaMass, axisConfigurations.axisDeltaPhi}); + histos.add("HelicityEfficiencyQA/hAntiLambdaMassVsPhiLambdaMinusPhiProtonStar", "m_{#bar{#Lambda}}, AEE probe; m_{Inv}; #phi_{#bar{#Lambda}} - #phi_{p}^{*} ; Counts", kTH2D, {axisConfigurations.axisLambdaMass, axisConfigurations.axisDeltaPhi}); + // Watching the effect on the ring observable as well: + histos.add("HelicityEfficiencyQA/p2dRing_LambdaMassVsPhiLambdaMinusPhiProtonStar", "<#it{R}>, AEE probe; m_{Inv}; #phi_{#Lambda} - #phi_{p}^{*} ; <#it{R}>", kTProfile2D, {axisConfigurations.axisLambdaMass, axisConfigurations.axisDeltaPhi}); + histos.add("HelicityEfficiencyQA/p2dRing_AntiLambdaMassVsPhiLambdaMinusPhiProtonStar", "<#it{R}>, AEE probe; m_{Inv}; #phi_{#bar{#Lambda}} - #phi_{p}^{*} ; <#it{R}>", kTProfile2D, {axisConfigurations.axisLambdaMass, axisConfigurations.axisDeltaPhi}); + // Integrated observable for events with NLambda+NAntiLambda V0s per event // (an interesting measurement of correlation between and Lambda-like V0s multiplicity. A proxy of covariance) // (calculated for leading jets only) @@ -1086,6 +1127,24 @@ struct lambdajetpolarizationionsderived { // (this estimator is calculated outside of any gate, as it does not depend on jet proxy used) float cosFakePol = protonLikeStarUnit3Vec.Dot(lambdaLikeUnit3Vec); + // Calculating the azimuthal angle between the Lambda and the proton: + float deltaPhiLambdaProtonStar = wrapToPiFast(lambdaLikeUnit3Vec.Phi() - protonLikeStarUnit3Vec.Phi()); // Phi is defined from -PI to PI in ROOT::Math::Cartesian3D, thus kept the wrapping + + // Calculating the phi* angle: + // e_z = p_Lambda_hat; // e_x = normalize(z_hat cross p_Lambda); // e_y = e_z cross e_x; + // // phi_star = atan2(p_p_star dot e_y, p_p_star dot e_x); + // XYZVector e_x(-lambdaLikeUnit3Vec.Y(), lambdaLikeUnit3Vec.X(), 0.); // Same as e_x = zHat.Cross(lambdaLikeUnit3Vec); + // XYZVector e_y = lambdaLikeUnit3Vec.Cross(e_x); // e_y completes the right-handed coordinate system (e_z is lambdaLikeUnit3Vec) + // float pX = protonLikeStarUnit3Vec.Dot(e_x); + // float pY = protonLikeStarUnit3Vec.Dot(e_y); + // float phiStar = std::atan2(pY, pX); + // Faster implementation: + // pX = p_y * L_x - p_x * L_y + float pX = protonLikeStarUnit3Vec.Y() * lambdaLikeUnit3Vec.X() - protonLikeStarUnit3Vec.X() * lambdaLikeUnit3Vec.Y(); + // pY = p_z - L_z * (p_proton_star dot p_lambda_hat) + float pY = protonLikeStarUnit3Vec.Z() - lambdaLikeUnit3Vec.Z() * cosFakePol; // (Reusing cosFakePol calculated earlier!) + float phiStar = std::atan2(pY, pX); + // Another reconstruction efficiency measure: // (Formula is: p_{Lambda} \cross p_{Daughter}^{*} \cdot B, and B points in Z) if (analyseMagField) { @@ -1102,6 +1161,14 @@ struct lambdajetpolarizationionsderived { histos.fill(HIST("HelicityEfficiencyQA/hAntiLambdaMassDecayGeomLeft"), v0LambdaLikeMass); } + // Measuring the AEE effect differentially (azimuthal efficiency effect, which causes different V0 topologies to be enhanced/suppressed): + if (isLambda) + histos.fill(HIST("HelicityEfficiencyQA/hLambdaMassVsPhiLambdaMinusPhiProtonStar"), v0LambdaLikeMass, deltaPhiLambdaProtonStar); + else + histos.fill(HIST("HelicityEfficiencyQA/hAntiLambdaMassVsPhiLambdaMinusPhiProtonStar"), v0LambdaLikeMass, deltaPhiLambdaProtonStar); + // AEE and HEE correlation: + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCounts_CosThetaVsPhiStar"), cosFakePol, phiStar); + // Useful kinematic bools: const bool lambdaEtaPos = v0eta >= 0.; const bool pTLambdaCheck = v0pt > 0.5 && v0pt < 1.5; @@ -1220,6 +1287,15 @@ struct lambdajetpolarizationionsderived { binMass = mAxisMass->FindBin(v0LambdaLikeMass); binDTheta = mAxisDTheta->FindBin(deltaThetaJet); trackRing.addV0(ringObservable, binPt, binMass, binDTheta); + + // Measuring the AEE differentially (azimuthal efficiency effect, which causes different V0 topologies to be enhanced/suppressed): + if (isLambda) + histos.fill(HIST("HelicityEfficiencyQA/p2dRing_LambdaMassVsPhiLambdaMinusPhiProtonStar"), v0LambdaLikeMass, deltaPhiLambdaProtonStar, ringObservable); + else + histos.fill(HIST("HelicityEfficiencyQA/p2dRing_AntiLambdaMassVsPhiLambdaMinusPhiProtonStar"), v0LambdaLikeMass, deltaPhiLambdaProtonStar, ringObservable); + // AEE and HEE correlation: + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsJet_CosThetaVsPhiStar"), cosFakePol, phiStar); + histos.fill(HIST("HelicityEfficiencyQA/hFakePolSignal_CosThetaVsPhiStar"), cosFakePol, phiStar, ringObservable); } if (hasValidSubJet) { RING_OBSERVABLE_2NDJET_FILL_LIST(APPLY_HISTO_FILL, "Ring") @@ -1234,6 +1310,9 @@ struct lambdajetpolarizationionsderived { histos.fill(HIST("HelicityEfficiencyQA/hFakePolCounts"), cosFakePol, 0); histos.fill(HIST("HelicityEfficiencyQA/hFakePolCounts"), cosFakePol, lambdaEtaPos ? 3 : 4); + // Same for signal: + histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalVsCosTheta"), cosFakePol, 0, ringObservable); + histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalVsCosTheta"), cosFakePol, lambdaEtaPos ? 3 : 4, ringObservable); // Extra correlations test: histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaJet"), cosFakePol, deltaThetaJet); @@ -1253,6 +1332,9 @@ struct lambdajetpolarizationionsderived { histos.fill(HIST("HelicityEfficiencyQA/hFakePolCounts"), cosFakePol, 1); histos.fill(HIST("HelicityEfficiencyQA/hFakePolCounts"), cosFakePol, lambdaEtaPos ? 5 : 6); + // Same for signal: + histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalVsCosTheta"), cosFakePol, 1, ringObservable); + histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalVsCosTheta"), cosFakePol, lambdaEtaPos ? 5 : 6, ringObservable); histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaJetPosEta"), cosFakePol, deltaThetaJet); if (pTLambdaCheck) { @@ -1273,6 +1355,9 @@ struct lambdajetpolarizationionsderived { histos.fill(HIST("HelicityEfficiencyQA/hFakePolCounts"), cosFakePol, 2); histos.fill(HIST("HelicityEfficiencyQA/hFakePolCounts"), cosFakePol, lambdaEtaPos ? 7 : 8); + // Same for signal: + histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalVsCosTheta"), cosFakePol, 2, ringObservable); + histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalVsCosTheta"), cosFakePol, lambdaEtaPos ? 7 : 8, ringObservable); histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaJetNegEta"), cosFakePol, deltaThetaJet); From befd5348e9d22fdee0e8b63f9ff7fd139ec2597c Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Fri, 1 May 2026 02:07:15 +0000 Subject: [PATCH 09/31] Small fixes to histogram names to fix runtime errors --- PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx index e2eea756acc..99027ba1c75 100644 --- a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx @@ -554,7 +554,7 @@ struct lambdajetpolarizationionsderived { histos.add((folder + "/EtaDependence/p2dRingObservableEtaLambdaVsEta2ndJet").c_str(), "<#it{R}> vs #eta_{#Lambda} vs #eta_{SubJet};#eta_{#Lambda};#eta_{SubJet};<#it{R}>", kTProfile2D, {axisConfigurations.axisEtaCoarse, axisConfigurations.axisEtaCoarse}); // Counters for these histograms, instead of only TProfile2Ds: histos.add((folder + "/EtaDependence/h2dCounterEtaLambdaVsEtaJet").c_str(), "Counts, #eta_{#Lambda} vs #eta_{Jet};#eta_{#Lambda};#eta_{Jet};Counts", kTH2D, {axisConfigurations.axisEtaCoarse, axisConfigurations.axisEtaCoarse}); - histos.add((folder + "/EtaDependence/h2dCounterEtaLambdaVsEtaJet_FineBins").c_str(), "Counts (fine bins), #eta_{#Lambda} vs #eta_{Jet};#eta_{#Lambda};#eta_{Jet};Counts", kTProfile2D, {axisConfigurations.axisEta, axisConfigurations.axisEta}); + histos.add((folder + "/EtaDependence/h2dCounterEtaLambdaVsEtaJet_FineBins").c_str(), "Counts (fine bins), #eta_{#Lambda} vs #eta_{Jet};#eta_{#Lambda};#eta_{Jet};Counts", kTH2D, {axisConfigurations.axisEta, axisConfigurations.axisEta}); histos.add((folder + "/EtaDependence/h2dCounterEtaLambdaVsEtaLeadP").c_str(), "Counts, #eta_{#Lambda} vs #eta_{LeadP};#eta_{#Lambda};#eta_{LeadP};Counts", kTH2D, {axisConfigurations.axisEtaCoarse, axisConfigurations.axisEtaCoarse}); histos.add((folder + "/EtaDependence/h2dCounterEtaLambdaVsEta2ndJet").c_str(), "Counts, #eta_{#Lambda} vs #eta_{SubJet};#eta_{#Lambda};#eta_{SubJet};Counts", kTH2D, {axisConfigurations.axisEtaCoarse, axisConfigurations.axisEtaCoarse}); // --- TProfile3D: vs DeltaPhi vs Mass vs LambdaPt --- @@ -1292,7 +1292,7 @@ struct lambdajetpolarizationionsderived { histos.fill(HIST("HelicityEfficiencyQA/p2dRing_AntiLambdaMassVsPhiLambdaMinusPhiProtonStar"), v0LambdaLikeMass, deltaPhiLambdaProtonStar, ringObservable); // AEE and HEE correlation: histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsJet_CosThetaVsPhiStar"), cosFakePol, phiStar); - histos.fill(HIST("HelicityEfficiencyQA/hFakePolSignal_CosThetaVsPhiStar"), cosFakePol, phiStar, ringObservable); + histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignal_CosThetaVsPhiStar"), cosFakePol, phiStar, ringObservable); } if (hasValidSubJet) { RING_OBSERVABLE_2NDJET_FILL_LIST(APPLY_HISTO_FILL, "Ring") From f342456eadc019966d652dc248cd6c226ceba47a Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Wed, 6 May 2026 17:51:49 +0000 Subject: [PATCH 10/31] Implementing QA plots to understand (and find a way to minimize/remove the effects of) the "Azimuthal Efficiency Effect" (AEE), which is the main source of fake polarization signal -- caused by reconstruction efficiency --- .../lambdaJetPolarizationIonsDerived.cxx | 137 ++++++++++++++++-- 1 file changed, 125 insertions(+), 12 deletions(-) diff --git a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx index 99027ba1c75..4702c5e78dc 100644 --- a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx @@ -80,6 +80,7 @@ enum CentEstimator { #define RING_OBSERVABLE_FILL_LIST(X, FOLDER) \ /* Counters */ \ X(FOLDER "/QA/hDeltaPhi", deltaPhiJet) \ + X(FOLDER "/QA/hDeltaPhiVsDeltaEta", deltaPhiJet, deltaEtaJet) \ X(FOLDER "/QA/hDeltaTheta", deltaThetaJet) \ X(FOLDER "/QA/hCosDeltaTheta", cosDeltaThetaJet) \ X(FOLDER "/QA/hIntegrated", 0.) \ @@ -91,6 +92,8 @@ enum CentEstimator { X(FOLDER "/QA/h2dDeltaThetaVsLambdaPt", deltaThetaJet, v0pt) \ /* Additional plots for instant gratification - 1D Profiles */ \ X(FOLDER "/pRingObservableDeltaPhi", deltaPhiJet, ringObservable) \ + X(FOLDER "/pRingObservablePhiJet", leadingJetPhi, ringObservable) \ + X(FOLDER "/pRingObservablePhiLambda", v0phi, ringObservable) \ X(FOLDER "/pRingObservableDeltaTheta", deltaThetaJet, ringObservable) \ X(FOLDER "/EtaDependence/pRingObservableEtaLambda", v0eta, ringObservable) \ X(FOLDER "/EtaDependence/pRingObservableEtaJet", leadingJetEta, ringObservable) \ @@ -126,6 +129,8 @@ enum CentEstimator { X(FOLDER "/p2dRingObservableDeltaPhiVsMass", deltaPhiJet, v0LambdaLikeMass, ringObservable) \ X(FOLDER "/p2dRingObservableDeltaThetaVsMass", deltaThetaJet, v0LambdaLikeMass, ringObservable) \ /* 2D Profile: Ring vs Eta variables */ \ + X(FOLDER "/EtaDependence/hCounterEtaLambdaMinusEtaJet", v0eta - leadingJetEta) \ + X(FOLDER "/EtaDependence/pRingObservableEtaLambdaMinusEtaJet", v0eta - leadingJetEta, ringObservable) \ X(FOLDER "/EtaDependence/p2dRingObservableEtaLambdaVsEtaJet", v0eta, leadingJetEta, ringObservable) \ X(FOLDER "/EtaDependence/h2dCounterEtaLambdaVsEtaJet", v0eta, leadingJetEta) \ X(FOLDER "/EtaDependence/p2dRingObservableEtaLambdaVsEtaJet_FineBins", v0eta, leadingJetEta, ringObservable) \ @@ -329,8 +334,10 @@ struct lambdajetpolarizationionsderived { ConfigurableAxis axisJetPtSigExtract{"axisJetPtSigExtract", {VARIABLE_WIDTH, 0, 5, 10, 12, 16, 20, 25, 30, 35, 40, 60, 100, 200}, "Jet p_{t} (GeV)"}; ConfigurableAxis axisEta{"axisEta", {50, -1.0f, 1.0f}, "#eta"}; ConfigurableAxis axisEtaCoarse{"axisEtaCoarse", {20, -0.9f, 0.9f}, "#eta coarse axis"}; + ConfigurableAxis axisDeltaEtaCoarse{"axisDeltaEtaCoarse", {40, -1.8f, 1.8f}, "#Delta#eta coarse axis"}; ConfigurableAxis axisDeltaTheta{"axisDeltaTheta", {40, 0, constants::math::PI}, "#Delta #theta_{jet}"}; ConfigurableAxis axisCosTheta{"axisCosTheta", {50, -1, 1}, "cos(#theta)"}; + ConfigurableAxis axisPhi{"axisPhi", {40, 0., constants::math::TwoPI}, "#varphi"}; ConfigurableAxis axisDeltaPhi{"axisDeltaPhi", {40, -constants::math::PI, constants::math::PI}, "#Delta #phi_{jet}"}; // Coarser axes for signal extraction: @@ -402,6 +409,7 @@ struct lambdajetpolarizationionsderived { // (No mass dependency -- useful to check kinematic sculpting from cuts) // =============================== histos.add((folder + "/QA/hDeltaPhi").c_str(), "#Delta#varphi_{jet};#Delta#varphi_{jet};Counts", kTH1D, {axisConfigurations.axisDeltaPhi}); + histos.add((folder + "/QA/hDeltaPhiVsDeltaEta").c_str(), "#Delta#varphi_{jet};#Delta#varphi_{jet}; #eta_{#Lambda}-#eta_{Jet};Counts", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDeltaEtaCoarse}); histos.add((folder + "/QA/hDeltaTheta").c_str(), "#Delta#theta_{jet};#Delta#theta_{jet};Counts", kTH1D, {axisConfigurations.axisDeltaTheta}); histos.add((folder + "/QA/hCosDeltaTheta").c_str(), "cos(#Delta#theta_{jet});cos(#Delta#theta_{jet});Counts", kTH1D, {axisConfigurations.axisCosTheta}); // Should actually be flat due to the geometry histos.add((folder + "/QA/hIntegrated").c_str(), "Integrated counts; ;Counts", kTH1D, {{1, -0.5, 0.5}}); @@ -423,7 +431,7 @@ struct lambdajetpolarizationionsderived { // Polarization observable QAs // (not Ring: actual polarization!) // =============================== - // Will implement these as TProfiles, as polarization is also a measure like P_\Lambda = (3/\alpha_\Lambda) * , so the error is similar + // Will implement these as TProfiles, as polarization is also a measure like P_Lambda = (3/\alpha_Lambda) * , so the error is similar // =============================== // 1D TProfiles // =============================== @@ -452,6 +460,8 @@ struct lambdajetpolarizationionsderived { // 1D TProfiles // =============================== histos.add((folder + "/pRingObservableDeltaPhi").c_str(), "<#it{R}> vs #Delta#varphi_{jet};#Delta#varphi_{jet};<#it{R}>", kTProfile, {axisConfigurations.axisDeltaPhi}); + histos.add((folder + "/pRingObservablePhiJet").c_str(), "<#it{R}> vs #varphi_{jet};#varphi_{jet};<#it{R}>", kTProfile, {axisConfigurations.axisPhi}); + histos.add((folder + "/pRingObservablePhiLambda").c_str(), "<#it{R}> vs #varphi_{#Lambda};#varphi_{#Lambda};<#it{R}>", kTProfile, {axisConfigurations.axisPhi}); histos.add((folder + "/pRingObservableDeltaTheta").c_str(), "<#it{R}> vs #Delta#theta_{jet};#Delta#theta_{jet};<#it{R}>", kTProfile, {axisConfigurations.axisDeltaTheta}); histos.add((folder + "/pRingObservableIntegrated").c_str(), "Integrated <#it{R}>; ;<#it{R}>", kTProfile, {{1, -0.5, 0.5}}); histos.add((folder + "/pRingObservableLambdaPt").c_str(), "<#it{R}> vs #it{p}_{T}^{#Lambda};#it{p}_{T}^{#Lambda} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisPt}); @@ -539,7 +549,7 @@ struct lambdajetpolarizationionsderived { // =============================== // TProfiles vs Mass: quick glancing before signal extraction // =============================== - // TProfile of ring vs mass (integrated in all phi, and properly normalized by N_\Lambda): + // TProfile of ring vs mass (integrated in all phi, and properly normalized by N_Lambda): histos.add((folder + "/pRingObservableMass").c_str(), "<#it{R}> vs Mass;m_{p#pi} (GeV/c^{2});<#it{R}>", kTProfile, {axisConfigurations.axisLambdaMassSigExtract}); histos.add((folder + "/pRingObservableLeadPMass").c_str(), "<#it{R}> vs Mass (LeadP);m_{p#pi} (GeV/c^{2});<#it{R}>", kTProfile, {axisConfigurations.axisLambdaMassSigExtract}); histos.add((folder + "/pRingObservable2ndJetMass").c_str(), "<#it{R}> vs Mass (SubJet);m_{p#pi} (GeV/c^{2});<#it{R}>", kTProfile, {axisConfigurations.axisLambdaMassSigExtract}); @@ -548,6 +558,8 @@ struct lambdajetpolarizationionsderived { // TProfile2D: vs Mass (DeltaTheta) histos.add((folder + "/p2dRingObservableDeltaThetaVsMass").c_str(), "<#it{R}> vs #Delta#theta_{jet} vs Mass;#Delta#theta_{jet};m_{p#pi} (GeV/c^{2});<#it{R}>", kTProfile2D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract}); // TProfile2D: vs Eta Lambda vs Eta Jet (Understanding eta dependence seen in pRingEtaCuts) + histos.add((folder + "/EtaDependence/hCounterEtaLambdaMinusEtaJet").c_str(), "N_{V0s} vs #eta_{#Lambda} - #eta_{Jet};#eta_{#Lambda} - #eta_{Jet}; N_{V0s}", kTH1D, {axisConfigurations.axisDeltaEtaCoarse}); + histos.add((folder + "/EtaDependence/pRingObservableEtaLambdaMinusEtaJet").c_str(), "<#it{R}> vs #eta_{#Lambda} - #eta_{Jet};#eta_{#Lambda} - #eta_{Jet};<#it{R}>", kTProfile, {axisConfigurations.axisDeltaEtaCoarse}); histos.add((folder + "/EtaDependence/p2dRingObservableEtaLambdaVsEtaJet").c_str(), "<#it{R}> vs #eta_{#Lambda} vs #eta_{Jet};#eta_{#Lambda};#eta_{Jet};<#it{R}>", kTProfile2D, {axisConfigurations.axisEtaCoarse, axisConfigurations.axisEtaCoarse}); histos.add((folder + "/EtaDependence/p2dRingObservableEtaLambdaVsEtaJet_FineBins").c_str(), "<#it{R}> vs #eta_{#Lambda} vs #eta_{Jet} (fine bins);#eta_{#Lambda};#eta_{Jet};<#it{R}>", kTProfile2D, {axisConfigurations.axisEta, axisConfigurations.axisEta}); histos.add((folder + "/EtaDependence/p2dRingObservableEtaLambdaVsEtaLeadP").c_str(), "<#it{R}> vs #eta_{#Lambda} vs #eta_{LeadP};#eta_{#Lambda};#eta_{LeadP};<#it{R}>", kTProfile2D, {axisConfigurations.axisEtaCoarse, axisConfigurations.axisEtaCoarse}); @@ -711,7 +723,7 @@ struct lambdajetpolarizationionsderived { // The same, but for actual signal instead of counts: histos.add("HelicityEfficiencyQA/pFakePolSignalVsCosTheta", "FakePolSignal; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda};", kTProfile2D, {axisConfigurations.axisCosTheta, {9, 0, 9}}); - histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalVsCosTheta"))->GetZaxis()->SetTitle(""); + histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalVsCosTheta"))->GetZaxis()->SetTitle("<#it{R}>"); histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalVsCosTheta"))->GetYaxis()->SetBinLabel(1, "All #Lambda"); histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalVsCosTheta"))->GetYaxis()->SetBinLabel(2, "#eta_{Jet} #geq 0"); histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalVsCosTheta"))->GetYaxis()->SetBinLabel(3, "#eta_{Jet} < 0"); @@ -725,13 +737,59 @@ struct lambdajetpolarizationionsderived { // Seeing the dependence between phi* = atan2(p_p_star \cdot (p_Lambda_hat \times (z_hat \cross p_Lambda)), p_p_star \cdot (z_hat \cross p_Lambda)) // e_z = p_Lambda_hat; // e_x = normalize(z_hat cross p_Lambda); // e_y = e_z cross e_x; // phi_star = atan2(p_p_star dot e_y, p_p_star dot e_x); - histos.add("HelicityEfficiencyQA/hFakePolCounts_CosThetaVsPhiStar", "FakePolCounts; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; #phi^*", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisDeltaPhi}); - histos.add("HelicityEfficiencyQA/pFakePolSignal_CosThetaVsPhiStar", "FakePolSignal; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; #phi^*", kTProfile2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisDeltaPhi}); + histos.add("HelicityEfficiencyQA/hFakePolCounts_CosThetaVsPhiStar", "FakePolCounts; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; #phi^{*}", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisDeltaPhi}); + histos.add("HelicityEfficiencyQA/pFakePolSignal_CosThetaVsPhiStar", "FakePolSignal; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; #phi^{*}", kTProfile2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisDeltaPhi}); // Specific counter for when we have leading jets (relates directly to pFakePolSignal_CosThetaVsPhiStar): - histos.add("HelicityEfficiencyQA/hFakePolCountsJet_CosThetaVsPhiStar", "FakePolCounts - HasValidLeadJet OK; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; #phi^*", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisDeltaPhi}); - - - // Doing the same for leading particles: + histos.add("HelicityEfficiencyQA/hFakePolCountsJet_CosThetaVsPhiStar", "FakePolCounts - HasValidLeadJet OK; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; #phi^{*}", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisDeltaPhi}); + + // Similar split, but for AEE instead of HEE: + histos.add("HelicityEfficiencyQA/hCountsVsPhiStar", "FakePolCounts, AEE dependence; #phi^{*};", kTH2D, {axisConfigurations.axisDeltaPhi, {9, 0, 9}}); + histos.get(HIST("HelicityEfficiencyQA/hCountsVsPhiStar"))->GetZaxis()->SetTitle("Counts"); + histos.get(HIST("HelicityEfficiencyQA/hCountsVsPhiStar"))->GetYaxis()->SetBinLabel(1, "All #Lambda"); + histos.get(HIST("HelicityEfficiencyQA/hCountsVsPhiStar"))->GetYaxis()->SetBinLabel(2, "#eta_{Jet} #geq 0"); + histos.get(HIST("HelicityEfficiencyQA/hCountsVsPhiStar"))->GetYaxis()->SetBinLabel(3, "#eta_{Jet} < 0"); + histos.get(HIST("HelicityEfficiencyQA/hCountsVsPhiStar"))->GetYaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); + histos.get(HIST("HelicityEfficiencyQA/hCountsVsPhiStar"))->GetYaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); + histos.get(HIST("HelicityEfficiencyQA/hCountsVsPhiStar"))->GetYaxis()->SetBinLabel(6, "#eta_{Jet} #geq 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("HelicityEfficiencyQA/hCountsVsPhiStar"))->GetYaxis()->SetBinLabel(7, "#eta_{Jet} #geq 0, #eta_{#Lambda} < 0"); + histos.get(HIST("HelicityEfficiencyQA/hCountsVsPhiStar"))->GetYaxis()->SetBinLabel(8, "#eta_{Jet} < 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("HelicityEfficiencyQA/hCountsVsPhiStar"))->GetYaxis()->SetBinLabel(9, "#eta_{Jet} < 0, #eta_{#Lambda} < 0"); + // For the ring observable as well: + histos.add("HelicityEfficiencyQA/pFakePolSignalvsPhiStar", "FakePolSignal, AEE dependence; #phi^{*};", kTProfile2D, {axisConfigurations.axisDeltaPhi, {9, 0, 9}}); + histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiStar"))->GetZaxis()->SetTitle("<#it{R}>"); + histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiStar"))->GetYaxis()->SetBinLabel(1, "All #Lambda"); + histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiStar"))->GetYaxis()->SetBinLabel(2, "#eta_{Jet} #geq 0"); + histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiStar"))->GetYaxis()->SetBinLabel(3, "#eta_{Jet} < 0"); + histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiStar"))->GetYaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); + histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiStar"))->GetYaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); + histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiStar"))->GetYaxis()->SetBinLabel(6, "#eta_{Jet} #geq 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiStar"))->GetYaxis()->SetBinLabel(7, "#eta_{Jet} #geq 0, #eta_{#Lambda} < 0"); + histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiStar"))->GetYaxis()->SetBinLabel(8, "#eta_{Jet} < 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiStar"))->GetYaxis()->SetBinLabel(9, "#eta_{Jet} < 0, #eta_{#Lambda} < 0"); + + // For the phi_Lambda - phi_D^* dependency as well: + histos.add("HelicityEfficiencyQA/pFakePolSignalvsPhiLambdaMinusPhiProtonStar", "FakePolSignal, AEE dependence; #phi_{#Lambda} - #phi_{p}^{*};", kTProfile2D, {axisConfigurations.axisDeltaPhi, {9, 0, 9}}); + histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"))->GetZaxis()->SetTitle("<#it{R}>"); + histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"))->GetYaxis()->SetBinLabel(1, "All #Lambda"); + histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"))->GetYaxis()->SetBinLabel(2, "#eta_{Jet} #geq 0"); + histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"))->GetYaxis()->SetBinLabel(3, "#eta_{Jet} < 0"); + histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"))->GetYaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); + histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"))->GetYaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); + histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"))->GetYaxis()->SetBinLabel(6, "#eta_{Jet} #geq 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"))->GetYaxis()->SetBinLabel(7, "#eta_{Jet} #geq 0, #eta_{#Lambda} < 0"); + histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"))->GetYaxis()->SetBinLabel(8, "#eta_{Jet} < 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"))->GetYaxis()->SetBinLabel(9, "#eta_{Jet} < 0, #eta_{#Lambda} < 0"); + + // More about possible AEE dependencies (should see an invariance with JetEta and /Jz): + // TODO: think about these error bars: do they still make sense via regular TProfile's SEM error? + histos.add("HelicityEfficiencyQA/pRingVsJetZcomponent", "<#it{R}> vs #hat{t}_{z}; #hat{t}_{z}; <#it{R}>", kTProfile, {{40, -1, 1}}); // Numerically stable and can also show the sign flip (essentially the <#it{R}> vs Eta Jet plot in another scale) + histos.add("HelicityEfficiencyQA/pRingOverJetZcomponent_VsJetEta", "<#it{R}>/#hat{t}_{z}; #eta_{Jet}; <#it{R}>/#hat{t}_{z}", kTProfile, {axisConfigurations.axisEtaCoarse}); + histos.add("HelicityEfficiencyQA/pRingOverJetZcomponent_VsCosThetaHEE", "<#it{R}>/#hat{t}_{z} Vs cos(#theta) HEE; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; <#it{R}>/#hat{t}_{z}", kTProfile, {axisConfigurations.axisCosTheta}); + histos.add("HelicityEfficiencyQA/pRingOverJetZcomponent_VsPhiStar", "<#it{R}>/#hat{t}_{z} Vs #phi^{*}; #phi^{*} = atan2(#vec{p}^{*}_{p} #cdot [#hat{p}_{#Lambda} #times (#hat{z} #times #hat{p}_{#Lambda})] , #vec{p}^{*}_{p} #cdot (#hat{z} #times #hat{p}_{#Lambda})); <#it{R}>/#hat{t}_{z}", kTProfile, {axisConfigurations.axisDeltaPhi}); + histos.add("HelicityEfficiencyQA/pRingOverJetZcomponent_VsJetEtaVsCosThetaHEE", "<#it{R}>/#hat{t}_{z}; #eta_{Jet}; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; <#it{R}>/#hat{t}_{z}", kTProfile2D, {axisConfigurations.axisEtaCoarse, axisConfigurations.axisCosTheta}); + histos.add("HelicityEfficiencyQA/pRingOverJetZcomponent_VsJetEtaVsPhiStar", "<#it{R}>/#hat{t}_{z}; #eta_{Jet}; #phi^{*}; <#it{R}>/#hat{t}_{z}", kTProfile2D, {axisConfigurations.axisEtaCoarse, axisConfigurations.axisDeltaPhi}); + + // Doing the same HEE study for leading particles: // (eta_{Jet} may be a bad estimator!) histos.add("HelicityEfficiencyQA/hFakePolCountsLeadP", "FakePolCounts; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda};", kTH2D, {axisConfigurations.axisCosTheta, {9, 0, 9}}); histos.get(HIST("HelicityEfficiencyQA/hFakePolCountsLeadP"))->GetZaxis()->SetTitle("N_{V0s}"); @@ -796,8 +854,8 @@ struct lambdajetpolarizationionsderived { histos.add("HelicityEfficiencyQA/hLambdaMassVsPhiLambdaMinusPhiProtonStar", "m_{#Lambda}, AEE probe; m_{Inv}; #phi_{#Lambda} - #phi_{p}^{*} ; Counts", kTH2D, {axisConfigurations.axisLambdaMass, axisConfigurations.axisDeltaPhi}); histos.add("HelicityEfficiencyQA/hAntiLambdaMassVsPhiLambdaMinusPhiProtonStar", "m_{#bar{#Lambda}}, AEE probe; m_{Inv}; #phi_{#bar{#Lambda}} - #phi_{p}^{*} ; Counts", kTH2D, {axisConfigurations.axisLambdaMass, axisConfigurations.axisDeltaPhi}); // Watching the effect on the ring observable as well: - histos.add("HelicityEfficiencyQA/p2dRing_LambdaMassVsPhiLambdaMinusPhiProtonStar", "<#it{R}>, AEE probe; m_{Inv}; #phi_{#Lambda} - #phi_{p}^{*} ; <#it{R}>", kTProfile2D, {axisConfigurations.axisLambdaMass, axisConfigurations.axisDeltaPhi}); - histos.add("HelicityEfficiencyQA/p2dRing_AntiLambdaMassVsPhiLambdaMinusPhiProtonStar", "<#it{R}>, AEE probe; m_{Inv}; #phi_{#bar{#Lambda}} - #phi_{p}^{*} ; <#it{R}>", kTProfile2D, {axisConfigurations.axisLambdaMass, axisConfigurations.axisDeltaPhi}); + histos.add("HelicityEfficiencyQA/p2dRing_LambdaMassVsPhiLambdaMinusPhiProtonStar", "<#it{R}>, AEE probe; m_{Inv}; #phi_{#Lambda} - #phi_{p}^{*} ; <#it{R}>", kTProfile2D, {axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisDeltaPhi}); + histos.add("HelicityEfficiencyQA/p2dRing_AntiLambdaMassVsPhiLambdaMinusPhiProtonStar", "<#it{R}>, AEE probe; m_{Inv}; #phi_{#bar{#Lambda}} - #phi_{p}^{*} ; <#it{R}>", kTProfile2D, {axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisDeltaPhi}); // Integrated observable for events with NLambda+NAntiLambda V0s per event // (an interesting measurement of correlation between and Lambda-like V0s multiplicity. A proxy of covariance) @@ -1202,8 +1260,11 @@ struct lambdajetpolarizationionsderived { ////////////////////////////////////////// float ringObservable = 0.; float deltaPhiJet = 0.; + float deltaEtaJet = 0.; float deltaThetaJet = 0.; float cosDeltaThetaJet = 0.; + float Jz = 0.; + float ringObservableOverJetZ = 0.; if (hasValidLeadingJet) { // Cross product XYZVector cross = leadingJetUnitVec.Cross(lambdaLike3Vec); @@ -1215,9 +1276,29 @@ struct lambdajetpolarizationionsderived { ringObservable *= (isLambda) ? polPrefactorLambda : -1.0 * polPrefactorAntiLambda; // Angular variables deltaPhiJet = wrapToPiFast(v0phi - leadingJetPhi); + deltaEtaJet = v0eta - leadingJetEta; cosDeltaThetaJet = leadingJetUnitVec.Dot(lambdaLikeUnit3Vec); deltaThetaJet = std::acos(cosDeltaThetaJet); + + // Testing an invariance -- /\hat{t}_z -- Possible source of an artificial (trivial) sign flip in the observable: + Jz = leadingJetUnitVec.Z(); + if (std::abs(Jz) > 1e-4) + ringObservableOverJetZ = ringObservable/Jz; + else + ringObservableOverJetZ = 0.0; // A simple guard. May not be the best, but works + + // // A second projection schema, where e_x = normalize(t_hat cross p_Lambda), using t_hat instead of z_hat (different from phi*): + // // (this decomposes in an orthogonal basis related to the jet coordinates) + // XYZVector ez = lambdaLikeUnit3Vec; + // XYZVector ex = leadingJetUnitVec.Cross(lambdaLike3Vec); + // XYZVector ey = ez.Cross(ex); + + // ringObservableExProjection = ringObservable; + // ringObservableEyProjection = + // // Assuming that energy can get inside the average in: + // // P_Lambda \cdot p_Lambda = E_Lambda/m_Lambda * P_Lambda^* \cdot p_Lambda = * p_Lambda + // ringObservableEzProjection = cosFakePol * lambdaLike4Vec.E()/v0LambdaLikeMass; } ////////////////////////////////////////// @@ -1243,7 +1324,7 @@ struct lambdajetpolarizationionsderived { } // Calculating polarization observables (in the Lambda frame, because that is easier -- does not require boosts): - // To be precise, not actually the polarization, but a part of the summand in P^*_\Lambda = (3/\alpha_\Lambda) * + // To be precise, not actually the polarization, but a part of the summand in P^*_Lambda = (3/\alpha_Lambda) * float PolStarX = 0, PolStarY = 0, PolStarZ = 0; // Dummy initialization: avoid warnings in compile time if (isLambda) { // Notice there is no need to check analyseLambda again due to previous checks. PolStarX = polPrefactorLambda * protonLikeStarUnit3Vec.X(); @@ -1293,6 +1374,13 @@ struct lambdajetpolarizationionsderived { // AEE and HEE correlation: histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsJet_CosThetaVsPhiStar"), cosFakePol, phiStar); histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignal_CosThetaVsPhiStar"), cosFakePol, phiStar, ringObservable); + + histos.fill(HIST("HelicityEfficiencyQA/pRingVsJetZcomponent"), Jz, ringObservable); + histos.fill(HIST("HelicityEfficiencyQA/pRingOverJetZcomponent_VsJetEta"), leadingJetEta, ringObservableOverJetZ); + histos.fill(HIST("HelicityEfficiencyQA/pRingOverJetZcomponent_VsCosThetaHEE"), cosFakePol, ringObservableOverJetZ); + histos.fill(HIST("HelicityEfficiencyQA/pRingOverJetZcomponent_VsPhiStar"), phiStar, ringObservableOverJetZ); + histos.fill(HIST("HelicityEfficiencyQA/pRingOverJetZcomponent_VsJetEtaVsCosThetaHEE"), leadingJetEta, cosFakePol, ringObservableOverJetZ); + histos.fill(HIST("HelicityEfficiencyQA/pRingOverJetZcomponent_VsJetEtaVsPhiStar"), leadingJetEta, phiStar, ringObservableOverJetZ); } if (hasValidSubJet) { RING_OBSERVABLE_2NDJET_FILL_LIST(APPLY_HISTO_FILL, "Ring") @@ -1305,11 +1393,20 @@ struct lambdajetpolarizationionsderived { histos.fill(HIST("ProxyEta/pRingEtaCuts"), 0, ringObservable); histos.fill(HIST("ProxyEta/pRingEtaCuts"), lambdaEtaPos ? 3 : 4, ringObservable); + // HEE study (helicity efficiency effect): histos.fill(HIST("HelicityEfficiencyQA/hFakePolCounts"), cosFakePol, 0); histos.fill(HIST("HelicityEfficiencyQA/hFakePolCounts"), cosFakePol, lambdaEtaPos ? 3 : 4); // Same for signal: histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalVsCosTheta"), cosFakePol, 0, ringObservable); histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalVsCosTheta"), cosFakePol, lambdaEtaPos ? 3 : 4, ringObservable); + // Counter and ring accumulators for AEE study: + histos.fill(HIST("HelicityEfficiencyQA/hCountsVsPhiStar"), phiStar, 0); + histos.fill(HIST("HelicityEfficiencyQA/hCountsVsPhiStar"), phiStar, lambdaEtaPos ? 3 : 4); + histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiStar"), phiStar, 0, ringObservable); + histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiStar"), phiStar, lambdaEtaPos ? 3 : 4, ringObservable); + + histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"), deltaPhiLambdaProtonStar, 0, ringObservable); + histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"), deltaPhiLambdaProtonStar, lambdaEtaPos ? 3 : 4, ringObservable); // Extra correlations test: histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaJet"), cosFakePol, deltaThetaJet); @@ -1332,6 +1429,14 @@ struct lambdajetpolarizationionsderived { // Same for signal: histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalVsCosTheta"), cosFakePol, 1, ringObservable); histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalVsCosTheta"), cosFakePol, lambdaEtaPos ? 5 : 6, ringObservable); + // Counter and ring accumulators for AEE study: + histos.fill(HIST("HelicityEfficiencyQA/hCountsVsPhiStar"), phiStar, 1); + histos.fill(HIST("HelicityEfficiencyQA/hCountsVsPhiStar"), phiStar, lambdaEtaPos ? 5 : 6); + histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiStar"), phiStar, 1, ringObservable); + histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiStar"), phiStar, lambdaEtaPos ? 5 : 6, ringObservable); + + histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"), deltaPhiLambdaProtonStar, 1, ringObservable); + histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"), deltaPhiLambdaProtonStar, lambdaEtaPos ? 5 : 6, ringObservable); histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaJetPosEta"), cosFakePol, deltaThetaJet); if (pTLambdaCheck) { @@ -1355,6 +1460,14 @@ struct lambdajetpolarizationionsderived { // Same for signal: histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalVsCosTheta"), cosFakePol, 2, ringObservable); histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalVsCosTheta"), cosFakePol, lambdaEtaPos ? 7 : 8, ringObservable); + // Counter and ring accumulators for AEE study: + histos.fill(HIST("HelicityEfficiencyQA/hCountsVsPhiStar"), phiStar, 2); + histos.fill(HIST("HelicityEfficiencyQA/hCountsVsPhiStar"), phiStar, lambdaEtaPos ? 7 : 8); + histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiStar"), phiStar, 2, ringObservable); + histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiStar"), phiStar, lambdaEtaPos ? 7 : 8, ringObservable); + + histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"), deltaPhiLambdaProtonStar, 2, ringObservable); + histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"), deltaPhiLambdaProtonStar, lambdaEtaPos ? 7 : 8, ringObservable); histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaJetNegEta"), cosFakePol, deltaThetaJet); From 2a19647311c6a7ffec1ced710ce97570dead4165 Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Thu, 7 May 2026 23:01:07 +0000 Subject: [PATCH 11/31] Renaming histograms and adding "forceRandJet" options. As the Azimuthal Efficiency Effect (AEE)'s fake polarization signal is inherent to the Lambdas themselves and the magnetic field, randomly changing jet orientation should still give an AEE signal, but no true vorticity signal. By doing this, we can possibly get an efficiency map of AEE alone. Idea is similar to an event mixing where we take whole Lambdas from one event and whole jets from another -- but instead just randomizing the jet in the same event, avoiding some spurious physics correlations that could arise. --- .../Strangeness/lambdaJetPolarizationIons.cxx | 7 +- .../lambdaJetPolarizationIonsDerived.cxx | 539 +++++++++++------- 2 files changed, 322 insertions(+), 224 deletions(-) diff --git a/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx b/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx index 4d5f2849a44..ef21d09f3d4 100644 --- a/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx +++ b/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx @@ -210,9 +210,8 @@ struct lambdajetpolarizationions { std::string prefix = "v0Selections"; // JSON group name Configurable v0TypeSelection{"v0TypeSelection", 1, "select on a certain V0 type (leave negative if no selection desired)"}; + Configurable rapidityCut{"rapidityCut", 1.0f, "rapidity"}; // This is actually a physics cut, not a proper acceptance cut // Selection criteria: acceptance - Configurable rapidityCut{"rapidityCut", 1.0f, "rapidity"}; - Configurable v0EtaCut{"v0EtaCut", 0.9f, "eta cut for v0"}; Configurable daughterEtaCut{"daughterEtaCut", 0.9f, "max eta for daughters"}; // Default is 0.8. Changed to 0.9 to agree with jet selection. TODO: test the impact/biasing of this! // Standard 5 topological criteria -- Closed a bit more for the Lambda analysis @@ -1258,12 +1257,10 @@ struct lambdajetpolarizationions { return false; V0SelCounter.fill(); - // pseudorapidity cuts: + // rapidity cuts (this is actually a physics cut, moreso than being an acceptance cut): if (std::fabs(v0.yLambda()) > v0Selections.rapidityCut) return false; - // if (std::fabs(v0.eta()) > v0Selections.v0EtaCut) return false; V0SelCounter.fill(); - // if (std::fabs(v0.eta()) > v0Selections.daughterEtaCut) return false; // (TODO: properly consider this in daughter selection!) // competing mass rejection (if compMassRejection < 0, this cut does nothing) if (std::fabs(v0.mK0Short() - o2::constants::physics::MassK0Short) < v0Selections.compMassRejection) diff --git a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx index 4702c5e78dc..777193ba517 100644 --- a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx @@ -304,6 +304,7 @@ struct lambdajetpolarizationionsderived { Configurable forcePolSignQA{"forcePolSignQA", false, "force antiLambda decay constant to be positive: should kill all the signal, if any. For QA"}; Configurable forcePerpToJet{"forcePerpToJet", false, "force jet direction to be perpendicular to jet estimator. For QA"}; Configurable forceJetDirectionSmudge{"forceJetDirectionSmudge", false, "fluctuate jet direction by 10% of R around original axis. For QA (tests sensibility)"}; + Configurable forceRandJet{"forceRandJet", false, "makes jet direction random. A QA for AEE fake signal and its removal"}; // Configurable jetRForSmudging{"jetRForSmudging", 0.4, "QA quantity: the chosen R scale for the jet direction smudge"}; // Superseeded by jetR: kept the same scale in analysis and QA Configurable jetR{"jetR", 0.4f, "Radius of the jet"}; // Provide manually, please. Configurable minLeadParticlePt{"minLeadParticlePt", 2.0f, "Minimum Pt for a lead track to be considered a valid proxy for a jet (may be more restrictive than TableProducer)"}; @@ -340,6 +341,8 @@ struct lambdajetpolarizationionsderived { ConfigurableAxis axisPhi{"axisPhi", {40, 0., constants::math::TwoPI}, "#varphi"}; ConfigurableAxis axisDeltaPhi{"axisDeltaPhi", {40, -constants::math::PI, constants::math::PI}, "#Delta #phi_{jet}"}; + ConfigurableAxis axisDCAdau{"axisDCAdau", {20, 0., 2.0}, "DCA V0 daughters"}; + // Coarser axes for signal extraction: ConfigurableAxis axisPtSigExtract{"axisPtSigExtract", {VARIABLE_WIDTH, 0.0f, 0.25f, 0.5f, 0.75f, 1.0f, 1.25f, 1.5f, 2.0f, 2.5f, 3.0f, 4.0f, 6.0f, 8.0f, 10.0f, 15.0f, 20.0f, 30.0f, 50.0f}, "pt axis for signal extraction"}; // ConfigurableAxis axisLambdaMassSigExtract{"axisLambdaMassSigExtract", {175, 1.08f, 1.15f}, "Lambda mass in GeV/c"}; // With a sigma of 0.002 GeV/c, this has about 5 bins per sigma, so that the window is properly grasped. @@ -361,7 +364,8 @@ struct lambdajetpolarizationionsderived { // (TODO: add a lambdaPt axis that is pre-selected only on the 0.5 to 1.5 Pt region for the Ring observable with lambda cuts to not store a huge histogram with empty bins by construction) - ConfigurableAxis axisCentrality{"axisCentrality", {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.0f}, "Centrality"}; + // ConfigurableAxis axisCentrality{"axisCentrality", {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.0f}, "Centrality"}; + ConfigurableAxis axisCentrality{"axisCentrality", {VARIABLE_WIDTH, 0.0f, 20.0f, 50.0f, 100.0f}, "Centrality"}; // For the delta method error propagation (slightly better than just SEM error propagation with TProfiles): ConfigurableAxis axisDeltaComponents{"axisDeltaComponents", {5, 0.0, 5.0}, "0: r_k, 1: n_k, 2: r_k^2, 3: n_k^2, 4: r_k*n_k"}; @@ -660,79 +664,79 @@ struct lambdajetpolarizationionsderived { // Integrated observable dependent on jet proxy #eta to unfold possible asymmetries in detector: - histos.add("ProxyEta/pRingEtaCuts", "pRingEtaCuts; ;<#it{R}>", kTProfile, {{15, 0, 15}}); - histos.get(HIST("ProxyEta/pRingEtaCuts"))->GetXaxis()->SetBinLabel(1, "All #Lambda"); - histos.get(HIST("ProxyEta/pRingEtaCuts"))->GetXaxis()->SetBinLabel(2, "#eta_{Jet} #geq 0"); - histos.get(HIST("ProxyEta/pRingEtaCuts"))->GetXaxis()->SetBinLabel(3, "#eta_{Jet} < 0"); - histos.get(HIST("ProxyEta/pRingEtaCuts"))->GetXaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); - histos.get(HIST("ProxyEta/pRingEtaCuts"))->GetXaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); - histos.get(HIST("ProxyEta/pRingEtaCuts"))->GetXaxis()->SetBinLabel(6, "#eta_{Jet} #geq 0, #eta_{#Lambda} #geq 0"); - histos.get(HIST("ProxyEta/pRingEtaCuts"))->GetXaxis()->SetBinLabel(7, "#eta_{Jet} #geq 0, #eta_{#Lambda} < 0"); - histos.get(HIST("ProxyEta/pRingEtaCuts"))->GetXaxis()->SetBinLabel(8, "#eta_{Jet} < 0, #eta_{#Lambda} #geq 0"); - histos.get(HIST("ProxyEta/pRingEtaCuts"))->GetXaxis()->SetBinLabel(9, "#eta_{Jet} < 0, #eta_{#Lambda} < 0"); - histos.get(HIST("ProxyEta/pRingEtaCuts"))->GetXaxis()->SetBinLabel(10, "#eta_{Jet} > R"); - histos.get(HIST("ProxyEta/pRingEtaCuts"))->GetXaxis()->SetBinLabel(11, "#eta_{Jet} < -R"); - histos.get(HIST("ProxyEta/pRingEtaCuts"))->GetXaxis()->SetBinLabel(12, "#eta_{Jet} > R, #eta_{#Lambda} #geq 0"); - histos.get(HIST("ProxyEta/pRingEtaCuts"))->GetXaxis()->SetBinLabel(13, "#eta_{Jet} > R, #eta_{#Lambda} < 0"); - histos.get(HIST("ProxyEta/pRingEtaCuts"))->GetXaxis()->SetBinLabel(14, "#eta_{Jet} < -R, #eta_{#Lambda} #geq 0"); - histos.get(HIST("ProxyEta/pRingEtaCuts"))->GetXaxis()->SetBinLabel(15, "#eta_{Jet} < -R, #eta_{#Lambda} < 0"); - - histos.add("ProxyEta/pRingEtaCutsSubLeadingJet", "pRingEtaCutsSubLeadingJet; ;<#it{R}>", kTProfile, {{15, 0, 15}}); - histos.get(HIST("ProxyEta/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(1, "All #Lambda"); - histos.get(HIST("ProxyEta/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(2, "#eta_{SubJet} #geq 0"); - histos.get(HIST("ProxyEta/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(3, "#eta_{SubJet} < 0"); - histos.get(HIST("ProxyEta/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); - histos.get(HIST("ProxyEta/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); - histos.get(HIST("ProxyEta/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(6, "#eta_{SubJet} #geq 0, #eta_{#Lambda} #geq 0"); - histos.get(HIST("ProxyEta/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(7, "#eta_{SubJet} #geq 0, #eta_{#Lambda} < 0"); - histos.get(HIST("ProxyEta/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(8, "#eta_{SubJet} < 0, #eta_{#Lambda} #geq 0"); - histos.get(HIST("ProxyEta/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(9, "#eta_{SubJet} < 0, #eta_{#Lambda} < 0"); - histos.get(HIST("ProxyEta/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(10, "#eta_{SubJet} > R"); - histos.get(HIST("ProxyEta/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(11, "#eta_{SubJet} < -R"); - histos.get(HIST("ProxyEta/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(12, "#eta_{SubJet} > R, #eta_{#Lambda} #geq 0"); - histos.get(HIST("ProxyEta/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(13, "#eta_{SubJet} > R, #eta_{#Lambda} < 0"); - histos.get(HIST("ProxyEta/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(14, "#eta_{SubJet} < -R, #eta_{#Lambda} #geq 0"); - histos.get(HIST("ProxyEta/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(15, "#eta_{SubJet} < -R, #eta_{#Lambda} < 0"); - - histos.add("ProxyEta/pRingEtaCutsLeadingP", "pRingEtaCutsLeadingP; ;<#it{R}>", kTProfile, {{9, 0, 9}}); - histos.get(HIST("ProxyEta/pRingEtaCutsLeadingP"))->GetXaxis()->SetBinLabel(1, "All #Lambda"); - histos.get(HIST("ProxyEta/pRingEtaCutsLeadingP"))->GetXaxis()->SetBinLabel(2, "#eta_{LeadP} #geq 0"); - histos.get(HIST("ProxyEta/pRingEtaCutsLeadingP"))->GetXaxis()->SetBinLabel(3, "#eta_{LeadP} < 0"); - histos.get(HIST("ProxyEta/pRingEtaCutsLeadingP"))->GetXaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); - histos.get(HIST("ProxyEta/pRingEtaCutsLeadingP"))->GetXaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); - histos.get(HIST("ProxyEta/pRingEtaCutsLeadingP"))->GetXaxis()->SetBinLabel(6, "#eta_{LeadP} #geq 0, #eta_{#Lambda} #geq 0"); - histos.get(HIST("ProxyEta/pRingEtaCutsLeadingP"))->GetXaxis()->SetBinLabel(7, "#eta_{LeadP} #geq 0, #eta_{#Lambda} < 0"); - histos.get(HIST("ProxyEta/pRingEtaCutsLeadingP"))->GetXaxis()->SetBinLabel(8, "#eta_{LeadP} < 0, #eta_{#Lambda} #geq 0"); - histos.get(HIST("ProxyEta/pRingEtaCutsLeadingP"))->GetXaxis()->SetBinLabel(9, "#eta_{LeadP} < 0, #eta_{#Lambda} < 0"); + histos.add("EtaStudy/pRingEtaCuts", "pRingEtaCuts; ;<#it{R}>", kTProfile, {{15, 0, 15}}); + histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(1, "All #Lambda"); + histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(2, "#eta_{Jet} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(3, "#eta_{Jet} < 0"); + histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(6, "#eta_{Jet} #geq 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(7, "#eta_{Jet} #geq 0, #eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(8, "#eta_{Jet} < 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(9, "#eta_{Jet} < 0, #eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(10, "#eta_{Jet} > R"); + histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(11, "#eta_{Jet} < -R"); + histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(12, "#eta_{Jet} > R, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(13, "#eta_{Jet} > R, #eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(14, "#eta_{Jet} < -R, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(15, "#eta_{Jet} < -R, #eta_{#Lambda} < 0"); + + histos.add("EtaStudy/pRingEtaCutsSubLeadingJet", "pRingEtaCutsSubLeadingJet; ;<#it{R}>", kTProfile, {{15, 0, 15}}); + histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(1, "All #Lambda"); + histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(2, "#eta_{SubJet} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(3, "#eta_{SubJet} < 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(6, "#eta_{SubJet} #geq 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(7, "#eta_{SubJet} #geq 0, #eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(8, "#eta_{SubJet} < 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(9, "#eta_{SubJet} < 0, #eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(10, "#eta_{SubJet} > R"); + histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(11, "#eta_{SubJet} < -R"); + histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(12, "#eta_{SubJet} > R, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(13, "#eta_{SubJet} > R, #eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(14, "#eta_{SubJet} < -R, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(15, "#eta_{SubJet} < -R, #eta_{#Lambda} < 0"); + + histos.add("EtaStudy/pRingEtaCutsLeadingP", "pRingEtaCutsLeadingP; ;<#it{R}>", kTProfile, {{9, 0, 9}}); + histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP"))->GetXaxis()->SetBinLabel(1, "All #Lambda"); + histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP"))->GetXaxis()->SetBinLabel(2, "#eta_{LeadP} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP"))->GetXaxis()->SetBinLabel(3, "#eta_{LeadP} < 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP"))->GetXaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP"))->GetXaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP"))->GetXaxis()->SetBinLabel(6, "#eta_{LeadP} #geq 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP"))->GetXaxis()->SetBinLabel(7, "#eta_{LeadP} #geq 0, #eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP"))->GetXaxis()->SetBinLabel(8, "#eta_{LeadP} < 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP"))->GetXaxis()->SetBinLabel(9, "#eta_{LeadP} < 0, #eta_{#Lambda} < 0"); // Fake polarization signal QA // --> The "negative helicity problem", where topologies with a proton decaying opposite to the Lambda momentum are enhanced by // efficiency of reconstruction. The geometries where the proton moves in the same direction as the boost will have a very small // momentum pion, which is not as easily detected as the opposite case! This may insert a fake signal of polarization in the measurement! - histos.add("HelicityEfficiencyQA/hFakePolCounts", "FakePolCounts; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda};", kTH2D, {axisConfigurations.axisCosTheta, {9, 0, 9}}); - histos.get(HIST("HelicityEfficiencyQA/hFakePolCounts"))->GetZaxis()->SetTitle("N_{V0s}"); - histos.get(HIST("HelicityEfficiencyQA/hFakePolCounts"))->GetYaxis()->SetBinLabel(1, "All #Lambda"); - histos.get(HIST("HelicityEfficiencyQA/hFakePolCounts"))->GetYaxis()->SetBinLabel(2, "#eta_{Jet} #geq 0"); - histos.get(HIST("HelicityEfficiencyQA/hFakePolCounts"))->GetYaxis()->SetBinLabel(3, "#eta_{Jet} < 0"); - histos.get(HIST("HelicityEfficiencyQA/hFakePolCounts"))->GetYaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); - histos.get(HIST("HelicityEfficiencyQA/hFakePolCounts"))->GetYaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); - histos.get(HIST("HelicityEfficiencyQA/hFakePolCounts"))->GetYaxis()->SetBinLabel(6, "#eta_{Jet} #geq 0, #eta_{#Lambda} #geq 0"); - histos.get(HIST("HelicityEfficiencyQA/hFakePolCounts"))->GetYaxis()->SetBinLabel(7, "#eta_{Jet} #geq 0, #eta_{#Lambda} < 0"); - histos.get(HIST("HelicityEfficiencyQA/hFakePolCounts"))->GetYaxis()->SetBinLabel(8, "#eta_{Jet} < 0, #eta_{#Lambda} #geq 0"); - histos.get(HIST("HelicityEfficiencyQA/hFakePolCounts"))->GetYaxis()->SetBinLabel(9, "#eta_{Jet} < 0, #eta_{#Lambda} < 0"); + histos.add("EtaStudy/hFakePolCounts", "FakePolCounts; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda};", kTH2D, {axisConfigurations.axisCosTheta, {9, 0, 9}}); + histos.get(HIST("EtaStudy/hFakePolCounts"))->GetZaxis()->SetTitle("N_{V0s}"); + histos.get(HIST("EtaStudy/hFakePolCounts"))->GetYaxis()->SetBinLabel(1, "All #Lambda"); + histos.get(HIST("EtaStudy/hFakePolCounts"))->GetYaxis()->SetBinLabel(2, "#eta_{Jet} #geq 0"); + histos.get(HIST("EtaStudy/hFakePolCounts"))->GetYaxis()->SetBinLabel(3, "#eta_{Jet} < 0"); + histos.get(HIST("EtaStudy/hFakePolCounts"))->GetYaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/hFakePolCounts"))->GetYaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/hFakePolCounts"))->GetYaxis()->SetBinLabel(6, "#eta_{Jet} #geq 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/hFakePolCounts"))->GetYaxis()->SetBinLabel(7, "#eta_{Jet} #geq 0, #eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/hFakePolCounts"))->GetYaxis()->SetBinLabel(8, "#eta_{Jet} < 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/hFakePolCounts"))->GetYaxis()->SetBinLabel(9, "#eta_{Jet} < 0, #eta_{#Lambda} < 0"); // The same, but for actual signal instead of counts: - histos.add("HelicityEfficiencyQA/pFakePolSignalVsCosTheta", "FakePolSignal; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda};", kTProfile2D, {axisConfigurations.axisCosTheta, {9, 0, 9}}); - histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalVsCosTheta"))->GetZaxis()->SetTitle("<#it{R}>"); - histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalVsCosTheta"))->GetYaxis()->SetBinLabel(1, "All #Lambda"); - histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalVsCosTheta"))->GetYaxis()->SetBinLabel(2, "#eta_{Jet} #geq 0"); - histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalVsCosTheta"))->GetYaxis()->SetBinLabel(3, "#eta_{Jet} < 0"); - histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalVsCosTheta"))->GetYaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); - histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalVsCosTheta"))->GetYaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); - histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalVsCosTheta"))->GetYaxis()->SetBinLabel(6, "#eta_{Jet} #geq 0, #eta_{#Lambda} #geq 0"); - histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalVsCosTheta"))->GetYaxis()->SetBinLabel(7, "#eta_{Jet} #geq 0, #eta_{#Lambda} < 0"); - histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalVsCosTheta"))->GetYaxis()->SetBinLabel(8, "#eta_{Jet} < 0, #eta_{#Lambda} #geq 0"); - histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalVsCosTheta"))->GetYaxis()->SetBinLabel(9, "#eta_{Jet} < 0, #eta_{#Lambda} < 0"); + histos.add("EtaStudy/pFakePolSignalVsCosTheta", "FakePolSignal; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda};", kTProfile2D, {axisConfigurations.axisCosTheta, {9, 0, 9}}); + histos.get(HIST("EtaStudy/pFakePolSignalVsCosTheta"))->GetZaxis()->SetTitle("<#it{R}>"); + histos.get(HIST("EtaStudy/pFakePolSignalVsCosTheta"))->GetYaxis()->SetBinLabel(1, "All #Lambda"); + histos.get(HIST("EtaStudy/pFakePolSignalVsCosTheta"))->GetYaxis()->SetBinLabel(2, "#eta_{Jet} #geq 0"); + histos.get(HIST("EtaStudy/pFakePolSignalVsCosTheta"))->GetYaxis()->SetBinLabel(3, "#eta_{Jet} < 0"); + histos.get(HIST("EtaStudy/pFakePolSignalVsCosTheta"))->GetYaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pFakePolSignalVsCosTheta"))->GetYaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/pFakePolSignalVsCosTheta"))->GetYaxis()->SetBinLabel(6, "#eta_{Jet} #geq 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pFakePolSignalVsCosTheta"))->GetYaxis()->SetBinLabel(7, "#eta_{Jet} #geq 0, #eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/pFakePolSignalVsCosTheta"))->GetYaxis()->SetBinLabel(8, "#eta_{Jet} < 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pFakePolSignalVsCosTheta"))->GetYaxis()->SetBinLabel(9, "#eta_{Jet} < 0, #eta_{#Lambda} < 0"); // Seeing the dependence between phi* = atan2(p_p_star \cdot (p_Lambda_hat \times (z_hat \cross p_Lambda)), p_p_star \cdot (z_hat \cross p_Lambda)) // e_z = p_Lambda_hat; // e_x = normalize(z_hat cross p_Lambda); // e_y = e_z cross e_x; @@ -743,45 +747,47 @@ struct lambdajetpolarizationionsderived { histos.add("HelicityEfficiencyQA/hFakePolCountsJet_CosThetaVsPhiStar", "FakePolCounts - HasValidLeadJet OK; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; #phi^{*}", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisDeltaPhi}); // Similar split, but for AEE instead of HEE: - histos.add("HelicityEfficiencyQA/hCountsVsPhiStar", "FakePolCounts, AEE dependence; #phi^{*};", kTH2D, {axisConfigurations.axisDeltaPhi, {9, 0, 9}}); - histos.get(HIST("HelicityEfficiencyQA/hCountsVsPhiStar"))->GetZaxis()->SetTitle("Counts"); - histos.get(HIST("HelicityEfficiencyQA/hCountsVsPhiStar"))->GetYaxis()->SetBinLabel(1, "All #Lambda"); - histos.get(HIST("HelicityEfficiencyQA/hCountsVsPhiStar"))->GetYaxis()->SetBinLabel(2, "#eta_{Jet} #geq 0"); - histos.get(HIST("HelicityEfficiencyQA/hCountsVsPhiStar"))->GetYaxis()->SetBinLabel(3, "#eta_{Jet} < 0"); - histos.get(HIST("HelicityEfficiencyQA/hCountsVsPhiStar"))->GetYaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); - histos.get(HIST("HelicityEfficiencyQA/hCountsVsPhiStar"))->GetYaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); - histos.get(HIST("HelicityEfficiencyQA/hCountsVsPhiStar"))->GetYaxis()->SetBinLabel(6, "#eta_{Jet} #geq 0, #eta_{#Lambda} #geq 0"); - histos.get(HIST("HelicityEfficiencyQA/hCountsVsPhiStar"))->GetYaxis()->SetBinLabel(7, "#eta_{Jet} #geq 0, #eta_{#Lambda} < 0"); - histos.get(HIST("HelicityEfficiencyQA/hCountsVsPhiStar"))->GetYaxis()->SetBinLabel(8, "#eta_{Jet} < 0, #eta_{#Lambda} #geq 0"); - histos.get(HIST("HelicityEfficiencyQA/hCountsVsPhiStar"))->GetYaxis()->SetBinLabel(9, "#eta_{Jet} < 0, #eta_{#Lambda} < 0"); + histos.add("EtaStudy/hCountsVsPhiStar", "FakePolCounts, AEE dependence; #phi^{*};", kTH2D, {axisConfigurations.axisDeltaPhi, {9, 0, 9}}); + histos.get(HIST("EtaStudy/hCountsVsPhiStar"))->GetZaxis()->SetTitle("Counts"); + histos.get(HIST("EtaStudy/hCountsVsPhiStar"))->GetYaxis()->SetBinLabel(1, "All #Lambda"); + histos.get(HIST("EtaStudy/hCountsVsPhiStar"))->GetYaxis()->SetBinLabel(2, "#eta_{Jet} #geq 0"); + histos.get(HIST("EtaStudy/hCountsVsPhiStar"))->GetYaxis()->SetBinLabel(3, "#eta_{Jet} < 0"); + histos.get(HIST("EtaStudy/hCountsVsPhiStar"))->GetYaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/hCountsVsPhiStar"))->GetYaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/hCountsVsPhiStar"))->GetYaxis()->SetBinLabel(6, "#eta_{Jet} #geq 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/hCountsVsPhiStar"))->GetYaxis()->SetBinLabel(7, "#eta_{Jet} #geq 0, #eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/hCountsVsPhiStar"))->GetYaxis()->SetBinLabel(8, "#eta_{Jet} < 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/hCountsVsPhiStar"))->GetYaxis()->SetBinLabel(9, "#eta_{Jet} < 0, #eta_{#Lambda} < 0"); // For the ring observable as well: - histos.add("HelicityEfficiencyQA/pFakePolSignalvsPhiStar", "FakePolSignal, AEE dependence; #phi^{*};", kTProfile2D, {axisConfigurations.axisDeltaPhi, {9, 0, 9}}); - histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiStar"))->GetZaxis()->SetTitle("<#it{R}>"); - histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiStar"))->GetYaxis()->SetBinLabel(1, "All #Lambda"); - histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiStar"))->GetYaxis()->SetBinLabel(2, "#eta_{Jet} #geq 0"); - histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiStar"))->GetYaxis()->SetBinLabel(3, "#eta_{Jet} < 0"); - histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiStar"))->GetYaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); - histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiStar"))->GetYaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); - histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiStar"))->GetYaxis()->SetBinLabel(6, "#eta_{Jet} #geq 0, #eta_{#Lambda} #geq 0"); - histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiStar"))->GetYaxis()->SetBinLabel(7, "#eta_{Jet} #geq 0, #eta_{#Lambda} < 0"); - histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiStar"))->GetYaxis()->SetBinLabel(8, "#eta_{Jet} < 0, #eta_{#Lambda} #geq 0"); - histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiStar"))->GetYaxis()->SetBinLabel(9, "#eta_{Jet} < 0, #eta_{#Lambda} < 0"); + // Explicitly, checking the Phi* dependence on a series of #eta cuts. + // The fake, AEE-induced, signal should be zero when integrating on full solid angle, and then have some shape for each eta slice. + histos.add("EtaStudy/pFakePolSignalvsPhiStar", "FakePolSignal, AEE dependence; #phi^{*};", kTProfile2D, {axisConfigurations.axisDeltaPhi, {9, 0, 9}}); + histos.get(HIST("EtaStudy/pFakePolSignalvsPhiStar"))->GetZaxis()->SetTitle("<#it{R}>"); + histos.get(HIST("EtaStudy/pFakePolSignalvsPhiStar"))->GetYaxis()->SetBinLabel(1, "All #Lambda"); + histos.get(HIST("EtaStudy/pFakePolSignalvsPhiStar"))->GetYaxis()->SetBinLabel(2, "#eta_{Jet} #geq 0"); + histos.get(HIST("EtaStudy/pFakePolSignalvsPhiStar"))->GetYaxis()->SetBinLabel(3, "#eta_{Jet} < 0"); + histos.get(HIST("EtaStudy/pFakePolSignalvsPhiStar"))->GetYaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pFakePolSignalvsPhiStar"))->GetYaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/pFakePolSignalvsPhiStar"))->GetYaxis()->SetBinLabel(6, "#eta_{Jet} #geq 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pFakePolSignalvsPhiStar"))->GetYaxis()->SetBinLabel(7, "#eta_{Jet} #geq 0, #eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/pFakePolSignalvsPhiStar"))->GetYaxis()->SetBinLabel(8, "#eta_{Jet} < 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pFakePolSignalvsPhiStar"))->GetYaxis()->SetBinLabel(9, "#eta_{Jet} < 0, #eta_{#Lambda} < 0"); // For the phi_Lambda - phi_D^* dependency as well: - histos.add("HelicityEfficiencyQA/pFakePolSignalvsPhiLambdaMinusPhiProtonStar", "FakePolSignal, AEE dependence; #phi_{#Lambda} - #phi_{p}^{*};", kTProfile2D, {axisConfigurations.axisDeltaPhi, {9, 0, 9}}); - histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"))->GetZaxis()->SetTitle("<#it{R}>"); - histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"))->GetYaxis()->SetBinLabel(1, "All #Lambda"); - histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"))->GetYaxis()->SetBinLabel(2, "#eta_{Jet} #geq 0"); - histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"))->GetYaxis()->SetBinLabel(3, "#eta_{Jet} < 0"); - histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"))->GetYaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); - histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"))->GetYaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); - histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"))->GetYaxis()->SetBinLabel(6, "#eta_{Jet} #geq 0, #eta_{#Lambda} #geq 0"); - histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"))->GetYaxis()->SetBinLabel(7, "#eta_{Jet} #geq 0, #eta_{#Lambda} < 0"); - histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"))->GetYaxis()->SetBinLabel(8, "#eta_{Jet} < 0, #eta_{#Lambda} #geq 0"); - histos.get(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"))->GetYaxis()->SetBinLabel(9, "#eta_{Jet} < 0, #eta_{#Lambda} < 0"); + histos.add("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar", "FakePolSignal, AEE dependence; #phi_{#Lambda} - #phi_{p}^{*};", kTProfile2D, {axisConfigurations.axisDeltaPhi, {9, 0, 9}}); + histos.get(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"))->GetZaxis()->SetTitle("<#it{R}>"); + histos.get(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"))->GetYaxis()->SetBinLabel(1, "All #Lambda"); + histos.get(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"))->GetYaxis()->SetBinLabel(2, "#eta_{Jet} #geq 0"); + histos.get(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"))->GetYaxis()->SetBinLabel(3, "#eta_{Jet} < 0"); + histos.get(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"))->GetYaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"))->GetYaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"))->GetYaxis()->SetBinLabel(6, "#eta_{Jet} #geq 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"))->GetYaxis()->SetBinLabel(7, "#eta_{Jet} #geq 0, #eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"))->GetYaxis()->SetBinLabel(8, "#eta_{Jet} < 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"))->GetYaxis()->SetBinLabel(9, "#eta_{Jet} < 0, #eta_{#Lambda} < 0"); // More about possible AEE dependencies (should see an invariance with JetEta and /Jz): - // TODO: think about these error bars: do they still make sense via regular TProfile's SEM error? + // TODO: think about these error bars: do they still make sense via regular TProfile's SEM error? These are just a quick check, so wouldn't bother much about it. histos.add("HelicityEfficiencyQA/pRingVsJetZcomponent", "<#it{R}> vs #hat{t}_{z}; #hat{t}_{z}; <#it{R}>", kTProfile, {{40, -1, 1}}); // Numerically stable and can also show the sign flip (essentially the <#it{R}> vs Eta Jet plot in another scale) histos.add("HelicityEfficiencyQA/pRingOverJetZcomponent_VsJetEta", "<#it{R}>/#hat{t}_{z}; #eta_{Jet}; <#it{R}>/#hat{t}_{z}", kTProfile, {axisConfigurations.axisEtaCoarse}); histos.add("HelicityEfficiencyQA/pRingOverJetZcomponent_VsCosThetaHEE", "<#it{R}>/#hat{t}_{z} Vs cos(#theta) HEE; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; <#it{R}>/#hat{t}_{z}", kTProfile, {axisConfigurations.axisCosTheta}); @@ -789,45 +795,54 @@ struct lambdajetpolarizationionsderived { histos.add("HelicityEfficiencyQA/pRingOverJetZcomponent_VsJetEtaVsCosThetaHEE", "<#it{R}>/#hat{t}_{z}; #eta_{Jet}; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; <#it{R}>/#hat{t}_{z}", kTProfile2D, {axisConfigurations.axisEtaCoarse, axisConfigurations.axisCosTheta}); histos.add("HelicityEfficiencyQA/pRingOverJetZcomponent_VsJetEtaVsPhiStar", "<#it{R}>/#hat{t}_{z}; #eta_{Jet}; #phi^{*}; <#it{R}>/#hat{t}_{z}", kTProfile2D, {axisConfigurations.axisEtaCoarse, axisConfigurations.axisDeltaPhi}); + // Seeing if phi* is indeed influenced by the DCA between daughters: + histos.add("HelicityEfficiencyQA/hFakePolCountsJet_PhiStarVsDCAdau", "FakePolCounts - HasValidLeadJet OK; #phi^{*}; DCA_{V0 Daughters}", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDCAdau}); + histos.add("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAdau", "FakePolSignal; #phi^{*}; DCA_{V0 Daughters}", kTProfile2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDCAdau}); + + // Adding a way to check if the jet eta is positive or negative as well + // (AEE signal could be closer to zero otherwise: phi^* dependency may not make it fall to zero as we are no longer integrating in full solid angle, yet analyzing this other dependency is also important) + histos.add("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAdauVsEtaJet", "FakePolSignal; #phi^{*}; DCA_{V0 Daughters}; #eta_{Jet} sign", kTProfile3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDCAdau, {2, -0.9, 0.9}}); + histos.add("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAdauVsEtaLambda", "FakePolSignal; #phi^{*}; DCA_{V0 Daughters}; #eta_{#Lambda} sign", kTProfile3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDCAdau, {2, -0.9, 0.9}}); + // Doing the same HEE study for leading particles: // (eta_{Jet} may be a bad estimator!) - histos.add("HelicityEfficiencyQA/hFakePolCountsLeadP", "FakePolCounts; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda};", kTH2D, {axisConfigurations.axisCosTheta, {9, 0, 9}}); - histos.get(HIST("HelicityEfficiencyQA/hFakePolCountsLeadP"))->GetZaxis()->SetTitle("N_{V0s}"); - histos.get(HIST("HelicityEfficiencyQA/hFakePolCountsLeadP"))->GetYaxis()->SetBinLabel(1, "All #Lambda"); - histos.get(HIST("HelicityEfficiencyQA/hFakePolCountsLeadP"))->GetYaxis()->SetBinLabel(2, "#eta_{LeadP} #geq 0"); - histos.get(HIST("HelicityEfficiencyQA/hFakePolCountsLeadP"))->GetYaxis()->SetBinLabel(3, "#eta_{LeadP} < 0"); - histos.get(HIST("HelicityEfficiencyQA/hFakePolCountsLeadP"))->GetYaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); - histos.get(HIST("HelicityEfficiencyQA/hFakePolCountsLeadP"))->GetYaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); - histos.get(HIST("HelicityEfficiencyQA/hFakePolCountsLeadP"))->GetYaxis()->SetBinLabel(6, "#eta_{LeadP} #geq 0, #eta_{#Lambda} #geq 0"); - histos.get(HIST("HelicityEfficiencyQA/hFakePolCountsLeadP"))->GetYaxis()->SetBinLabel(7, "#eta_{LeadP} #geq 0, #eta_{#Lambda} < 0"); - histos.get(HIST("HelicityEfficiencyQA/hFakePolCountsLeadP"))->GetYaxis()->SetBinLabel(8, "#eta_{LeadP} < 0, #eta_{#Lambda} #geq 0"); - histos.get(HIST("HelicityEfficiencyQA/hFakePolCountsLeadP"))->GetYaxis()->SetBinLabel(9, "#eta_{LeadP} < 0, #eta_{#Lambda} < 0"); + histos.add("EtaStudy/hFakePolCountsLeadP", "FakePolCounts; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda};", kTH2D, {axisConfigurations.axisCosTheta, {9, 0, 9}}); + histos.get(HIST("EtaStudy/hFakePolCountsLeadP"))->GetZaxis()->SetTitle("N_{V0s}"); + histos.get(HIST("EtaStudy/hFakePolCountsLeadP"))->GetYaxis()->SetBinLabel(1, "All #Lambda"); + histos.get(HIST("EtaStudy/hFakePolCountsLeadP"))->GetYaxis()->SetBinLabel(2, "#eta_{LeadP} #geq 0"); + histos.get(HIST("EtaStudy/hFakePolCountsLeadP"))->GetYaxis()->SetBinLabel(3, "#eta_{LeadP} < 0"); + histos.get(HIST("EtaStudy/hFakePolCountsLeadP"))->GetYaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/hFakePolCountsLeadP"))->GetYaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/hFakePolCountsLeadP"))->GetYaxis()->SetBinLabel(6, "#eta_{LeadP} #geq 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/hFakePolCountsLeadP"))->GetYaxis()->SetBinLabel(7, "#eta_{LeadP} #geq 0, #eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/hFakePolCountsLeadP"))->GetYaxis()->SetBinLabel(8, "#eta_{LeadP} < 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/hFakePolCountsLeadP"))->GetYaxis()->SetBinLabel(9, "#eta_{LeadP} < 0, #eta_{#Lambda} < 0"); // Avoid fake signal by jets boosting the Lambda in its own direction, then modifying efficiency of reconstruction in a similar way: - histos.add("HelicityEfficiencyQA/hFakePolCountsLambdaPtCut", "FakePol,p_{T}^{#Lambda}#in[0.5,1.5]; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda};", kTH2D, {axisConfigurations.axisCosTheta, {9, 0, 9}}); - histos.get(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtCut"))->GetZaxis()->SetTitle("N_{V0s}"); - histos.get(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtCut"))->GetYaxis()->SetBinLabel(1, "All #Lambda"); - histos.get(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtCut"))->GetYaxis()->SetBinLabel(2, "#eta_{Jet} #geq 0"); - histos.get(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtCut"))->GetYaxis()->SetBinLabel(3, "#eta_{Jet} < 0"); - histos.get(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtCut"))->GetYaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); - histos.get(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtCut"))->GetYaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); - histos.get(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtCut"))->GetYaxis()->SetBinLabel(6, "#eta_{Jet} #geq 0, #eta_{#Lambda} #geq 0"); - histos.get(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtCut"))->GetYaxis()->SetBinLabel(7, "#eta_{Jet} #geq 0, #eta_{#Lambda} < 0"); - histos.get(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtCut"))->GetYaxis()->SetBinLabel(8, "#eta_{Jet} < 0, #eta_{#Lambda} #geq 0"); - histos.get(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtCut"))->GetYaxis()->SetBinLabel(9, "#eta_{Jet} < 0, #eta_{#Lambda} < 0"); + histos.add("EtaStudy/hFakePolCountsLambdaPtCut", "FakePol,p_{T}^{#Lambda}#in[0.5,1.5]; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda};", kTH2D, {axisConfigurations.axisCosTheta, {9, 0, 9}}); + histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtCut"))->GetZaxis()->SetTitle("N_{V0s}"); + histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtCut"))->GetYaxis()->SetBinLabel(1, "All #Lambda"); + histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtCut"))->GetYaxis()->SetBinLabel(2, "#eta_{Jet} #geq 0"); + histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtCut"))->GetYaxis()->SetBinLabel(3, "#eta_{Jet} < 0"); + histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtCut"))->GetYaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtCut"))->GetYaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtCut"))->GetYaxis()->SetBinLabel(6, "#eta_{Jet} #geq 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtCut"))->GetYaxis()->SetBinLabel(7, "#eta_{Jet} #geq 0, #eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtCut"))->GetYaxis()->SetBinLabel(8, "#eta_{Jet} < 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtCut"))->GetYaxis()->SetBinLabel(9, "#eta_{Jet} < 0, #eta_{#Lambda} < 0"); // Even stricter cut (also demands rapidity cut stricter than jets, so may see different boosting): - histos.add("HelicityEfficiencyQA/hFakePolCountsLambdaPtYCuts", "FakePol,p_{T}^{#Lambda}#in[0.5,1.5],|y_{#Lambda}|<0.5; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda};", kTH2D, {axisConfigurations.axisCosTheta, {9, 0, 9}}); - histos.get(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtYCuts"))->GetZaxis()->SetTitle("N_{V0s}"); - histos.get(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtYCuts"))->GetYaxis()->SetBinLabel(1, "All #Lambda"); - histos.get(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtYCuts"))->GetYaxis()->SetBinLabel(2, "#eta_{Jet} #geq 0"); - histos.get(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtYCuts"))->GetYaxis()->SetBinLabel(3, "#eta_{Jet} < 0"); - histos.get(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtYCuts"))->GetYaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); - histos.get(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtYCuts"))->GetYaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); - histos.get(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtYCuts"))->GetYaxis()->SetBinLabel(6, "#eta_{Jet} #geq 0, #eta_{#Lambda} #geq 0"); - histos.get(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtYCuts"))->GetYaxis()->SetBinLabel(7, "#eta_{Jet} #geq 0, #eta_{#Lambda} < 0"); - histos.get(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtYCuts"))->GetYaxis()->SetBinLabel(8, "#eta_{Jet} < 0, #eta_{#Lambda} #geq 0"); - histos.get(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtYCuts"))->GetYaxis()->SetBinLabel(9, "#eta_{Jet} < 0, #eta_{#Lambda} < 0"); + histos.add("EtaStudy/hFakePolCountsLambdaPtYCuts", "FakePol,p_{T}^{#Lambda}#in[0.5,1.5],|y_{#Lambda}|<0.5; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda};", kTH2D, {axisConfigurations.axisCosTheta, {9, 0, 9}}); + histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"))->GetZaxis()->SetTitle("N_{V0s}"); + histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"))->GetYaxis()->SetBinLabel(1, "All #Lambda"); + histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"))->GetYaxis()->SetBinLabel(2, "#eta_{Jet} #geq 0"); + histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"))->GetYaxis()->SetBinLabel(3, "#eta_{Jet} < 0"); + histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"))->GetYaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"))->GetYaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"))->GetYaxis()->SetBinLabel(6, "#eta_{Jet} #geq 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"))->GetYaxis()->SetBinLabel(7, "#eta_{Jet} #geq 0, #eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"))->GetYaxis()->SetBinLabel(8, "#eta_{Jet} < 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"))->GetYaxis()->SetBinLabel(9, "#eta_{Jet} < 0, #eta_{#Lambda} < 0"); // Another useful quantity -- How much is the fake signal related to the jet's momentum (how much the fake signal is correlated with the Jet-Lambda angular separation): histos.add("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaJet", "FakePolCounts; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; #Delta#theta_{Jet}", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisDeltaTheta}); @@ -954,14 +969,11 @@ struct lambdajetpolarizationionsderived { // continue; // Apply minimum pT selection for the leading particle (not necessarily the same as in derived data builder. Can be a stricter cut!): - const bool hasValidLeadingP = leadPPt > minLeadParticlePt; + bool hasValidLeadingP = leadPPt > minLeadParticlePt; // Build leading particle unit vector, outside the V0 loop for performance. XYZVector leadPUnitVec(1., 0., 0.); // dummy (overwritten below when hasValidLeadingP) if (hasValidLeadingP) { - histos.fill(HIST("JetKinematicsQA/hLeadPEta"), leadPEta); - histos.fill(HIST("JetKinematicsQA/hJetCounterPtLeadP"), leadPPt); - leadPUnitVec = XYZVector(leadPPx, leadPPy, leadPPz).Unit(); // QA: same direction-smearing/perp logic as for the leading jet estimator. // The hLeadPEta histogram above intentionally uses the unmodified direction. @@ -981,7 +993,35 @@ struct lambdajetpolarizationionsderived { XYZVector smearAxis = perpVec * std::cos(smearAzimuth) + leadPUnitVec.Cross(perpVec) * std::sin(smearAzimuth); double smearAngle = std::abs(randomGen.Gaus(0., 0.05 * jetR)); leadPUnitVec = leadPUnitVec * std::cos(smearAngle) + smearAxis.Cross(leadPUnitVec) * std::sin(smearAngle); + } else if (forceRandJet){ + // This randomization was made different for each proxy (LeadP, LeadJet, SubLeadJet): bear that in mind! + // 1) Uniformly sample cos(theta) and phi to ensure an isotropic distribution (could also use TRandom::Sphere as well) + // Notice that uniformly sampling theta would make the distribution non-isotropic, thus we use cos(theta)! + double cosTheta = randomGen.Uniform(-1., 1.); + double sinTheta = std::sqrt(1. - cosTheta * cosTheta); + double randPhi = randomGen.Uniform(0., o2::constants::math::TwoPI); + + // 2) Construct the new random unit vector (there is no need to use the magnitude at all! We only need direction here): + leadPUnitVec = XYZVector(sinTheta * std::cos(randPhi), sinTheta * std::sin(randPhi), cosTheta); } + // Recalculating pT, phi and eta after distortions: + // (without this, later kinematic selections make no sense at all! In the forceRandJet case, the ring observable would always sum zero) + // Calculating total jet momentum, which should be preserved, just to recalculate the new jet Pt: + double leadPMag = leadPPt * std::cosh(leadPEta); + // Recalculate pT: + double transverseNorm = std::max(leadPUnitVec.Rho(), 1e-12); // For stability (Rho is the projection on the transverse plane, badly behaved for high |eta|) + leadPPt = leadPMag * transverseNorm; + // Recalculate phi: + leadPPhi = std::atan2(leadPUnitVec.Y(), leadPUnitVec.X()); + // Stable eta computation: + leadPEta = std::asinh(leadPUnitVec.Z() / transverseNorm); // Stabler than 0.5 * std::log((1. + cosTheta) / (1. - cosTheta)) + + // Filling histograms after distortions: + histos.fill(HIST("JetKinematicsQA/hLeadPEta"), leadPEta); + histos.fill(HIST("JetKinematicsQA/hJetCounterPtLeadP"), leadPPt); + + // Recalculate the bool after distortion to see if we proceed with this jet proxy: + hasValidLeadingP = leadPPt > minLeadParticlePt; } // 3) Checking if the event has a leading jet: @@ -1009,8 +1049,8 @@ struct lambdajetpolarizationionsderived { // Some useful bools to check if we have a leading jet and a subleading jet: // const bool hasValidLeadingJet = leadingJetPt > 0.; - const bool hasValidLeadingJet = leadingJetPt > minLeadJetPt; // Finer control on jet momentum - const bool hasValidSubJet = subleadingJetPt > minSubLeadJetPt; + bool hasValidLeadingJet = leadingJetPt > minLeadJetPt; // Finer control on jet momentum + bool hasValidSubJet = subleadingJetPt > minSubLeadJetPt; // Build jet vectors (only when the corresponding jet exists): // Dummy initialisations are safe: all jet-dependent fills are gated on hasValidLeadingJet / hasValidSubJet. @@ -1022,9 +1062,7 @@ struct lambdajetpolarizationionsderived { leadingJetPhi = leadingJet->jetPhi(); // Using internal getters to make code cleaner: leadingJetUnitVec = XYZVector(leadingJet->jetPx(), leadingJet->jetPy(), leadingJet->jetPz()).Unit(); - histos.fill(HIST("JetKinematicsQA/hLeadJetEta"), leadingJetEta); // This will not be subject to the forcePerpToJet nor the forceJetDirectionSmudge QAs, for simplicity - histos.fill(HIST("JetKinematicsQA/hJetCounterPtJet"), leadingJetPt); - + // QA block -- Purposefully changing the jet direction (should kill signal, if any): if (forcePerpToJet) { // Use modified jet direction (done outside loop to guarantee all V0s inside event use same fake jet) // First, we build a vector perpendicular to the jet by picking an arbitrary vector not parallel to the jet @@ -1071,6 +1109,34 @@ struct lambdajetpolarizationionsderived { leadingJetUnitVec = leadingJetUnitVec * std::cos(smearAngle) + smearAxis.Cross(leadingJetUnitVec) * std::sin(smearAngle); // Also, rotation preserves the norm, so no re-normalisation is needed for this to be a unit vector. } + else if (forceRandJet){ + // Uniformly sample cos(theta) and phi to ensure an isotropic distribution + // Notice that uniformly sampling theta would make the distribution non-isotropic, thus we use cos(theta)! + double cosTheta = randomGen.Uniform(-1., 1.); + double sinTheta = std::sqrt(1. - cosTheta * cosTheta); + double randPhi = randomGen.Uniform(0., o2::constants::math::TwoPI); + + // Construct the new random unit vector (there is no need to use the magnitude at all! We only need direction here): + leadingJetUnitVec = XYZVector(sinTheta * std::cos(randPhi), sinTheta * std::sin(randPhi), cosTheta); + } + // Recalculating pT, phi and eta after distortions: + // (without this, later kinematic selections make no sense at all! In the forceRandJet case, the ring observable would always sum zero) + // Calculating total jet momentum, which should be preserved, just to recalculate the new jet Pt: + double leadingJetMag = leadingJetPt * std::cosh(leadingJetEta); + // Recalculate pT: + double transverseNorm = std::max(leadingJetUnitVec.Rho(), 1e-12); // For stability (Rho is the projection on the transverse plane, badly behaved for high |eta|) + leadingJetPt = leadingJetMag * transverseNorm; + // Recalculate phi: + leadingJetPhi = std::atan2(leadingJetUnitVec.Y(), leadingJetUnitVec.X()); + // Stable eta computation: + leadingJetEta = std::asinh(leadingJetUnitVec.Z() / transverseNorm); // Stabler than 0.5 * std::log((1. + cosTheta) / (1. - cosTheta)) + + // Filling histograms after distortions: + histos.fill(HIST("JetKinematicsQA/hLeadJetEta"), leadingJetEta); + histos.fill(HIST("JetKinematicsQA/hJetCounterPtJet"), leadingJetPt); + + // Recalculate the bool after distortion to see if we proceed with this jet proxy: + hasValidLeadingJet = leadingJetPt > minLeadJetPt; } float subleadingJetEta = 0.; @@ -1100,6 +1166,34 @@ struct lambdajetpolarizationionsderived { double smearAngle = std::abs(randomGen.Gaus(0., 0.05 * jetR)); subJetUnitVec = subJetUnitVec * std::cos(smearAngle) + smearAxis.Cross(subJetUnitVec) * std::sin(smearAngle); } + else if (forceRandJet){ + // Uniformly sample cos(theta) and phi to ensure an isotropic distribution + // Notice that uniformly sampling theta would make the distribution non-isotropic, thus we use cos(theta)! + double cosTheta = randomGen.Uniform(-1., 1.); + double sinTheta = std::sqrt(1. - cosTheta * cosTheta); + double randPhi = randomGen.Uniform(0., o2::constants::math::TwoPI); + + // Construct the new random unit vector (there is no need to use the magnitude at all! We only need direction here): + subJetUnitVec = XYZVector(sinTheta * std::cos(randPhi), sinTheta * std::sin(randPhi), cosTheta); + } + // Recalculating pT, phi and eta after distortions: + // (without this, later kinematic selections make no sense at all! In the forceRandJet case, the ring observable would always sum zero) + // Calculating total jet momentum, which should be preserved, just to recalculate the new jet Pt: + double subleadingJetMag = subleadingJetPt * std::cosh(subleadingJetEta); + // Recalculate pT: + double transverseNorm = std::max(subJetUnitVec.Rho(), 1e-12); // For stability (Rho is the projection on the transverse plane, badly behaved for high |eta|) + subleadingJetPt = subleadingJetMag * transverseNorm; + // Recalculate phi: + subleadingJetPhi = std::atan2(subJetUnitVec.Y(), subJetUnitVec.X()); + // Stable eta computation: + subleadingJetEta = std::asinh(subJetUnitVec.Z() / transverseNorm); // Stabler than 0.5 * std::log((1. + cosTheta) / (1. - cosTheta)) + + // Filling histograms after distortions: + histos.fill(HIST("JetKinematicsQA/hSubLeadJetEta"), subleadingJetEta); + histos.fill(HIST("JetKinematicsQA/hJetCounterPt2ndJet"), subleadingJetPt); + + // Recalculate the bool after distortion to see if we proceed with this jet proxy: + hasValidSubJet = subleadingJetPt > minSubLeadJetPt; } // (jet eta cuts only meaningful when the jet actually exists) @@ -1144,6 +1238,7 @@ struct lambdajetpolarizationionsderived { const float v0pt = v0.v0Pt(); const float v0eta = v0.v0Eta(); const float v0phi = v0.v0Phi(); + const float dcaDau = v0.dcaV0Daughters(); float v0LambdaLikeMass = 0; // Initialized just to catch any stray behavior float protonLikePt = 0; @@ -1167,7 +1262,7 @@ struct lambdajetpolarizationionsderived { PtEtaPhiMVector lambdaLike4Vec(v0pt, v0eta, v0phi, v0LambdaLikeMass); PtEtaPhiMVector protonLike4Vec(protonLikePt, protonLikeEta, protonLikePhi, protonMass); - float lambdaRapidity = lambdaLike4Vec.Rapidity(); // For further kinematic selections + const float lambdaRapidity = lambdaLike4Vec.Rapidity(); // For further kinematic selections // Boosting proton into lambda frame: XYZVector beta = lambdaLike4Vec.BoostToCM(); // Boost trivector that goes from laboratory frame to Lambda's rest frame (convenient new function, different from TLorentzVector's BoostVector()) @@ -1375,6 +1470,12 @@ struct lambdajetpolarizationionsderived { histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsJet_CosThetaVsPhiStar"), cosFakePol, phiStar); histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignal_CosThetaVsPhiStar"), cosFakePol, phiStar, ringObservable); + // AEE and DCA between daughters correlation: + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsJet_PhiStarVsDCAdau"), phiStar, dcaDau); + histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAdau"), phiStar, dcaDau, ringObservable); + histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAdauVsEtaJet"), phiStar, dcaDau, leadingJetEta, ringObservable); + histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAdauVsEtaLambda"), phiStar, dcaDau, v0eta, ringObservable); + histos.fill(HIST("HelicityEfficiencyQA/pRingVsJetZcomponent"), Jz, ringObservable); histos.fill(HIST("HelicityEfficiencyQA/pRingOverJetZcomponent_VsJetEta"), leadingJetEta, ringObservableOverJetZ); histos.fill(HIST("HelicityEfficiencyQA/pRingOverJetZcomponent_VsCosThetaHEE"), cosFakePol, ringObservableOverJetZ); @@ -1390,147 +1491,147 @@ struct lambdajetpolarizationionsderived { // Filling eta dependence QAs of the result (both for V0 and jet proxy): if (hasValidLeadingJet) { - histos.fill(HIST("ProxyEta/pRingEtaCuts"), 0, ringObservable); - histos.fill(HIST("ProxyEta/pRingEtaCuts"), lambdaEtaPos ? 3 : 4, ringObservable); + histos.fill(HIST("EtaStudy/pRingEtaCuts"), 0, ringObservable); + histos.fill(HIST("EtaStudy/pRingEtaCuts"), lambdaEtaPos ? 3 : 4, ringObservable); // HEE study (helicity efficiency effect): - histos.fill(HIST("HelicityEfficiencyQA/hFakePolCounts"), cosFakePol, 0); - histos.fill(HIST("HelicityEfficiencyQA/hFakePolCounts"), cosFakePol, lambdaEtaPos ? 3 : 4); + histos.fill(HIST("EtaStudy/hFakePolCounts"), cosFakePol, 0); + histos.fill(HIST("EtaStudy/hFakePolCounts"), cosFakePol, lambdaEtaPos ? 3 : 4); // Same for signal: - histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalVsCosTheta"), cosFakePol, 0, ringObservable); - histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalVsCosTheta"), cosFakePol, lambdaEtaPos ? 3 : 4, ringObservable); + histos.fill(HIST("EtaStudy/pFakePolSignalVsCosTheta"), cosFakePol, 0, ringObservable); + histos.fill(HIST("EtaStudy/pFakePolSignalVsCosTheta"), cosFakePol, lambdaEtaPos ? 3 : 4, ringObservable); // Counter and ring accumulators for AEE study: - histos.fill(HIST("HelicityEfficiencyQA/hCountsVsPhiStar"), phiStar, 0); - histos.fill(HIST("HelicityEfficiencyQA/hCountsVsPhiStar"), phiStar, lambdaEtaPos ? 3 : 4); - histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiStar"), phiStar, 0, ringObservable); - histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiStar"), phiStar, lambdaEtaPos ? 3 : 4, ringObservable); + histos.fill(HIST("EtaStudy/hCountsVsPhiStar"), phiStar, 0); + histos.fill(HIST("EtaStudy/hCountsVsPhiStar"), phiStar, lambdaEtaPos ? 3 : 4); + histos.fill(HIST("EtaStudy/pFakePolSignalvsPhiStar"), phiStar, 0, ringObservable); + histos.fill(HIST("EtaStudy/pFakePolSignalvsPhiStar"), phiStar, lambdaEtaPos ? 3 : 4, ringObservable); - histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"), deltaPhiLambdaProtonStar, 0, ringObservable); - histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"), deltaPhiLambdaProtonStar, lambdaEtaPos ? 3 : 4, ringObservable); + histos.fill(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"), deltaPhiLambdaProtonStar, 0, ringObservable); + histos.fill(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"), deltaPhiLambdaProtonStar, lambdaEtaPos ? 3 : 4, ringObservable); // Extra correlations test: histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaJet"), cosFakePol, deltaThetaJet); histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsCosThetaVsPtForJets"), cosFakePol, v0pt); if (pTLambdaCheck) { - histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtCut"), cosFakePol, 0); - histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtCut"), cosFakePol, lambdaEtaPos ? 3 : 4); + histos.fill(HIST("EtaStudy/hFakePolCountsLambdaPtCut"), cosFakePol, 0); + histos.fill(HIST("EtaStudy/hFakePolCountsLambdaPtCut"), cosFakePol, lambdaEtaPos ? 3 : 4); if (rapidityLambdaCheck){ // Stricter check - histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtYCuts"), cosFakePol, 0); - histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtYCuts"), cosFakePol, lambdaEtaPos ? 3 : 4); + histos.fill(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"), cosFakePol, 0); + histos.fill(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"), cosFakePol, lambdaEtaPos ? 3 : 4); } } if (jetEtaPos) { // Less readable than "if ( jetEtaPos && lambdaEtaPos)", yet more efficient - histos.fill(HIST("ProxyEta/pRingEtaCuts"), 1, ringObservable); - histos.fill(HIST("ProxyEta/pRingEtaCuts"), lambdaEtaPos ? 5 : 6, ringObservable); + histos.fill(HIST("EtaStudy/pRingEtaCuts"), 1, ringObservable); + histos.fill(HIST("EtaStudy/pRingEtaCuts"), lambdaEtaPos ? 5 : 6, ringObservable); - histos.fill(HIST("HelicityEfficiencyQA/hFakePolCounts"), cosFakePol, 1); - histos.fill(HIST("HelicityEfficiencyQA/hFakePolCounts"), cosFakePol, lambdaEtaPos ? 5 : 6); + histos.fill(HIST("EtaStudy/hFakePolCounts"), cosFakePol, 1); + histos.fill(HIST("EtaStudy/hFakePolCounts"), cosFakePol, lambdaEtaPos ? 5 : 6); // Same for signal: - histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalVsCosTheta"), cosFakePol, 1, ringObservable); - histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalVsCosTheta"), cosFakePol, lambdaEtaPos ? 5 : 6, ringObservable); + histos.fill(HIST("EtaStudy/pFakePolSignalVsCosTheta"), cosFakePol, 1, ringObservable); + histos.fill(HIST("EtaStudy/pFakePolSignalVsCosTheta"), cosFakePol, lambdaEtaPos ? 5 : 6, ringObservable); // Counter and ring accumulators for AEE study: - histos.fill(HIST("HelicityEfficiencyQA/hCountsVsPhiStar"), phiStar, 1); - histos.fill(HIST("HelicityEfficiencyQA/hCountsVsPhiStar"), phiStar, lambdaEtaPos ? 5 : 6); - histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiStar"), phiStar, 1, ringObservable); - histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiStar"), phiStar, lambdaEtaPos ? 5 : 6, ringObservable); + histos.fill(HIST("EtaStudy/hCountsVsPhiStar"), phiStar, 1); + histos.fill(HIST("EtaStudy/hCountsVsPhiStar"), phiStar, lambdaEtaPos ? 5 : 6); + histos.fill(HIST("EtaStudy/pFakePolSignalvsPhiStar"), phiStar, 1, ringObservable); + histos.fill(HIST("EtaStudy/pFakePolSignalvsPhiStar"), phiStar, lambdaEtaPos ? 5 : 6, ringObservable); - histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"), deltaPhiLambdaProtonStar, 1, ringObservable); - histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"), deltaPhiLambdaProtonStar, lambdaEtaPos ? 5 : 6, ringObservable); + histos.fill(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"), deltaPhiLambdaProtonStar, 1, ringObservable); + histos.fill(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"), deltaPhiLambdaProtonStar, lambdaEtaPos ? 5 : 6, ringObservable); histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaJetPosEta"), cosFakePol, deltaThetaJet); if (pTLambdaCheck) { - histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtCut"), cosFakePol, 1); - histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtCut"), cosFakePol, lambdaEtaPos ? 5 : 6); + histos.fill(HIST("EtaStudy/hFakePolCountsLambdaPtCut"), cosFakePol, 1); + histos.fill(HIST("EtaStudy/hFakePolCountsLambdaPtCut"), cosFakePol, lambdaEtaPos ? 5 : 6); if (rapidityLambdaCheck){ // Stricter check - histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtYCuts"), cosFakePol, 1); - histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtYCuts"), cosFakePol, lambdaEtaPos ? 5 : 6); + histos.fill(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"), cosFakePol, 1); + histos.fill(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"), cosFakePol, lambdaEtaPos ? 5 : 6); } } if (jetEtaStrict) { // eta_{Jet} >= R - histos.fill(HIST("ProxyEta/pRingEtaCuts"), 9, ringObservable); - histos.fill(HIST("ProxyEta/pRingEtaCuts"), lambdaEtaPos ? 11 : 12, ringObservable); + histos.fill(HIST("EtaStudy/pRingEtaCuts"), 9, ringObservable); + histos.fill(HIST("EtaStudy/pRingEtaCuts"), lambdaEtaPos ? 11 : 12, ringObservable); } } else { - histos.fill(HIST("ProxyEta/pRingEtaCuts"), 2, ringObservable); - histos.fill(HIST("ProxyEta/pRingEtaCuts"), lambdaEtaPos ? 7 : 8, ringObservable); + histos.fill(HIST("EtaStudy/pRingEtaCuts"), 2, ringObservable); + histos.fill(HIST("EtaStudy/pRingEtaCuts"), lambdaEtaPos ? 7 : 8, ringObservable); - histos.fill(HIST("HelicityEfficiencyQA/hFakePolCounts"), cosFakePol, 2); - histos.fill(HIST("HelicityEfficiencyQA/hFakePolCounts"), cosFakePol, lambdaEtaPos ? 7 : 8); + histos.fill(HIST("EtaStudy/hFakePolCounts"), cosFakePol, 2); + histos.fill(HIST("EtaStudy/hFakePolCounts"), cosFakePol, lambdaEtaPos ? 7 : 8); // Same for signal: - histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalVsCosTheta"), cosFakePol, 2, ringObservable); - histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalVsCosTheta"), cosFakePol, lambdaEtaPos ? 7 : 8, ringObservable); + histos.fill(HIST("EtaStudy/pFakePolSignalVsCosTheta"), cosFakePol, 2, ringObservable); + histos.fill(HIST("EtaStudy/pFakePolSignalVsCosTheta"), cosFakePol, lambdaEtaPos ? 7 : 8, ringObservable); // Counter and ring accumulators for AEE study: - histos.fill(HIST("HelicityEfficiencyQA/hCountsVsPhiStar"), phiStar, 2); - histos.fill(HIST("HelicityEfficiencyQA/hCountsVsPhiStar"), phiStar, lambdaEtaPos ? 7 : 8); - histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiStar"), phiStar, 2, ringObservable); - histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiStar"), phiStar, lambdaEtaPos ? 7 : 8, ringObservable); + histos.fill(HIST("EtaStudy/hCountsVsPhiStar"), phiStar, 2); + histos.fill(HIST("EtaStudy/hCountsVsPhiStar"), phiStar, lambdaEtaPos ? 7 : 8); + histos.fill(HIST("EtaStudy/pFakePolSignalvsPhiStar"), phiStar, 2, ringObservable); + histos.fill(HIST("EtaStudy/pFakePolSignalvsPhiStar"), phiStar, lambdaEtaPos ? 7 : 8, ringObservable); - histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"), deltaPhiLambdaProtonStar, 2, ringObservable); - histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"), deltaPhiLambdaProtonStar, lambdaEtaPos ? 7 : 8, ringObservable); + histos.fill(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"), deltaPhiLambdaProtonStar, 2, ringObservable); + histos.fill(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"), deltaPhiLambdaProtonStar, lambdaEtaPos ? 7 : 8, ringObservable); histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaJetNegEta"), cosFakePol, deltaThetaJet); if (pTLambdaCheck) { - histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtCut"), cosFakePol, 2); - histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtCut"), cosFakePol, lambdaEtaPos ? 7 : 8); + histos.fill(HIST("EtaStudy/hFakePolCountsLambdaPtCut"), cosFakePol, 2); + histos.fill(HIST("EtaStudy/hFakePolCountsLambdaPtCut"), cosFakePol, lambdaEtaPos ? 7 : 8); if (rapidityLambdaCheck){ // Stricter check - histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtYCuts"), cosFakePol, 2); - histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsLambdaPtYCuts"), cosFakePol, lambdaEtaPos ? 7 : 8); + histos.fill(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"), cosFakePol, 2); + histos.fill(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"), cosFakePol, lambdaEtaPos ? 7 : 8); } } if (jetEtaStrict) { // eta_{Jet} <= -R - histos.fill(HIST("ProxyEta/pRingEtaCuts"), 10, ringObservable); - histos.fill(HIST("ProxyEta/pRingEtaCuts"), lambdaEtaPos ? 13 : 14, ringObservable); + histos.fill(HIST("EtaStudy/pRingEtaCuts"), 10, ringObservable); + histos.fill(HIST("EtaStudy/pRingEtaCuts"), lambdaEtaPos ? 13 : 14, ringObservable); } } } if (hasValidSubJet) { - histos.fill(HIST("ProxyEta/pRingEtaCutsSubLeadingJet"), 0, ringObservable2ndJet); - histos.fill(HIST("ProxyEta/pRingEtaCutsSubLeadingJet"), lambdaEtaPos ? 3 : 4, ringObservable2ndJet); + histos.fill(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"), 0, ringObservable2ndJet); + histos.fill(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"), lambdaEtaPos ? 3 : 4, ringObservable2ndJet); if (subJetEtaPos) { - histos.fill(HIST("ProxyEta/pRingEtaCutsSubLeadingJet"), 1, ringObservable2ndJet); - histos.fill(HIST("ProxyEta/pRingEtaCutsSubLeadingJet"), lambdaEtaPos ? 5 : 6, ringObservable2ndJet); + histos.fill(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"), 1, ringObservable2ndJet); + histos.fill(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"), lambdaEtaPos ? 5 : 6, ringObservable2ndJet); if (subJetEtaStrict) { // eta_{SubJet} >= R - histos.fill(HIST("ProxyEta/pRingEtaCutsSubLeadingJet"), 9, ringObservable2ndJet); - histos.fill(HIST("ProxyEta/pRingEtaCutsSubLeadingJet"), lambdaEtaPos ? 11 : 12, ringObservable2ndJet); + histos.fill(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"), 9, ringObservable2ndJet); + histos.fill(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"), lambdaEtaPos ? 11 : 12, ringObservable2ndJet); } } else { - histos.fill(HIST("ProxyEta/pRingEtaCutsSubLeadingJet"), 2, ringObservable2ndJet); - histos.fill(HIST("ProxyEta/pRingEtaCutsSubLeadingJet"), lambdaEtaPos ? 7 : 8, ringObservable2ndJet); + histos.fill(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"), 2, ringObservable2ndJet); + histos.fill(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"), lambdaEtaPos ? 7 : 8, ringObservable2ndJet); if (subJetEtaStrict) { // eta_{SubJet} <= -R - histos.fill(HIST("ProxyEta/pRingEtaCutsSubLeadingJet"), 10, ringObservable2ndJet); - histos.fill(HIST("ProxyEta/pRingEtaCutsSubLeadingJet"), lambdaEtaPos ? 13 : 14, ringObservable2ndJet); + histos.fill(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"), 10, ringObservable2ndJet); + histos.fill(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"), lambdaEtaPos ? 13 : 14, ringObservable2ndJet); } } } if (hasValidLeadingP) { - histos.fill(HIST("ProxyEta/pRingEtaCutsLeadingP"), 0, ringObservableLeadP); - histos.fill(HIST("ProxyEta/pRingEtaCutsLeadingP"), lambdaEtaPos ? 3 : 4, ringObservableLeadP); + histos.fill(HIST("EtaStudy/pRingEtaCutsLeadingP"), 0, ringObservableLeadP); + histos.fill(HIST("EtaStudy/pRingEtaCutsLeadingP"), lambdaEtaPos ? 3 : 4, ringObservableLeadP); - histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsLeadP"), cosFakePol, 0); - histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsLeadP"), cosFakePol, lambdaEtaPos ? 3 : 4); + histos.fill(HIST("EtaStudy/hFakePolCountsLeadP"), cosFakePol, 0); + histos.fill(HIST("EtaStudy/hFakePolCountsLeadP"), cosFakePol, lambdaEtaPos ? 3 : 4); histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsCosThetaVsPtForLeadP"), cosFakePol, v0pt); // Understanding the population of events that has a leading particle (even though this does not need one to be calculated!) histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaLeadP"), cosFakePol, deltaThetaLeadP); // Extra correlations test: - histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsLeadP"), cosFakePol, deltaThetaLeadP); + // histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsLeadP"), cosFakePol, deltaThetaLeadP); if (leadPEtaPos) { - histos.fill(HIST("ProxyEta/pRingEtaCutsLeadingP"), 1, ringObservableLeadP); - histos.fill(HIST("ProxyEta/pRingEtaCutsLeadingP"), lambdaEtaPos ? 5 : 6, ringObservableLeadP); + histos.fill(HIST("EtaStudy/pRingEtaCutsLeadingP"), 1, ringObservableLeadP); + histos.fill(HIST("EtaStudy/pRingEtaCutsLeadingP"), lambdaEtaPos ? 5 : 6, ringObservableLeadP); - histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsLeadP"), cosFakePol, 1); - histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsLeadP"), cosFakePol, lambdaEtaPos ? 5 : 6); + histos.fill(HIST("EtaStudy/hFakePolCountsLeadP"), cosFakePol, 1); + histos.fill(HIST("EtaStudy/hFakePolCountsLeadP"), cosFakePol, lambdaEtaPos ? 5 : 6); histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaLeadPPosEta"), cosFakePol, deltaThetaLeadP); } else { - histos.fill(HIST("ProxyEta/pRingEtaCutsLeadingP"), 2, ringObservableLeadP); - histos.fill(HIST("ProxyEta/pRingEtaCutsLeadingP"), lambdaEtaPos ? 7 : 8, ringObservableLeadP); + histos.fill(HIST("EtaStudy/pRingEtaCutsLeadingP"), 2, ringObservableLeadP); + histos.fill(HIST("EtaStudy/pRingEtaCutsLeadingP"), lambdaEtaPos ? 7 : 8, ringObservableLeadP); - histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsLeadP"), cosFakePol, 2); - histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsLeadP"), cosFakePol, lambdaEtaPos ? 7 : 8); + histos.fill(HIST("EtaStudy/hFakePolCountsLeadP"), cosFakePol, 2); + histos.fill(HIST("EtaStudy/hFakePolCountsLeadP"), cosFakePol, lambdaEtaPos ? 7 : 8); histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaLeadPNegEta"), cosFakePol, deltaThetaLeadP); } From 1007d64f66b177bd3e09f3ceb27133518582271d Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Thu, 14 May 2026 19:25:27 +0000 Subject: [PATCH 12/31] - Refactoring all jet distortion QAs into a single function. - Adding a new QA where we do a very naive event mixing: cache the proxy from the previous collision to calculate spurious AEE effects. - Adding new ring observable plots wrt the Lambda being in the mass peak or outside the mass peak, to understand how the integrated ring may change between background and signal --- .../lambdaJetPolarizationIonsDerived.cxx | 339 +++++++++--------- 1 file changed, 172 insertions(+), 167 deletions(-) diff --git a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx index 777193ba517..c2932161e83 100644 --- a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx @@ -305,6 +305,7 @@ struct lambdajetpolarizationionsderived { Configurable forcePerpToJet{"forcePerpToJet", false, "force jet direction to be perpendicular to jet estimator. For QA"}; Configurable forceJetDirectionSmudge{"forceJetDirectionSmudge", false, "fluctuate jet direction by 10% of R around original axis. For QA (tests sensibility)"}; Configurable forceRandJet{"forceRandJet", false, "makes jet direction random. A QA for AEE fake signal and its removal"}; + Configurable forcePreviousJet{"forcePreviousJet", false, "uses previous event's jet direction instead of a random sample. A baseline for fake signal removal"}; // Configurable jetRForSmudging{"jetRForSmudging", 0.4, "QA quantity: the chosen R scale for the jet direction smudge"}; // Superseeded by jetR: kept the same scale in analysis and QA Configurable jetR{"jetR", 0.4f, "Radius of the jet"}; // Provide manually, please. Configurable minLeadParticlePt{"minLeadParticlePt", 2.0f, "Minimum Pt for a lead track to be considered a valid proxy for a jet (may be more restrictive than TableProducer)"}; @@ -709,6 +710,20 @@ struct lambdajetpolarizationionsderived { histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP"))->GetXaxis()->SetBinLabel(8, "#eta_{LeadP} < 0, #eta_{#Lambda} #geq 0"); histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP"))->GetXaxis()->SetBinLabel(9, "#eta_{LeadP} < 0, #eta_{#Lambda} < 0"); + // Studying the signal Vs background integral (a very naive estimative of the invariant mass peak) + histos.add("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground", "pRingEtaCutsLeadingP_MassSignalVsBackground; ; ;<#it{R}>", kTProfile2D, {{9, 0, 9}, {2, 0, 2}}); + histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"))->GetXaxis()->SetBinLabel(1, "All #Lambda"); + histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"))->GetXaxis()->SetBinLabel(2, "#eta_{LeadP} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"))->GetXaxis()->SetBinLabel(3, "#eta_{LeadP} < 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"))->GetXaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"))->GetXaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"))->GetXaxis()->SetBinLabel(6, "#eta_{LeadP} #geq 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"))->GetXaxis()->SetBinLabel(7, "#eta_{LeadP} #geq 0, #eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"))->GetXaxis()->SetBinLabel(8, "#eta_{LeadP} < 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"))->GetXaxis()->SetBinLabel(9, "#eta_{LeadP} < 0, #eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"))->GetYaxis()->SetBinLabel(1, "#Lambda in mass peak"); + histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"))->GetYaxis()->SetBinLabel(2, "#Lambda out of mass peak"); + // Fake polarization signal QA // --> The "negative helicity problem", where topologies with a proton decaying opposite to the Lambda momentum are enhanced by // efficiency of reconstruction. The geometries where the proton moves in the same direction as the boost will have a very small @@ -906,6 +921,114 @@ struct lambdajetpolarizationionsderived { return -1.f; } + // Helper to modify the jet direction for QA and for spurious signal baseline removal tests: + void applyProxyDistortion(bool& hasValidProxy, float& pt, float& eta, float& phi, XYZVector& unitVec, + float minPtThreshold, bool& cacheHadProxy, float& cacheEta, float& cachePhi){ + if (!forcePerpToJet && !forceJetDirectionSmudge && !forceRandJet && !forcePreviousJet)[[likely]] { + return; // Skip this function if none of the modifications are actually being executed! + } + + // QA block -- Purposefully changing the jet direction (should kill signal, if any): + if (forcePerpToJet) { + // First, we build a vector perpendicular to the jet by picking an arbitrary vector not parallel to the jet + XYZVector refVec(1., 0., 0.); + if (std::abs(unitVec.Dot(refVec)) > 0.99) + refVec = XYZVector(0., 1., 0.); + + // Now we get a perpendicular vector to the jet direction: + XYZVector perpVec = unitVec.Cross(refVec).Unit(); + + // Now we rotate around the jet axis by a random angle, just to make sure we are not introducing a bias in the QA: + // We will use Rodrigues' rotation formula (v_rot = v*cos(randomAngle) + (Jet \cross v)*sin(randomAngle)) + double randomAngle = randomGen.Uniform(0., o2::constants::math::TwoPI); + unitVec = perpVec * std::cos(randomAngle) + unitVec.Cross(perpVec) * std::sin(randomAngle); + } else if (forceJetDirectionSmudge) { + // Smear the jet direction by a small random angle to estimate sensitivity to + // jet axis uncertainty. We rotate the jet axis by angle theta around a uniformly + // random perpendicular axis -- this is isotropic and coordinate-independent, + // unlike smearing eta and phi separately (which would break azimuthal symmetry + // around the jet axis and depend on where in eta the jet sits). + + // 1) We pick a uniformly random axis perpendicular to the jet. + // (re-using the same Rodrigues formula as in the forcePerpToJet block above) + XYZVector refVec(1., 0., 0.); + if (std::abs(unitVec.Dot(refVec)) > 0.99) + refVec = XYZVector(0., 1., 0.); + XYZVector perpVec = unitVec.Cross(refVec).Unit(); + + // Rotate perpVec around the jet axis by a uniform random azimuth to get + // a uniformly distributed random perpendicular direction (the smear axis): + double smearAzimuth = randomGen.Uniform(0., o2::constants::math::TwoPI); + XYZVector smearAxis = perpVec * std::cos(smearAzimuth) + unitVec.Cross(perpVec) * std::sin(smearAzimuth); + + // 2) draw the smearing polar angle from a Gaussian: + // sigma = 0.05 * R --> ~68% of events smeared within 5% of R, + // ~95% of events smeared within 10% of R, + // ~5% see a displacement > 0.1*R (a very "badly determined jet", for our QA purposes) + // std::abs() folds the symmetric Gaussian onto a half-normal ([0, inf)) + // -- R is not really an angle: just gives me a scale for the angular shift I am performing. + // -- This may pose problems for forward jets: a small displacement in \theta becomes a large displacement in \eta space + double smearSigma = 0.05 * jetR; + double smearAngle = std::abs(randomGen.Gaus(0., smearSigma)); + + // 3) rotate the jet axis by smearAngle around smearAxis. + // Rodrigues is v_rot = v*cos(theta) + (k \cross v)*sin(theta) + k*(k \cdot v)*(1-cos(theta)) + // But the last term vanishes because smearAxis is perpendicular to unitVec: + unitVec = unitVec * std::cos(smearAngle) + smearAxis.Cross(unitVec) * std::sin(smearAngle); + // Also, rotation preserves the norm, so no re-normalisation is needed for this to be a unit vector. + } else if (forceRandJet) { + // This randomization was made different for each proxy (LeadP, LeadJet, SubLeadJet): bear that in mind! + // 1) Uniformly sample cos(theta) and phi to ensure an isotropic distribution (could also use TRandom::Sphere as well) + // Notice that uniformly sampling theta would make the distribution non-isotropic, thus we use cos(theta)! + double cosTheta = randomGen.Uniform(-1., 1.); + double sinTheta = std::sqrt(1. - cosTheta * cosTheta); + double randPhi = randomGen.Uniform(0., o2::constants::math::TwoPI); + + // 2) Construct the new random unit vector (there is no need to use the magnitude at all! We only need direction here): + unitVec = XYZVector(sinTheta * std::cos(randPhi), sinTheta * std::sin(randPhi), cosTheta); + } else if (forcePreviousJet) { // Use the jet direction from the immediately preceding collision. The simplest event mixing + const bool usableProxy = cacheHadProxy; + if (usableProxy) { + const float inverseCoshEta = 1.0f / std::cosh(cacheEta); + const float sinPhi = std::sin(cachePhi); + const float cosPhi = std::cos(cachePhi); + unitVec = XYZVector(cosPhi * inverseCoshEta, sinPhi * inverseCoshEta, std::tanh(cacheEta)); + } + + // Update cache with the current event data: + cacheHadProxy = hasValidProxy; + cacheEta = eta; + cachePhi = phi; + + // Current event cannot use previous-jet mixing if previous event lacked a proxy + if (!usableProxy) + hasValidProxy = false; + } + + // Recalculating pT, phi and eta after distortions: + // (without this, later kinematic selections make no sense at all! In the forceRandJet case, the ring observable would always sum zero) + if (hasValidProxy) { // If you don't check this flag here, the "if (!usableProxy)" change would be silently overwritten + // Calculating total jet momentum, which should be preserved, just to recalculate the new jet Pt: + double mag = pt * std::cosh(eta); + + // For stability (Rho is the projection on the transverse plane, badly behaved for high |eta|): + double transverseNorm = std::max(unitVec.Rho(), 1e-12); // Stability guard + + // Recalculate pT: + pt = mag * transverseNorm; + + // Recalculate phi: + phi = std::atan2(unitVec.Y(), unitVec.X()); + + // Stable eta computation: + // Stabler than 0.5 * std::log((1. + cosTheta) / (1. - cosTheta)) + eta = std::asinh(unitVec.Z() / transverseNorm); + + // Recalculate the bool after distortion to see if we proceed with this jet proxy: + hasValidProxy = pt > minPtThreshold; + } + } + // Initializing a random number generator for the worker (for perpendicular-to-jet direction QAs): TRandom3 randomGen{0}; // 0 means we auto-seed from machine entropy. This is called once per device in the pipeline, so we should not see repeated seeds across workers @@ -917,6 +1040,14 @@ struct lambdajetpolarizationionsderived { void processPolarizationData(o2::aod::RingCollisions const& collisions, o2::aod::RingJets const& jets, o2::aod::RingLaV0s const& v0s, o2::aod::RingLeadPs const& leadPs) { + // Caching the previous collision's jet directions -- An optional feature for forcePreviousJet: + struct PrevJetCache { + bool hadLeadJet; float leadJetEta; float leadJetPhi; // Leading jet + bool hadSubJet; float subJetEta; float subJetPhi; // Subleading jet + bool hadLeadP; float leadPEta; float leadPPhi; // Leading particle + }; + PrevJetCache prevJetCache{}; // Initializing struct before the collisions loop. This zero-initializes, so bools start as false + for (auto const& collision : collisions) { const auto collId = collision.globalIndex(); // The self-index accessor const double centrality = getCentrality(collision); @@ -975,53 +1106,18 @@ struct lambdajetpolarizationionsderived { XYZVector leadPUnitVec(1., 0., 0.); // dummy (overwritten below when hasValidLeadingP) if (hasValidLeadingP) { leadPUnitVec = XYZVector(leadPPx, leadPPy, leadPPz).Unit(); - // QA: same direction-smearing/perp logic as for the leading jet estimator. - // The hLeadPEta histogram above intentionally uses the unmodified direction. - if (forcePerpToJet) { - XYZVector refVec(1., 0., 0.); - if (std::abs(leadPUnitVec.Dot(refVec)) > 0.99) - refVec = XYZVector(0., 1., 0.); - XYZVector perpVec = leadPUnitVec.Cross(refVec).Unit(); - double randomAngle = randomGen.Uniform(0., o2::constants::math::TwoPI); - leadPUnitVec = perpVec * std::cos(randomAngle) + leadPUnitVec.Cross(perpVec) * std::sin(randomAngle); - } else if (forceJetDirectionSmudge) { - XYZVector refVec(1., 0., 0.); - if (std::abs(leadPUnitVec.Dot(refVec)) > 0.99) - refVec = XYZVector(0., 1., 0.); - XYZVector perpVec = leadPUnitVec.Cross(refVec).Unit(); - double smearAzimuth = randomGen.Uniform(0., o2::constants::math::TwoPI); - XYZVector smearAxis = perpVec * std::cos(smearAzimuth) + leadPUnitVec.Cross(perpVec) * std::sin(smearAzimuth); - double smearAngle = std::abs(randomGen.Gaus(0., 0.05 * jetR)); - leadPUnitVec = leadPUnitVec * std::cos(smearAngle) + smearAxis.Cross(leadPUnitVec) * std::sin(smearAngle); - } else if (forceRandJet){ - // This randomization was made different for each proxy (LeadP, LeadJet, SubLeadJet): bear that in mind! - // 1) Uniformly sample cos(theta) and phi to ensure an isotropic distribution (could also use TRandom::Sphere as well) - // Notice that uniformly sampling theta would make the distribution non-isotropic, thus we use cos(theta)! - double cosTheta = randomGen.Uniform(-1., 1.); - double sinTheta = std::sqrt(1. - cosTheta * cosTheta); - double randPhi = randomGen.Uniform(0., o2::constants::math::TwoPI); - - // 2) Construct the new random unit vector (there is no need to use the magnitude at all! We only need direction here): - leadPUnitVec = XYZVector(sinTheta * std::cos(randPhi), sinTheta * std::sin(randPhi), cosTheta); + + // Apply distortion logic: + const bool hadPreviousProxy = prevJetCache.hadLeadP; + applyProxyDistortion(hasValidLeadingP, leadPPt, leadPEta, leadPPhi, leadPUnitVec, + minLeadParticlePt, prevJetCache.hadLeadP, prevJetCache.leadPEta, prevJetCache.leadPPhi); + + // Fill distorted-proxy QA histograms: + // Do not gate on the post-distortion hasValidLeadingP (a pT cut) value here! + if (!forcePreviousJet || hadPreviousProxy){ + histos.fill(HIST("JetKinematicsQA/hLeadPEta"), leadPEta); + histos.fill(HIST("JetKinematicsQA/hJetCounterPtLeadP"), leadPPt); } - // Recalculating pT, phi and eta after distortions: - // (without this, later kinematic selections make no sense at all! In the forceRandJet case, the ring observable would always sum zero) - // Calculating total jet momentum, which should be preserved, just to recalculate the new jet Pt: - double leadPMag = leadPPt * std::cosh(leadPEta); - // Recalculate pT: - double transverseNorm = std::max(leadPUnitVec.Rho(), 1e-12); // For stability (Rho is the projection on the transverse plane, badly behaved for high |eta|) - leadPPt = leadPMag * transverseNorm; - // Recalculate phi: - leadPPhi = std::atan2(leadPUnitVec.Y(), leadPUnitVec.X()); - // Stable eta computation: - leadPEta = std::asinh(leadPUnitVec.Z() / transverseNorm); // Stabler than 0.5 * std::log((1. + cosTheta) / (1. - cosTheta)) - - // Filling histograms after distortions: - histos.fill(HIST("JetKinematicsQA/hLeadPEta"), leadPEta); - histos.fill(HIST("JetKinematicsQA/hJetCounterPtLeadP"), leadPPt); - - // Recalculate the bool after distortion to see if we proceed with this jet proxy: - hasValidLeadingP = leadPPt > minLeadParticlePt; } // 3) Checking if the event has a leading jet: @@ -1062,81 +1158,18 @@ struct lambdajetpolarizationionsderived { leadingJetPhi = leadingJet->jetPhi(); // Using internal getters to make code cleaner: leadingJetUnitVec = XYZVector(leadingJet->jetPx(), leadingJet->jetPy(), leadingJet->jetPz()).Unit(); - - // QA block -- Purposefully changing the jet direction (should kill signal, if any): - if (forcePerpToJet) { // Use modified jet direction (done outside loop to guarantee all V0s inside event use same fake jet) - // First, we build a vector perpendicular to the jet by picking an arbitrary vector not parallel to the jet - XYZVector refVec(1., 0., 0.); - if (std::abs(leadingJetUnitVec.Dot(refVec)) > 0.99) - refVec = XYZVector(0., 1., 0.); - // Now we get a perpendicular vector to the jet direction: - XYZVector perpVec = leadingJetUnitVec.Cross(refVec).Unit(); - // Now we rotate around the jet axis by a random angle, just to make sure we are not introducing a bias in the QA: - // We will use Rodrigues' rotation formula (v_rot = v*cos(randomAngle) + (Jet \cross v)*sin(randomAngle)) - double randomAngle = randomGen.Uniform(0., o2::constants::math::TwoPI); - leadingJetUnitVec = perpVec * std::cos(randomAngle) + leadingJetUnitVec.Cross(perpVec) * std::sin(randomAngle); - } else if (forceJetDirectionSmudge) { - // Smear the jet direction by a small random angle to estimate sensitivity to - // jet axis uncertainty. We rotate the jet axis by angle theta around a uniformly - // random perpendicular axis -- this is isotropic and coordinate-independent, - // unlike smearing eta and phi separately (which would break azimuthal symmetry - // around the jet axis and depend on where in eta the jet sits). - - // 1) We pick a uniformly random axis perpendicular to the jet. - // (re-using the same Rodrigues formula as in the forcePerpToJet block above) - XYZVector refVec(1., 0., 0.); - if (std::abs(leadingJetUnitVec.Dot(refVec)) > 0.99) - refVec = XYZVector(0., 1., 0.); - XYZVector perpVec = leadingJetUnitVec.Cross(refVec).Unit(); - // Rotate perpVec around the jet axis by a uniform random azimuth to get - // a uniformly distributed random perpendicular direction (the smear axis): - double smearAzimuth = randomGen.Uniform(0., o2::constants::math::TwoPI); - XYZVector smearAxis = perpVec * std::cos(smearAzimuth) + leadingJetUnitVec.Cross(perpVec) * std::sin(smearAzimuth); - - // Step 2: draw the smearing polar angle from a Gaussian: - // sigma = 0.05 * R --> ~68% of events smeared within 5% of R, - // ~95% of events smeared within 10% of R, - // ~5% see a displacement > 0.1*R (a very "badly determined jet", for our QA purposes) - // std::abs() folds the symmetric Gaussian onto a half-normal ([0, inf)) - // -- R is not really an angle: just gives me a scale for the angular shift I am performing. - // -- This may pose problems for forward jets: a small displacement in \theta becomes a large displacement in \eta space - double smearSigma = 0.05 * jetR; - double smearAngle = std::abs(randomGen.Gaus(0., smearSigma)); - - // Step 3: rotate the jet axis by smearAngle around smearAxis. - // Rodrigues is v_rot = v*cos(theta) + (k \cross v)*sin(theta) + k*(k \cdot v)*(1-cos(theta)) - // But the last term vanishes because smearAxis is perpendicular to leadingJetUnitVec: - leadingJetUnitVec = leadingJetUnitVec * std::cos(smearAngle) + smearAxis.Cross(leadingJetUnitVec) * std::sin(smearAngle); - // Also, rotation preserves the norm, so no re-normalisation is needed for this to be a unit vector. - } - else if (forceRandJet){ - // Uniformly sample cos(theta) and phi to ensure an isotropic distribution - // Notice that uniformly sampling theta would make the distribution non-isotropic, thus we use cos(theta)! - double cosTheta = randomGen.Uniform(-1., 1.); - double sinTheta = std::sqrt(1. - cosTheta * cosTheta); - double randPhi = randomGen.Uniform(0., o2::constants::math::TwoPI); - - // Construct the new random unit vector (there is no need to use the magnitude at all! We only need direction here): - leadingJetUnitVec = XYZVector(sinTheta * std::cos(randPhi), sinTheta * std::sin(randPhi), cosTheta); + + // Apply distortion logic: + const bool hadPreviousProxy = prevJetCache.hadLeadJet; + applyProxyDistortion(hasValidLeadingJet, leadingJetPt, leadingJetEta, leadingJetPhi, leadingJetUnitVec, + minLeadJetPt, prevJetCache.hadLeadJet, prevJetCache.leadJetEta, prevJetCache.leadJetPhi); + + // Fill distorted-proxy QA histograms: + // Do not gate on the post-distortion hasValidLeadingJet (a pT cut) value here! + if (!forcePreviousJet || hadPreviousProxy) { + histos.fill(HIST("JetKinematicsQA/hLeadJetEta"), leadingJetEta); + histos.fill(HIST("JetKinematicsQA/hJetCounterPtJet"), leadingJetPt); } - // Recalculating pT, phi and eta after distortions: - // (without this, later kinematic selections make no sense at all! In the forceRandJet case, the ring observable would always sum zero) - // Calculating total jet momentum, which should be preserved, just to recalculate the new jet Pt: - double leadingJetMag = leadingJetPt * std::cosh(leadingJetEta); - // Recalculate pT: - double transverseNorm = std::max(leadingJetUnitVec.Rho(), 1e-12); // For stability (Rho is the projection on the transverse plane, badly behaved for high |eta|) - leadingJetPt = leadingJetMag * transverseNorm; - // Recalculate phi: - leadingJetPhi = std::atan2(leadingJetUnitVec.Y(), leadingJetUnitVec.X()); - // Stable eta computation: - leadingJetEta = std::asinh(leadingJetUnitVec.Z() / transverseNorm); // Stabler than 0.5 * std::log((1. + cosTheta) / (1. - cosTheta)) - - // Filling histograms after distortions: - histos.fill(HIST("JetKinematicsQA/hLeadJetEta"), leadingJetEta); - histos.fill(HIST("JetKinematicsQA/hJetCounterPtJet"), leadingJetPt); - - // Recalculate the bool after distortion to see if we proceed with this jet proxy: - hasValidLeadingJet = leadingJetPt > minLeadJetPt; } float subleadingJetEta = 0.; @@ -1146,54 +1179,19 @@ struct lambdajetpolarizationionsderived { subleadingJetEta = subleadingJet->jetEta(); subleadingJetPhi = subleadingJet->jetPhi(); subJetUnitVec = XYZVector(subleadingJet->jetPx(), subleadingJet->jetPy(), subleadingJet->jetPz()).Unit(); - histos.fill(HIST("JetKinematicsQA/hSubLeadJetEta"), subleadingJetEta); // Unmodified direction - histos.fill(HIST("JetKinematicsQA/hJetCounterPt2ndJet"), subleadingJetPt); - // QA: same direction-smearing/perp logic as for the leading jet estimator. - if (forcePerpToJet) { - XYZVector refVec(1., 0., 0.); - if (std::abs(subJetUnitVec.Dot(refVec)) > 0.99) - refVec = XYZVector(0., 1., 0.); - XYZVector perpVec = subJetUnitVec.Cross(refVec).Unit(); - double randomAngle = randomGen.Uniform(0., o2::constants::math::TwoPI); - subJetUnitVec = perpVec * std::cos(randomAngle) + subJetUnitVec.Cross(perpVec) * std::sin(randomAngle); - } else if (forceJetDirectionSmudge) { - XYZVector refVec(1., 0., 0.); - if (std::abs(subJetUnitVec.Dot(refVec)) > 0.99) - refVec = XYZVector(0., 1., 0.); - XYZVector perpVec = subJetUnitVec.Cross(refVec).Unit(); - double smearAzimuth = randomGen.Uniform(0., o2::constants::math::TwoPI); - XYZVector smearAxis = perpVec * std::cos(smearAzimuth) + subJetUnitVec.Cross(perpVec) * std::sin(smearAzimuth); - double smearAngle = std::abs(randomGen.Gaus(0., 0.05 * jetR)); - subJetUnitVec = subJetUnitVec * std::cos(smearAngle) + smearAxis.Cross(subJetUnitVec) * std::sin(smearAngle); - } - else if (forceRandJet){ - // Uniformly sample cos(theta) and phi to ensure an isotropic distribution - // Notice that uniformly sampling theta would make the distribution non-isotropic, thus we use cos(theta)! - double cosTheta = randomGen.Uniform(-1., 1.); - double sinTheta = std::sqrt(1. - cosTheta * cosTheta); - double randPhi = randomGen.Uniform(0., o2::constants::math::TwoPI); - - // Construct the new random unit vector (there is no need to use the magnitude at all! We only need direction here): - subJetUnitVec = XYZVector(sinTheta * std::cos(randPhi), sinTheta * std::sin(randPhi), cosTheta); + + // Apply distortion logic: + const bool hadPreviousProxy = prevJetCache.hadSubJet; + applyProxyDistortion(hasValidSubJet, subleadingJetPt, subleadingJetEta, subleadingJetPhi, subJetUnitVec, + minSubLeadJetPt, prevJetCache.hadSubJet, prevJetCache.subJetEta, prevJetCache.subJetPhi); + + + // Fill distorted-proxy QA histograms: + // Do not gate on the post-distortion hasValidSubJet (a pT cut) value here! + if (!forcePreviousJet || hadPreviousProxy) { + histos.fill(HIST("JetKinematicsQA/hSubLeadJetEta"), subleadingJetEta); + histos.fill(HIST("JetKinematicsQA/hJetCounterPt2ndJet"), subleadingJetPt); } - // Recalculating pT, phi and eta after distortions: - // (without this, later kinematic selections make no sense at all! In the forceRandJet case, the ring observable would always sum zero) - // Calculating total jet momentum, which should be preserved, just to recalculate the new jet Pt: - double subleadingJetMag = subleadingJetPt * std::cosh(subleadingJetEta); - // Recalculate pT: - double transverseNorm = std::max(subJetUnitVec.Rho(), 1e-12); // For stability (Rho is the projection on the transverse plane, badly behaved for high |eta|) - subleadingJetPt = subleadingJetMag * transverseNorm; - // Recalculate phi: - subleadingJetPhi = std::atan2(subJetUnitVec.Y(), subJetUnitVec.X()); - // Stable eta computation: - subleadingJetEta = std::asinh(subJetUnitVec.Z() / transverseNorm); // Stabler than 0.5 * std::log((1. + cosTheta) / (1. - cosTheta)) - - // Filling histograms after distortions: - histos.fill(HIST("JetKinematicsQA/hSubLeadJetEta"), subleadingJetEta); - histos.fill(HIST("JetKinematicsQA/hJetCounterPt2ndJet"), subleadingJetPt); - - // Recalculate the bool after distortion to see if we proceed with this jet proxy: - hasValidSubJet = subleadingJetPt > minSubLeadJetPt; } // (jet eta cuts only meaningful when the jet actually exists) @@ -1263,6 +1261,7 @@ struct lambdajetpolarizationionsderived { PtEtaPhiMVector lambdaLike4Vec(v0pt, v0eta, v0phi, v0LambdaLikeMass); PtEtaPhiMVector protonLike4Vec(protonLikePt, protonLikeEta, protonLikePhi, protonMass); const float lambdaRapidity = lambdaLike4Vec.Rapidity(); // For further kinematic selections + const int v0InMassPeak = (v0LambdaLikeMass >= 1.1020593 && v0LambdaLikeMass <= 1.1288811); // Very naive estimator. Based on signal extractions from outside this code // Boosting proton into lambda frame: XYZVector beta = lambdaLike4Vec.BoostToCM(); // Boost trivector that goes from laboratory frame to Lambda's rest frame (convenient new function, different from TLorentzVector's BoostVector()) @@ -1608,6 +1607,8 @@ struct lambdajetpolarizationionsderived { if (hasValidLeadingP) { histos.fill(HIST("EtaStudy/pRingEtaCutsLeadingP"), 0, ringObservableLeadP); histos.fill(HIST("EtaStudy/pRingEtaCutsLeadingP"), lambdaEtaPos ? 3 : 4, ringObservableLeadP); + histos.fill(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"), 0, v0InMassPeak, ringObservableLeadP); + histos.fill(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"), lambdaEtaPos ? 3 : 4, v0InMassPeak, ringObservableLeadP); histos.fill(HIST("EtaStudy/hFakePolCountsLeadP"), cosFakePol, 0); histos.fill(HIST("EtaStudy/hFakePolCountsLeadP"), cosFakePol, lambdaEtaPos ? 3 : 4); @@ -1620,6 +1621,8 @@ struct lambdajetpolarizationionsderived { if (leadPEtaPos) { histos.fill(HIST("EtaStudy/pRingEtaCutsLeadingP"), 1, ringObservableLeadP); histos.fill(HIST("EtaStudy/pRingEtaCutsLeadingP"), lambdaEtaPos ? 5 : 6, ringObservableLeadP); + histos.fill(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"), 1, v0InMassPeak, ringObservableLeadP); + histos.fill(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"), lambdaEtaPos ? 5 : 6, v0InMassPeak, ringObservableLeadP); histos.fill(HIST("EtaStudy/hFakePolCountsLeadP"), cosFakePol, 1); histos.fill(HIST("EtaStudy/hFakePolCountsLeadP"), cosFakePol, lambdaEtaPos ? 5 : 6); @@ -1629,6 +1632,8 @@ struct lambdajetpolarizationionsderived { else { histos.fill(HIST("EtaStudy/pRingEtaCutsLeadingP"), 2, ringObservableLeadP); histos.fill(HIST("EtaStudy/pRingEtaCutsLeadingP"), lambdaEtaPos ? 7 : 8, ringObservableLeadP); + histos.fill(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"), 2, v0InMassPeak, ringObservableLeadP); + histos.fill(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"), lambdaEtaPos ? 7 : 8, v0InMassPeak, ringObservableLeadP); histos.fill(HIST("EtaStudy/hFakePolCountsLeadP"), cosFakePol, 2); histos.fill(HIST("EtaStudy/hFakePolCountsLeadP"), cosFakePol, lambdaEtaPos ? 7 : 8); From cd9bf1fbf8885e2e43215658746631e06dc9e2dd Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Thu, 14 May 2026 21:59:25 +0000 Subject: [PATCH 13/31] Implementing forceDatalikeJet variation and further optimizing applyProxyDistortion. Still need to properly work out the phiWeights and etaWeights distributions for the jets and subleading jets though! --- .../lambdaJetPolarizationIonsDerived.cxx | 101 ++++++++++++------ 1 file changed, 68 insertions(+), 33 deletions(-) diff --git a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx index c2932161e83..ab84a6b40b0 100644 --- a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx @@ -55,6 +55,8 @@ #include #include #include +#include +#include using namespace o2; using namespace o2::framework; @@ -294,7 +296,7 @@ struct lambdajetpolarizationionsderived { Configurable analyseLambda{"analyseLambda", true, "process Lambda-like candidates"}; Configurable analyseAntiLambda{"analyseAntiLambda", false, "process AntiLambda-like candidates"}; Configurable analyseMagField{"analyseMagField", true, "analyse efficiency effects wrt magnetic field"}; // Older DerivedData lacks magField. "if constexpr (requires { collision.magField(); })" would only see the current header definition, so need a flag for retro-comp. - Configurable doPPAnalysis{"doPPAnalysis", false, "if in pp, set to true. Default is HI"}; + // Configurable doPPAnalysis{"doPPAnalysis", false, "if in pp, set to true. Default is HI"}; // Centrality: Configurable centralityEstimator{"centralityEstimator", kCentFT0M, "Run 3 centrality estimator (0:CentFT0C, 1:CentFT0M, 2:CentFV0A)"}; // Default is FT0M @@ -306,6 +308,7 @@ struct lambdajetpolarizationionsderived { Configurable forceJetDirectionSmudge{"forceJetDirectionSmudge", false, "fluctuate jet direction by 10% of R around original axis. For QA (tests sensibility)"}; Configurable forceRandJet{"forceRandJet", false, "makes jet direction random. A QA for AEE fake signal and its removal"}; Configurable forcePreviousJet{"forcePreviousJet", false, "uses previous event's jet direction instead of a random sample. A baseline for fake signal removal"}; + Configurable forceDatalikeJet{"forceDatalikeJet", false, "A compromise between forceRandJet and forcePreviousJet. Parameterized distribution from data"}; // Configurable jetRForSmudging{"jetRForSmudging", 0.4, "QA quantity: the chosen R scale for the jet direction smudge"}; // Superseeded by jetR: kept the same scale in analysis and QA Configurable jetR{"jetR", 0.4f, "Radius of the jet"}; // Provide manually, please. Configurable minLeadParticlePt{"minLeadParticlePt", 2.0f, "Minimum Pt for a lead track to be considered a valid proxy for a jet (may be more restrictive than TableProducer)"}; @@ -921,26 +924,40 @@ struct lambdajetpolarizationionsderived { return -1.f; } + // Initializing a random number generator for the worker (for perpendicular-to-jet direction QAs): + TRandom3 randomGen{0}; // 0 means we auto-seed from machine entropy. This is called once per device in the pipeline, so we should not see repeated seeds across workers + std::mt19937 rng{std::random_device{}()}; + + // Pre-computed values for helper below: + const double smearSigma = 0.05 * jetR; + // Normalized eta weights spanning -0.9 to 0.9 (46 bins) and phi weights for the 50 bins spanning [0, 2pi]: + static constexpr std::array etaLeadPWeights = {{0.01782505198123039,0.01826119427561306,0.01890047073124532,0.01942224199989093,0.01993380780273602,0.02047274597515178,0.02094135547756474,0.02140259654932778,0.02178490245182078,0.02218346916434517,0.02252861343224298,0.02278214932340838,0.02297395476452691,0.02311861709583109,0.02322295246943318,0.02329274166449468,0.02335344516182264,0.02335971904087711,0.02340163522424806,0.02352868368676468,0.02345839093195849,0.02295391718536531,0.02306543716698383,0.02293131780181040,0.02265098126991631,0.02318893563623931,0.02322457978177088,0.02316188601954564,0.02308812992419636,0.02305831097751334,0.02300695504254397,0.02296398287895598,0.02286956017362988,0.02274321888486162,0.02254049413132752,0.02233024817234042,0.02204811997596179,0.02170252191737713,0.02130517220864903,0.02086349641950970,0.02036590755841183,0.01987946074337928,0.01934550418314029,0.01879487530409137,0.01812577211265362,0.01766945805710696}}; + static constexpr std::array phiLeadPWeights = {{0.01907529231698144,0.02044679008716434,0.01948618554713157,0.02046288887443206,0.02142057576726765,0.01961361841611185,0.02174981627752354,0.02160945937846856,0.02027231236207667,0.02153799273983672,0.02107609996106984,0.02001899849606885,0.02196516947939817,0.02047654705787587,0.02059561382369167,0.02148625027035289,0.02001510925188416,0.02183059661361331,0.02111406548114694,0.01881826666371129,0.02112797285609031,0.02034071592473790,0.01968993216337670,0.02126946766383166,0.02025580366897253,0.02061136834815962,0.02083881238552183,0.01994368379135331,0.02046280212696892,0.02131631148368759,0.01967275608960357,0.02064965975476278,0.02155758091535052,0.02012557837329328,0.02084718400924722,0.02065094443305587,0.01969546187428681,0.02136531489392276,0.02084491659074202,0.01970883494847568,0.02080349992636310,0.02098440611808174,0.02159984658204099,0.02045819959592145,0.01952755742791563,0.02166002908586709,0.02017512590511229,0.01932658087972190,0.02108933627170306,0.02019288778648293}}; + // Build discrete eta distribution for sampling: + std::discrete_distribution etaLeadPDist{etaLeadPWeights.begin(),etaLeadPWeights.end()}; // Will be passed as the etaDist variable + std::discrete_distribution phiLeadPDist{phiLeadPWeights.begin(), phiLeadPWeights.end()}; + // Helper to modify the jet direction for QA and for spurious signal baseline removal tests: - void applyProxyDistortion(bool& hasValidProxy, float& pt, float& eta, float& phi, XYZVector& unitVec, - float minPtThreshold, bool& cacheHadProxy, float& cacheEta, float& cachePhi){ - if (!forcePerpToJet && !forceJetDirectionSmudge && !forceRandJet && !forcePreviousJet)[[likely]] { + inline void applyProxyDistortion(bool& hasValidProxy, float& pt, float& eta, float& phi, XYZVector& unitVec, + float minPtThreshold, bool& cacheHadProxy, float& cacheEta, float& cachePhi, + std::discrete_distribution& etaDist, std::discrete_distribution& phiDist, std::mt19937& rng){ + if (!forcePerpToJet && !forceJetDirectionSmudge && !forceRandJet && !forcePreviousJet && !forceDatalikeJet)[[likely]] { return; // Skip this function if none of the modifications are actually being executed! } // QA block -- Purposefully changing the jet direction (should kill signal, if any): if (forcePerpToJet) { // First, we build a vector perpendicular to the jet by picking an arbitrary vector not parallel to the jet - XYZVector refVec(1., 0., 0.); - if (std::abs(unitVec.Dot(refVec)) > 0.99) - refVec = XYZVector(0., 1., 0.); - - // Now we get a perpendicular vector to the jet direction: - XYZVector perpVec = unitVec.Cross(refVec).Unit(); + XYZVector perpVec; + if (std::abs(unitVec.X()) > 0.99) { + perpVec = XYZVector(-unitVec.Z(), 0., unitVec.X()).Unit(); // Cross product with Y-axis (0, 1, 0) + } else { + perpVec = XYZVector(0., unitVec.Z(), -unitVec.Y()).Unit(); // Cross product with X-axis (1, 0, 0) + } // Now we rotate around the jet axis by a random angle, just to make sure we are not introducing a bias in the QA: // We will use Rodrigues' rotation formula (v_rot = v*cos(randomAngle) + (Jet \cross v)*sin(randomAngle)) - double randomAngle = randomGen.Uniform(0., o2::constants::math::TwoPI); + const double randomAngle = randomGen.Uniform(0., o2::constants::math::TwoPI); unitVec = perpVec * std::cos(randomAngle) + unitVec.Cross(perpVec) * std::sin(randomAngle); } else if (forceJetDirectionSmudge) { // Smear the jet direction by a small random angle to estimate sensitivity to @@ -951,14 +968,16 @@ struct lambdajetpolarizationionsderived { // 1) We pick a uniformly random axis perpendicular to the jet. // (re-using the same Rodrigues formula as in the forcePerpToJet block above) - XYZVector refVec(1., 0., 0.); - if (std::abs(unitVec.Dot(refVec)) > 0.99) - refVec = XYZVector(0., 1., 0.); - XYZVector perpVec = unitVec.Cross(refVec).Unit(); + XYZVector perpVec; + if (std::abs(unitVec.X()) > 0.99) { + perpVec = XYZVector(-unitVec.Z(), 0., unitVec.X()).Unit(); // Cross product with Y-axis (0, 1, 0) + } else { + perpVec = XYZVector(0., unitVec.Z(), -unitVec.Y()).Unit(); // Cross product with X-axis (1, 0, 0) + } // Rotate perpVec around the jet axis by a uniform random azimuth to get // a uniformly distributed random perpendicular direction (the smear axis): - double smearAzimuth = randomGen.Uniform(0., o2::constants::math::TwoPI); + const double smearAzimuth = randomGen.Uniform(0., o2::constants::math::TwoPI); XYZVector smearAxis = perpVec * std::cos(smearAzimuth) + unitVec.Cross(perpVec) * std::sin(smearAzimuth); // 2) draw the smearing polar angle from a Gaussian: @@ -968,8 +987,7 @@ struct lambdajetpolarizationionsderived { // std::abs() folds the symmetric Gaussian onto a half-normal ([0, inf)) // -- R is not really an angle: just gives me a scale for the angular shift I am performing. // -- This may pose problems for forward jets: a small displacement in \theta becomes a large displacement in \eta space - double smearSigma = 0.05 * jetR; - double smearAngle = std::abs(randomGen.Gaus(0., smearSigma)); + const double smearAngle = std::abs(randomGen.Gaus(0., smearSigma)); // 3) rotate the jet axis by smearAngle around smearAxis. // Rodrigues is v_rot = v*cos(theta) + (k \cross v)*sin(theta) + k*(k \cdot v)*(1-cos(theta)) @@ -978,20 +996,20 @@ struct lambdajetpolarizationionsderived { // Also, rotation preserves the norm, so no re-normalisation is needed for this to be a unit vector. } else if (forceRandJet) { // This randomization was made different for each proxy (LeadP, LeadJet, SubLeadJet): bear that in mind! - // 1) Uniformly sample cos(theta) and phi to ensure an isotropic distribution (could also use TRandom::Sphere as well) + // 1) Uniformly sample cos(theta) and phi to ensure an isotropic distribution (could also use TRandom::Sphere as well, but may be slower) // Notice that uniformly sampling theta would make the distribution non-isotropic, thus we use cos(theta)! - double cosTheta = randomGen.Uniform(-1., 1.); - double sinTheta = std::sqrt(1. - cosTheta * cosTheta); - double randPhi = randomGen.Uniform(0., o2::constants::math::TwoPI); + const double cosTheta = randomGen.Uniform(-1., 1.); + const double sinTheta = std::sqrt(1. - cosTheta * cosTheta); + const double randPhi = randomGen.Uniform(0., o2::constants::math::TwoPI); // 2) Construct the new random unit vector (there is no need to use the magnitude at all! We only need direction here): unitVec = XYZVector(sinTheta * std::cos(randPhi), sinTheta * std::sin(randPhi), cosTheta); } else if (forcePreviousJet) { // Use the jet direction from the immediately preceding collision. The simplest event mixing const bool usableProxy = cacheHadProxy; if (usableProxy) { - const float inverseCoshEta = 1.0f / std::cosh(cacheEta); - const float sinPhi = std::sin(cachePhi); - const float cosPhi = std::cos(cachePhi); + const double inverseCoshEta = 1.0 / std::cosh(cacheEta); + const double sinPhi = std::sin(cachePhi); + const double cosPhi = std::cos(cachePhi); unitVec = XYZVector(cosPhi * inverseCoshEta, sinPhi * inverseCoshEta, std::tanh(cacheEta)); } @@ -1003,16 +1021,33 @@ struct lambdajetpolarizationionsderived { // Current event cannot use previous-jet mixing if previous event lacked a proxy if (!usableProxy) hasValidProxy = false; + } else if (forceDatalikeJet){ // A compromise between forceRandJet and forcePreviousJet, using data-like weights for sampling jets + const float etaMin = -0.92f; + const float etaBinWidth = 0.04f; + constexpr float phiBinWidth = constants::math::TwoPI / 50.f; + + // Pick one of the 46 bins according to etaWeights: + const int binEtaIdx = etaDist(rng); + const int binPhiIdx = phiDist(rng); + + // Uniformly smear inside the chosen bin: + eta = etaMin + etaBinWidth * (binEtaIdx + std::generate_canonical(rng)); + phi = phiBinWidth * (binPhiIdx + std::generate_canonical(rng)); + + const double inverseCoshEta = 1.0 / std::cosh(eta); + const double sinPhi = std::sin(phi); + const double cosPhi = std::cos(phi); + unitVec = XYZVector(cosPhi * inverseCoshEta, sinPhi * inverseCoshEta, std::tanh(eta)); } // Recalculating pT, phi and eta after distortions: // (without this, later kinematic selections make no sense at all! In the forceRandJet case, the ring observable would always sum zero) if (hasValidProxy) { // If you don't check this flag here, the "if (!usableProxy)" change would be silently overwritten // Calculating total jet momentum, which should be preserved, just to recalculate the new jet Pt: - double mag = pt * std::cosh(eta); + const double mag = pt * std::cosh(eta); // For stability (Rho is the projection on the transverse plane, badly behaved for high |eta|): - double transverseNorm = std::max(unitVec.Rho(), 1e-12); // Stability guard + const double transverseNorm = std::max(unitVec.Rho(), 1e-12); // Stability guard // Recalculate pT: pt = mag * transverseNorm; @@ -1029,9 +1064,6 @@ struct lambdajetpolarizationionsderived { } } - // Initializing a random number generator for the worker (for perpendicular-to-jet direction QAs): - TRandom3 randomGen{0}; // 0 means we auto-seed from machine entropy. This is called once per device in the pipeline, so we should not see repeated seeds across workers - // Preslices for correct collisions association: // (TODO: test using custom grouping) Preslice perColJets = o2::aod::lambdajetpol::ringCollisionId; @@ -1110,7 +1142,8 @@ struct lambdajetpolarizationionsderived { // Apply distortion logic: const bool hadPreviousProxy = prevJetCache.hadLeadP; applyProxyDistortion(hasValidLeadingP, leadPPt, leadPEta, leadPPhi, leadPUnitVec, - minLeadParticlePt, prevJetCache.hadLeadP, prevJetCache.leadPEta, prevJetCache.leadPPhi); + minLeadParticlePt, prevJetCache.hadLeadP, prevJetCache.leadPEta, prevJetCache.leadPPhi, + etaLeadPDist, phiLeadPDist, rng); // Fill distorted-proxy QA histograms: // Do not gate on the post-distortion hasValidLeadingP (a pT cut) value here! @@ -1162,7 +1195,8 @@ struct lambdajetpolarizationionsderived { // Apply distortion logic: const bool hadPreviousProxy = prevJetCache.hadLeadJet; applyProxyDistortion(hasValidLeadingJet, leadingJetPt, leadingJetEta, leadingJetPhi, leadingJetUnitVec, - minLeadJetPt, prevJetCache.hadLeadJet, prevJetCache.leadJetEta, prevJetCache.leadJetPhi); + minLeadJetPt, prevJetCache.hadLeadJet, prevJetCache.leadJetEta, prevJetCache.leadJetPhi, + etaLeadPDist, phiLeadPDist, rng); // Fill distorted-proxy QA histograms: // Do not gate on the post-distortion hasValidLeadingJet (a pT cut) value here! @@ -1183,7 +1217,8 @@ struct lambdajetpolarizationionsderived { // Apply distortion logic: const bool hadPreviousProxy = prevJetCache.hadSubJet; applyProxyDistortion(hasValidSubJet, subleadingJetPt, subleadingJetEta, subleadingJetPhi, subJetUnitVec, - minSubLeadJetPt, prevJetCache.hadSubJet, prevJetCache.subJetEta, prevJetCache.subJetPhi); + minSubLeadJetPt, prevJetCache.hadSubJet, prevJetCache.subJetEta, prevJetCache.subJetPhi, + etaLeadPDist, phiLeadPDist, rng); // Fill distorted-proxy QA histograms: From 0f83bf13b2a3428ef4046ad7a46b596a7a6a8a10 Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Mon, 18 May 2026 16:41:49 +0000 Subject: [PATCH 14/31] Fixing phi angle conventions (from 0 to 2PI in some plots, instead of atan2's -PI to PI output --- .../Strangeness/lambdaJetPolarizationIons.cxx | 2 +- .../lambdaJetPolarizationIonsDerived.cxx | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx b/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx index ef21d09f3d4..fdb9d8e3e7e 100644 --- a/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx +++ b/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx @@ -1688,7 +1688,7 @@ struct lambdajetpolarizationions { histos.fill(HIST("JetKinematicsQA/h2dJetsPerEventvsCosThetaToLead"), jetsInEvent, cosTheta); } } - if (doJetKinematicsQA) { + if (doJetKinematicsQA) { // Fills even when the jet is outside of the (0.9f - jetConfigurations.radiusJet) eta window. histos.fill(HIST("JetKinematicsQA/hLeadingJetPt"), leadingJet.pt()); histos.fill(HIST("JetKinematicsQA/hLeadingJetEta"), leadingJet.eta()); histos.fill(HIST("JetKinematicsQA/hLeadingJetPhi"), leadingJet.phi()); diff --git a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx index ab84a6b40b0..75eac3488c7 100644 --- a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx @@ -895,10 +895,15 @@ struct lambdajetpolarizationionsderived { // (calculated for leading jets only) histos.add("IntegratedCuts/pRingVsNV0s", "pRingVsNV0s; N_{#Lambda}+N_{#bar{#Lambda}};<#it{R}>", kTProfile, {{20, 0, 20}}); - // Leading Jet QA: + // Proxy Eta QA: histos.add("JetKinematicsQA/hLeadJetEta", "hLeadJetEta", kTH1D, {axisConfigurations.axisEta}); histos.add("JetKinematicsQA/hSubLeadJetEta", "hSubLeadJetEta", kTH1D, {axisConfigurations.axisEta}); histos.add("JetKinematicsQA/hLeadPEta", "hLeadPEta", kTH1D, {axisConfigurations.axisEta}); + + // Proxy Phi QA: + histos.add("JetKinematicsQA/hLeadJetPhi", "hLeadJetPhi", kTH1D, {axisConfigurations.axisPhi}); + histos.add("JetKinematicsQA/hSubLeadJetPhi", "hSubLeadJetPhi", kTH1D, {axisConfigurations.axisPhi}); + histos.add("JetKinematicsQA/hLeadPPhi", "hLeadPPhi", kTH1D, {axisConfigurations.axisPhi}); // Counting the number of jets/proxies themselves (these count at most once per event) -- Similar to the FOLDER/QA/hPtJet counters: histos.add("JetKinematicsQA/hJetCounterPtJet", "hJetCounterPtJet; p_{T}^{Jet} (GeV/c)", kTH1D, {axisConfigurations.axisJetPt}); @@ -1040,6 +1045,7 @@ struct lambdajetpolarizationionsderived { unitVec = XYZVector(cosPhi * inverseCoshEta, sinPhi * inverseCoshEta, std::tanh(eta)); } + // TODO: avoid recalculation for forcePreviousJet and forceDatalikeJet, where we already know eta and phi variables // Recalculating pT, phi and eta after distortions: // (without this, later kinematic selections make no sense at all! In the forceRandJet case, the ring observable would always sum zero) if (hasValidProxy) { // If you don't check this flag here, the "if (!usableProxy)" change would be silently overwritten @@ -1054,6 +1060,8 @@ struct lambdajetpolarizationionsderived { // Recalculate phi: phi = std::atan2(unitVec.Y(), unitVec.X()); + if (phi < 0.0f) + phi += o2::constants::math::TwoPI; // Notice that atan2 outputs values from -PI to PI, and DataModel convention was from 0 to 2PI as per FastJet's phi() getter // Stable eta computation: // Stabler than 0.5 * std::log((1. + cosTheta) / (1. - cosTheta)) @@ -1149,6 +1157,7 @@ struct lambdajetpolarizationionsderived { // Do not gate on the post-distortion hasValidLeadingP (a pT cut) value here! if (!forcePreviousJet || hadPreviousProxy){ histos.fill(HIST("JetKinematicsQA/hLeadPEta"), leadPEta); + histos.fill(HIST("JetKinematicsQA/hLeadPPhi"), leadPPhi); histos.fill(HIST("JetKinematicsQA/hJetCounterPtLeadP"), leadPPt); } } @@ -1202,6 +1211,7 @@ struct lambdajetpolarizationionsderived { // Do not gate on the post-distortion hasValidLeadingJet (a pT cut) value here! if (!forcePreviousJet || hadPreviousProxy) { histos.fill(HIST("JetKinematicsQA/hLeadJetEta"), leadingJetEta); + histos.fill(HIST("JetKinematicsQA/hLeadJetPhi"), leadingJetPhi); histos.fill(HIST("JetKinematicsQA/hJetCounterPtJet"), leadingJetPt); } } @@ -1225,6 +1235,7 @@ struct lambdajetpolarizationionsderived { // Do not gate on the post-distortion hasValidSubJet (a pT cut) value here! if (!forcePreviousJet || hadPreviousProxy) { histos.fill(HIST("JetKinematicsQA/hSubLeadJetEta"), subleadingJetEta); + histos.fill(HIST("JetKinematicsQA/hSubLeadJetPhi"), subleadingJetPhi); histos.fill(HIST("JetKinematicsQA/hJetCounterPt2ndJet"), subleadingJetPt); } } @@ -1327,7 +1338,7 @@ struct lambdajetpolarizationionsderived { float pX = protonLikeStarUnit3Vec.Y() * lambdaLikeUnit3Vec.X() - protonLikeStarUnit3Vec.X() * lambdaLikeUnit3Vec.Y(); // pY = p_z - L_z * (p_proton_star dot p_lambda_hat) float pY = protonLikeStarUnit3Vec.Z() - lambdaLikeUnit3Vec.Z() * cosFakePol; // (Reusing cosFakePol calculated earlier!) - float phiStar = std::atan2(pY, pX); + float phiStar = std::atan2(pY, pX); // This will give an output from -PI to PI // Another reconstruction efficiency measure: // (Formula is: p_{Lambda} \cross p_{Daughter}^{*} \cdot B, and B points in Z) From 091be109ae0ddc3313082affb4d481e4c92893c0 Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Wed, 8 Jul 2026 02:20:45 +0000 Subject: [PATCH 15/31] - Added nProxyResamples configurable, which allows for a resampling experiment. When the jet direction is drawn from a distribution in an attempt to disentangle physical ring polarization and efficiency-induced contributions, one can experiment with taking 10 different jet directions for each Lambda as a way of artificially reducing error bars and probing Lambda-only effects, to some level; - Fixed the eta axis for V0s, which was too small compared to what is allowed by the lambdaJetPolarizationIons table producer; - Added DCA-dependency checks for the ring observable, for DCA between the V0's daughters, of the proton-like track and the pion-like track; - Added simplistic event-mixing capabilities (see forcePreviousJet) where the previous event's jet direction is taken as the current event's proxy direction for the ring; --- .../Strangeness/lambdaJetPolarizationIons.cxx | 4 +- .../lambdaJetPolarizationIonsDerived.cxx | 117 ++++++++++++------ 2 files changed, 83 insertions(+), 38 deletions(-) diff --git a/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx b/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx index fdb9d8e3e7e..be22ad11617 100644 --- a/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx +++ b/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx @@ -193,7 +193,7 @@ struct lambdajetpolarizationions { Configurable requireINEL0{"requireINEL0", true, "require INEL>0 event selection"}; // Only truly useful in pp Configurable requireINEL1{"requireINEL1", false, "require INEL>1 event selection"}; - Configurable maxZVtxPosition{"maxZVtxPosition", 10., "max Z vtx position"}; + Configurable maxZVtxPosition{"maxZVtxPosition", 10., "max Z vtx position [cm]"}; 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", true, "Apply Z-vtx cut on the PV of the generated collision?"}; // I see no reason as to not do this by default @@ -320,7 +320,7 @@ struct lambdajetpolarizationions { // topological variable QA axes ConfigurableAxis axisDCAtoPV{"axisDCAtoPV", {20, 0.0f, 1.0f}, "DCA (cm)"}; - ConfigurableAxis axisDCAdau{"axisDCAdau", {20, 0.0f, 2.0f}, "DCA (cm)"}; + ConfigurableAxis axisDCAdau{"axisDCAdau", {20, 0.0f, 1.2f}, "DCA (cm)"}; // This should follow v0Selections.dcav0dau's default ConfigurableAxis axisPointingAngle{"axisPointingAngle", {20, 0.0f, 2.0f}, "pointing angle (rad)"}; ConfigurableAxis axisV0Radius{"axisV0Radius", {20, 0.0f, 60.0f}, "V0 2D radius (cm)"}; ConfigurableAxis axisNsigmaTPC{"axisNsigmaTPC", {200, -10.0f, 10.0f}, "N sigma TPC"}; diff --git a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx index 75eac3488c7..06f06e4b725 100644 --- a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx @@ -93,6 +93,7 @@ enum CentEstimator { X(FOLDER "/QA/h2dDeltaPhiVsLambdaPt", deltaPhiJet, v0pt) \ X(FOLDER "/QA/h2dDeltaThetaVsLambdaPt", deltaThetaJet, v0pt) \ /* Additional plots for instant gratification - 1D Profiles */ \ + X(FOLDER "/hRingObservableCounts", ringObservable) \ X(FOLDER "/pRingObservableDeltaPhi", deltaPhiJet, ringObservable) \ X(FOLDER "/pRingObservablePhiJet", leadingJetPhi, ringObservable) \ X(FOLDER "/pRingObservablePhiLambda", v0phi, ringObservable) \ @@ -157,6 +158,7 @@ enum CentEstimator { X(FOLDER "/QA/hDeltaThetaLeadP", deltaThetaLeadP) \ X(FOLDER "/QA/hPtLeadP", leadPPt) \ X(FOLDER "/QA/hCosDeltaThetaLeadP", cosDeltaThetaLeadP) \ + X(FOLDER "/hRingObservableLeadPCounts", ringObservableLeadP) \ X(FOLDER "/pRingObservableLeadPDeltaPhi", deltaPhiLeadP, ringObservableLeadP) \ X(FOLDER "/pRingObservableLeadPDeltaTheta", deltaThetaLeadP, ringObservableLeadP) \ X(FOLDER "/EtaDependence/pRingObservableEtaLambdaLeadP", v0eta, ringObservableLeadP) \ @@ -213,6 +215,7 @@ enum CentEstimator { X(FOLDER "/QA/hDeltaTheta2ndJet", deltaTheta2ndJet) \ X(FOLDER "/QA/hCosDeltaTheta2ndJet", cosDeltaTheta2ndJet) \ X(FOLDER "/QA/hPt2ndJet", subleadingJetPt) \ + X(FOLDER "/hRingObservable2ndJetCounter", ringObservable2ndJet) \ X(FOLDER "/pRingObservable2ndJetDeltaPhi", deltaPhi2ndJet, ringObservable2ndJet) \ X(FOLDER "/pRingObservable2ndJetDeltaTheta", deltaTheta2ndJet, ringObservable2ndJet) \ X(FOLDER "/EtaDependence/pRingObservableEtaLambda2ndJet", v0eta, ringObservable2ndJet) \ @@ -309,6 +312,7 @@ struct lambdajetpolarizationionsderived { Configurable forceRandJet{"forceRandJet", false, "makes jet direction random. A QA for AEE fake signal and its removal"}; Configurable forcePreviousJet{"forcePreviousJet", false, "uses previous event's jet direction instead of a random sample. A baseline for fake signal removal"}; Configurable forceDatalikeJet{"forceDatalikeJet", false, "A compromise between forceRandJet and forcePreviousJet. Parameterized distribution from data"}; + Configurable nProxyResamples{"nProxyResamples", 1, "The amount of resamplings of jet direction per event. Use ONLY for forceRandJet and forceDatalikeJet"}; // Configurable jetRForSmudging{"jetRForSmudging", 0.4, "QA quantity: the chosen R scale for the jet direction smudge"}; // Superseeded by jetR: kept the same scale in analysis and QA Configurable jetR{"jetR", 0.4f, "Radius of the jet"}; // Provide manually, please. Configurable minLeadParticlePt{"minLeadParticlePt", 2.0f, "Minimum Pt for a lead track to be considered a valid proxy for a jet (may be more restrictive than TableProducer)"}; @@ -339,13 +343,17 @@ struct lambdajetpolarizationionsderived { ConfigurableAxis axisJetPtSigExtract{"axisJetPtSigExtract", {VARIABLE_WIDTH, 0, 5, 10, 12, 16, 20, 25, 30, 35, 40, 60, 100, 200}, "Jet p_{t} (GeV)"}; ConfigurableAxis axisEta{"axisEta", {50, -1.0f, 1.0f}, "#eta"}; ConfigurableAxis axisEtaCoarse{"axisEtaCoarse", {20, -0.9f, 0.9f}, "#eta coarse axis"}; + ConfigurableAxis axisV0Eta{"axisV0Eta", {75, -1.5f, 1.5f}, "V0 #eta"}; // An axis for V0 eta, which can go up to 1.5 given standard producer selections + ConfigurableAxis axisV0EtaCoarse{"axisV0EtaCoarse", {32, -1.5f, 1.5f}, "V0 #eta coarse"}; ConfigurableAxis axisDeltaEtaCoarse{"axisDeltaEtaCoarse", {40, -1.8f, 1.8f}, "#Delta#eta coarse axis"}; ConfigurableAxis axisDeltaTheta{"axisDeltaTheta", {40, 0, constants::math::PI}, "#Delta #theta_{jet}"}; ConfigurableAxis axisCosTheta{"axisCosTheta", {50, -1, 1}, "cos(#theta)"}; ConfigurableAxis axisPhi{"axisPhi", {40, 0., constants::math::TwoPI}, "#varphi"}; ConfigurableAxis axisDeltaPhi{"axisDeltaPhi", {40, -constants::math::PI, constants::math::PI}, "#Delta #phi_{jet}"}; + ConfigurableAxis axisRingCounts{"axisRingCounts", {90, -4.5, 4.5}, "<#it{R}>"}; - ConfigurableAxis axisDCAdau{"axisDCAdau", {20, 0., 2.0}, "DCA V0 daughters"}; + ConfigurableAxis axisDCAdau{"axisDCAdau", {10, 0., 2.0}, "DCA V0 daughters (cm)"}; + ConfigurableAxis axisDCAdauPV{"axisDCAdauPV", {10, 0., 1.2}, "DCA dauPV (cm)"}; // v0Selections.dcav0dau's default maximum is 1.2f in the TableProducer // Coarser axes for signal extraction: ConfigurableAxis axisPtSigExtract{"axisPtSigExtract", {VARIABLE_WIDTH, 0.0f, 0.25f, 0.5f, 0.75f, 1.0f, 1.25f, 1.5f, 2.0f, 2.5f, 3.0f, 4.0f, 6.0f, 8.0f, 10.0f, 15.0f, 20.0f, 30.0f, 50.0f}, "pt axis for signal extraction"}; @@ -468,6 +476,8 @@ struct lambdajetpolarizationionsderived { // 1D TProfiles // =============================== histos.add((folder + "/pRingObservableDeltaPhi").c_str(), "<#it{R}> vs #Delta#varphi_{jet};#Delta#varphi_{jet};<#it{R}>", kTProfile, {axisConfigurations.axisDeltaPhi}); + // To see the actual distribution of counts in data (another differential-like shape of the distribution we are taking an average of): + histos.add((folder + "/hRingObservableCounts").c_str(), "Counts vs <#it{R}>_{jet};<#it{R}>; Counts", kTH1D, {axisConfigurations.axisRingCounts}); histos.add((folder + "/pRingObservablePhiJet").c_str(), "<#it{R}> vs #varphi_{jet};#varphi_{jet};<#it{R}>", kTProfile, {axisConfigurations.axisPhi}); histos.add((folder + "/pRingObservablePhiLambda").c_str(), "<#it{R}> vs #varphi_{#Lambda};#varphi_{#Lambda};<#it{R}>", kTProfile, {axisConfigurations.axisPhi}); histos.add((folder + "/pRingObservableDeltaTheta").c_str(), "<#it{R}> vs #Delta#theta_{jet};#Delta#theta_{jet};<#it{R}>", kTProfile, {axisConfigurations.axisDeltaTheta}); @@ -506,20 +516,22 @@ struct lambdajetpolarizationionsderived { histos.add((folder + "/ProxyPtDependence/pRingVsPtLeadP_NegEtaLeadP_NegEtaV0").c_str(), "<#it{R}> vs LeadP #it{p}_{T} (#eta_{LeadP}<0, #eta_{V0}<0);#it{p}_{T}^{LeadP} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisJetPt}); // Understanding eta dependence seen in pRingEtaCuts: - histos.add((folder + "/EtaDependence/pRingObservableEtaLambda").c_str(), "<#it{R}> vs #eta_{#Lambda};#eta_{#Lambda};<#it{R}>", kTProfile, {axisConfigurations.axisEtaCoarse}); + histos.add((folder + "/EtaDependence/pRingObservableEtaLambda").c_str(), "<#it{R}> vs #eta_{#Lambda};#eta_{#Lambda};<#it{R}>", kTProfile, {axisConfigurations.axisV0EtaCoarse}); histos.add((folder + "/EtaDependence/pRingObservableEtaJet").c_str(), "<#it{R}> vs #eta_{Jet};#eta_{Jet};<#it{R}>", kTProfile, {axisConfigurations.axisEtaCoarse}); - histos.add((folder + "/EtaDependence/pRingObservableEtaLambda2ndJet").c_str(), "<#it{R}> vs #eta_{#Lambda} (SubJet);#eta_{#Lambda};<#it{R}>", kTProfile, {axisConfigurations.axisEtaCoarse}); + histos.add((folder + "/EtaDependence/pRingObservableEtaLambda2ndJet").c_str(), "<#it{R}> vs #eta_{#Lambda} (SubJet);#eta_{#Lambda};<#it{R}>", kTProfile, {axisConfigurations.axisV0EtaCoarse}); histos.add((folder + "/EtaDependence/pRingObservableEta2ndJet").c_str(), "<#it{R}> vs #eta_{SubJet};#eta_{SubJet};<#it{R}>", kTProfile, {axisConfigurations.axisEtaCoarse}); - histos.add((folder + "/EtaDependence/pRingObservableEtaLambdaLeadP").c_str(), "<#it{R}> vs #eta_{#Lambda} (LeadP);#eta_{#Lambda};<#it{R}>", kTProfile, {axisConfigurations.axisEtaCoarse}); + histos.add((folder + "/EtaDependence/pRingObservableEtaLambdaLeadP").c_str(), "<#it{R}> vs #eta_{#Lambda} (LeadP);#eta_{#Lambda};<#it{R}>", kTProfile, {axisConfigurations.axisV0EtaCoarse}); histos.add((folder + "/EtaDependence/pRingObservableEtaLeadP").c_str(), "<#it{R}> vs #eta_{LeadP};#eta_{LeadP};<#it{R}>", kTProfile, {axisConfigurations.axisEtaCoarse}); // For the leading particle: + histos.add((folder + "/hRingObservableLeadPCounts").c_str(), "Counts vs <#it{R}>_{LeadP};<#it{R}>; Counts", kTH1D, {axisConfigurations.axisRingCounts}); histos.add((folder + "/pRingObservableLeadPDeltaPhi").c_str(), "<#it{R}> vs #Delta#varphi_{LeadP};#Delta#varphi_{LeadP};<#it{R}>", kTProfile, {axisConfigurations.axisDeltaPhi}); histos.add((folder + "/pRingObservableLeadPDeltaTheta").c_str(), "<#it{R}> vs #Delta#theta_{LeadP};#Delta#theta_{LeadP};<#it{R}>", kTProfile, {axisConfigurations.axisDeltaTheta}); histos.add((folder + "/pRingObservableLeadPIntegrated").c_str(), "Integrated <#it{R}> (LeadP); ;<#it{R}>", kTProfile, {{1, -0.5, 0.5}}); histos.add((folder + "/pRingObservableLeadPLambdaPt").c_str(), "<#it{R}> vs #it{p}_{T}^{#Lambda} (LeadP);#it{p}_{T}^{#Lambda} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisPt}); // For the second-to-leading jet: + histos.add((folder + "/hRingObservable2ndJetCounter").c_str(), "Counts vs <#it{R}>_{SubJet};<#it{R}>; Counts", kTH1D, {axisConfigurations.axisRingCounts}); histos.add((folder + "/pRingObservable2ndJetDeltaPhi").c_str(), "<#it{R}> vs #Delta#varphi_{SubJet};#Delta#varphi_{SubJet};<#it{R}>", kTProfile, {axisConfigurations.axisDeltaPhi}); histos.add((folder + "/pRingObservable2ndJetDeltaTheta").c_str(), "<#it{R}> vs #Delta#theta_{SubJet};#Delta#theta_{SubJet};<#it{R}>", kTProfile, {axisConfigurations.axisDeltaTheta}); histos.add((folder + "/pRingObservable2ndJetIntegrated").c_str(), "Integrated <#it{R}> (SubJet); ;<#it{R}>", kTProfile, {{1, -0.5, 0.5}}); @@ -568,15 +580,15 @@ struct lambdajetpolarizationionsderived { // TProfile2D: vs Eta Lambda vs Eta Jet (Understanding eta dependence seen in pRingEtaCuts) histos.add((folder + "/EtaDependence/hCounterEtaLambdaMinusEtaJet").c_str(), "N_{V0s} vs #eta_{#Lambda} - #eta_{Jet};#eta_{#Lambda} - #eta_{Jet}; N_{V0s}", kTH1D, {axisConfigurations.axisDeltaEtaCoarse}); histos.add((folder + "/EtaDependence/pRingObservableEtaLambdaMinusEtaJet").c_str(), "<#it{R}> vs #eta_{#Lambda} - #eta_{Jet};#eta_{#Lambda} - #eta_{Jet};<#it{R}>", kTProfile, {axisConfigurations.axisDeltaEtaCoarse}); - histos.add((folder + "/EtaDependence/p2dRingObservableEtaLambdaVsEtaJet").c_str(), "<#it{R}> vs #eta_{#Lambda} vs #eta_{Jet};#eta_{#Lambda};#eta_{Jet};<#it{R}>", kTProfile2D, {axisConfigurations.axisEtaCoarse, axisConfigurations.axisEtaCoarse}); - histos.add((folder + "/EtaDependence/p2dRingObservableEtaLambdaVsEtaJet_FineBins").c_str(), "<#it{R}> vs #eta_{#Lambda} vs #eta_{Jet} (fine bins);#eta_{#Lambda};#eta_{Jet};<#it{R}>", kTProfile2D, {axisConfigurations.axisEta, axisConfigurations.axisEta}); - histos.add((folder + "/EtaDependence/p2dRingObservableEtaLambdaVsEtaLeadP").c_str(), "<#it{R}> vs #eta_{#Lambda} vs #eta_{LeadP};#eta_{#Lambda};#eta_{LeadP};<#it{R}>", kTProfile2D, {axisConfigurations.axisEtaCoarse, axisConfigurations.axisEtaCoarse}); - histos.add((folder + "/EtaDependence/p2dRingObservableEtaLambdaVsEta2ndJet").c_str(), "<#it{R}> vs #eta_{#Lambda} vs #eta_{SubJet};#eta_{#Lambda};#eta_{SubJet};<#it{R}>", kTProfile2D, {axisConfigurations.axisEtaCoarse, axisConfigurations.axisEtaCoarse}); + histos.add((folder + "/EtaDependence/p2dRingObservableEtaLambdaVsEtaJet").c_str(), "<#it{R}> vs #eta_{#Lambda} vs #eta_{Jet};#eta_{#Lambda};#eta_{Jet};<#it{R}>", kTProfile2D, {axisConfigurations.axisV0EtaCoarse, axisConfigurations.axisEtaCoarse}); + histos.add((folder + "/EtaDependence/p2dRingObservableEtaLambdaVsEtaJet_FineBins").c_str(), "<#it{R}> vs #eta_{#Lambda} vs #eta_{Jet} (fine bins);#eta_{#Lambda};#eta_{Jet};<#it{R}>", kTProfile2D, {axisConfigurations.axisV0Eta, axisConfigurations.axisEta}); + histos.add((folder + "/EtaDependence/p2dRingObservableEtaLambdaVsEtaLeadP").c_str(), "<#it{R}> vs #eta_{#Lambda} vs #eta_{LeadP};#eta_{#Lambda};#eta_{LeadP};<#it{R}>", kTProfile2D, {axisConfigurations.axisV0EtaCoarse, axisConfigurations.axisEtaCoarse}); + histos.add((folder + "/EtaDependence/p2dRingObservableEtaLambdaVsEta2ndJet").c_str(), "<#it{R}> vs #eta_{#Lambda} vs #eta_{SubJet};#eta_{#Lambda};#eta_{SubJet};<#it{R}>", kTProfile2D, {axisConfigurations.axisV0EtaCoarse, axisConfigurations.axisEtaCoarse}); // Counters for these histograms, instead of only TProfile2Ds: - histos.add((folder + "/EtaDependence/h2dCounterEtaLambdaVsEtaJet").c_str(), "Counts, #eta_{#Lambda} vs #eta_{Jet};#eta_{#Lambda};#eta_{Jet};Counts", kTH2D, {axisConfigurations.axisEtaCoarse, axisConfigurations.axisEtaCoarse}); - histos.add((folder + "/EtaDependence/h2dCounterEtaLambdaVsEtaJet_FineBins").c_str(), "Counts (fine bins), #eta_{#Lambda} vs #eta_{Jet};#eta_{#Lambda};#eta_{Jet};Counts", kTH2D, {axisConfigurations.axisEta, axisConfigurations.axisEta}); - histos.add((folder + "/EtaDependence/h2dCounterEtaLambdaVsEtaLeadP").c_str(), "Counts, #eta_{#Lambda} vs #eta_{LeadP};#eta_{#Lambda};#eta_{LeadP};Counts", kTH2D, {axisConfigurations.axisEtaCoarse, axisConfigurations.axisEtaCoarse}); - histos.add((folder + "/EtaDependence/h2dCounterEtaLambdaVsEta2ndJet").c_str(), "Counts, #eta_{#Lambda} vs #eta_{SubJet};#eta_{#Lambda};#eta_{SubJet};Counts", kTH2D, {axisConfigurations.axisEtaCoarse, axisConfigurations.axisEtaCoarse}); + histos.add((folder + "/EtaDependence/h2dCounterEtaLambdaVsEtaJet").c_str(), "Counts, #eta_{#Lambda} vs #eta_{Jet};#eta_{#Lambda};#eta_{Jet};Counts", kTH2D, {axisConfigurations.axisV0EtaCoarse, axisConfigurations.axisEtaCoarse}); + histos.add((folder + "/EtaDependence/h2dCounterEtaLambdaVsEtaJet_FineBins").c_str(), "Counts (fine bins), #eta_{#Lambda} vs #eta_{Jet};#eta_{#Lambda};#eta_{Jet};Counts", kTH2D, {axisConfigurations.axisV0Eta, axisConfigurations.axisEta}); + histos.add((folder + "/EtaDependence/h2dCounterEtaLambdaVsEtaLeadP").c_str(), "Counts, #eta_{#Lambda} vs #eta_{LeadP};#eta_{#Lambda};#eta_{LeadP};Counts", kTH2D, {axisConfigurations.axisV0EtaCoarse, axisConfigurations.axisEtaCoarse}); + histos.add((folder + "/EtaDependence/h2dCounterEtaLambdaVsEta2ndJet").c_str(), "Counts, #eta_{#Lambda} vs #eta_{SubJet};#eta_{#Lambda};#eta_{SubJet};Counts", kTH2D, {axisConfigurations.axisV0EtaCoarse, axisConfigurations.axisEtaCoarse}); // --- TProfile3D: vs DeltaPhi vs Mass vs LambdaPt --- histos.add((folder + "/p3dRingObservableDeltaPhiVsMassVsLambdaPt").c_str(), "<#it{R}> vs #Delta#varphi_{jet} vs Mass vs #it{p}_{T}^{#Lambda};#Delta#varphi_{jet};m_{p#pi} (GeV/c^{2});#it{p}_{T}^{#Lambda} (GeV/c)", kTProfile3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisPtSigExtract}); // --- TProfile3D: vs DeltaTheta vs Mass vs LambdaPt --- @@ -822,6 +834,17 @@ struct lambdajetpolarizationionsderived { histos.add("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAdauVsEtaJet", "FakePolSignal; #phi^{*}; DCA_{V0 Daughters}; #eta_{Jet} sign", kTProfile3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDCAdau, {2, -0.9, 0.9}}); histos.add("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAdauVsEtaLambda", "FakePolSignal; #phi^{*}; DCA_{V0 Daughters}; #eta_{#Lambda} sign", kTProfile3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDCAdau, {2, -0.9, 0.9}}); + // Similar checks for dcaPosToPV and dcaNegToPV, which influence AEE the strongest: + histos.add("HelicityEfficiencyQA/hFakePolCountsJet_PhiStarVsDCAProLike", "FakePolCounts - HasValidLeadJet OK; #phi^{*}; DCA_{PosPV}", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDCAdauPV}); + histos.add("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAProLike", "FakePolSignal; #phi^{*}; DCA_{PosPV}", kTProfile2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDCAdauPV}); + histos.add("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAProLikeVsEtaJet", "FakePolSignal; #phi^{*}; DCA_{PosPV}; #eta_{Jet} sign", kTProfile3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDCAdauPV, {2, -0.9, 0.9}}); + histos.add("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAProLikeVsEtaLambda", "FakePolSignal; #phi^{*}; DCA_{PosPV}; #eta_{#Lambda} sign", kTProfile3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDCAdauPV, {2, -0.9, 0.9}}); + + histos.add("HelicityEfficiencyQA/hFakePolCountsJet_PhiStarVsDCAPiLike", "FakePolCounts - HasValidLeadJet OK; #phi^{*}; DCA_{NegPV}", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDCAdauPV}); + histos.add("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAPiLike", "FakePolSignal; #phi^{*}; DCA_{NegPV}", kTProfile2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDCAdauPV}); + histos.add("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAPiLikeVsEtaJet", "FakePolSignal; #phi^{*}; DCA_{NegPV}; #eta_{Jet} sign", kTProfile3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDCAdauPV, {2, -0.9, 0.9}}); + histos.add("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAPiLikeVsEtaLambda", "FakePolSignal; #phi^{*}; DCA_{NegPV}; #eta_{#Lambda} sign", kTProfile3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDCAdauPV, {2, -0.9, 0.9}}); + // Doing the same HEE study for leading particles: // (eta_{Jet} may be a bad estimator!) histos.add("EtaStudy/hFakePolCountsLeadP", "FakePolCounts; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda};", kTH2D, {axisConfigurations.axisCosTheta, {9, 0, 9}}); @@ -1045,33 +1068,41 @@ struct lambdajetpolarizationionsderived { unitVec = XYZVector(cosPhi * inverseCoshEta, sinPhi * inverseCoshEta, std::tanh(eta)); } - // TODO: avoid recalculation for forcePreviousJet and forceDatalikeJet, where we already know eta and phi variables // Recalculating pT, phi and eta after distortions: // (without this, later kinematic selections make no sense at all! In the forceRandJet case, the ring observable would always sum zero) if (hasValidProxy) { // If you don't check this flag here, the "if (!usableProxy)" change would be silently overwritten - // Calculating total jet momentum, which should be preserved, just to recalculate the new jet Pt: - const double mag = pt * std::cosh(eta); - - // For stability (Rho is the projection on the transverse plane, badly behaved for high |eta|): - const double transverseNorm = std::max(unitVec.Rho(), 1e-12); // Stability guard - - // Recalculate pT: - pt = mag * transverseNorm; - - // Recalculate phi: - phi = std::atan2(unitVec.Y(), unitVec.X()); - if (phi < 0.0f) - phi += o2::constants::math::TwoPI; // Notice that atan2 outputs values from -PI to PI, and DataModel convention was from 0 to 2PI as per FastJet's phi() getter - - // Stable eta computation: - // Stabler than 0.5 * std::log((1. + cosTheta) / (1. - cosTheta)) - eta = std::asinh(unitVec.Z() / transverseNorm); + if (!forcePreviousJet && !forceDatalikeJet){ // In these two cases, we already know the eta and phi variables. No need to recompute + // Calculating total jet momentum, which should be preserved, just to recalculate the new jet Pt: + const double mag = pt * std::cosh(eta); + + // For stability (Rho is the projection on the transverse plane, badly behaved for high |eta|): + const double transverseNorm = std::max(unitVec.Rho(), 1e-12); // Stability guard + + // Recalculate pT: + pt = mag * transverseNorm; + + // Recalculate phi: + phi = std::atan2(unitVec.Y(), unitVec.X()); + if (phi < 0.0f) + phi += o2::constants::math::TwoPI; // Notice that atan2 outputs values from -PI to PI, and DataModel convention was from 0 to 2PI as per FastJet's phi() getter + + // Stable eta computation: + // Stabler than 0.5 * std::log((1. + cosTheta) / (1. - cosTheta)) + eta = std::asinh(unitVec.Z() / transverseNorm); + } // Recalculate the bool after distortion to see if we proceed with this jet proxy: hasValidProxy = pt > minPtThreshold; } } + // Caching the previous collision's jet directions -- An optional feature for forcePreviousJet QA: + struct PrevJetCache { + bool hadLeadJet; float leadJetEta; float leadJetPhi; // Leading jet + bool hadSubJet; float subJetEta; float subJetPhi; // Subleading jet + bool hadLeadP; float leadPEta; float leadPPhi; // Leading particle + }; + // Preslices for correct collisions association: // (TODO: test using custom grouping) Preslice perColJets = o2::aod::lambdajetpol::ringCollisionId; @@ -1080,14 +1111,9 @@ struct lambdajetpolarizationionsderived { void processPolarizationData(o2::aod::RingCollisions const& collisions, o2::aod::RingJets const& jets, o2::aod::RingLaV0s const& v0s, o2::aod::RingLeadPs const& leadPs) { - // Caching the previous collision's jet directions -- An optional feature for forcePreviousJet: - struct PrevJetCache { - bool hadLeadJet; float leadJetEta; float leadJetPhi; // Leading jet - bool hadSubJet; float subJetEta; float subJetPhi; // Subleading jet - bool hadLeadP; float leadPEta; float leadPPhi; // Leading particle - }; PrevJetCache prevJetCache{}; // Initializing struct before the collisions loop. This zero-initializes, so bools start as false + for (int idxResampling = 0; idxResampling < nProxyResamples; idxResampling++){ // resampling loop for forceRandJet and forceDatalikeJet for (auto const& collision : collisions) { const auto collId = collision.globalIndex(); // The self-index accessor const double centrality = getCentrality(collision); @@ -1288,6 +1314,8 @@ struct lambdajetpolarizationionsderived { float protonLikePt = 0; float protonLikeEta = 0; float protonLikePhi = 0; + float protonLikeDCADauToPV = 0; + float pionLikeDCADauToPV = 0; if (isLambda) { if (!analyseLambda) continue; @@ -1295,6 +1323,8 @@ struct lambdajetpolarizationionsderived { protonLikePt = v0.posPt(); protonLikeEta = v0.posEta(); protonLikePhi = v0.posPhi(); + protonLikeDCADauToPV = v0.dcaPosToPV(); + pionLikeDCADauToPV = v0.dcaNegToPV(); } else if (isAntiLambda) { // (TODO: add a split histogram where you consider Lambda and AntiLambda polarization separately?) if (!analyseAntiLambda) continue; @@ -1302,6 +1332,8 @@ struct lambdajetpolarizationionsderived { protonLikePt = v0.negPt(); protonLikeEta = v0.negEta(); protonLikePhi = v0.negPhi(); + protonLikeDCADauToPV = v0.dcaNegToPV(); + pionLikeDCADauToPV = v0.dcaPosToPV(); } PtEtaPhiMVector lambdaLike4Vec(v0pt, v0eta, v0phi, v0LambdaLikeMass); @@ -1521,6 +1553,18 @@ struct lambdajetpolarizationionsderived { histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAdauVsEtaJet"), phiStar, dcaDau, leadingJetEta, ringObservable); histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAdauVsEtaLambda"), phiStar, dcaDau, v0eta, ringObservable); + // DCA dau to PV correlation: + // For proton-like daughter: + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsJet_PhiStarVsDCAProLike"), phiStar, protonLikeDCADauToPV); + histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAProLike"), phiStar, protonLikeDCADauToPV, ringObservable); + histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAProLikeVsEtaJet"), phiStar, protonLikeDCADauToPV, leadingJetEta, ringObservable); + histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAProLikeVsEtaLambda"), phiStar, protonLikeDCADauToPV, v0eta, ringObservable); + // For pion-like daughter: + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsJet_PhiStarVsDCAPiLike"), phiStar, pionLikeDCADauToPV); + histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAPiLike"), phiStar, pionLikeDCADauToPV, ringObservable); + histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAPiLikeVsEtaJet"), phiStar, pionLikeDCADauToPV, leadingJetEta, ringObservable); + histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAPiLikeVsEtaLambda"), phiStar, pionLikeDCADauToPV, v0eta, ringObservable); + histos.fill(HIST("HelicityEfficiencyQA/pRingVsJetZcomponent"), Jz, ringObservable); histos.fill(HIST("HelicityEfficiencyQA/pRingOverJetZcomponent_VsJetEta"), leadingJetEta, ringObservableOverJetZ); histos.fill(HIST("HelicityEfficiencyQA/pRingOverJetZcomponent_VsCosThetaHEE"), cosFakePol, ringObservableOverJetZ); @@ -1767,6 +1811,7 @@ struct lambdajetpolarizationionsderived { FLUSH_DELTA_TRACKER("JetKinematicCuts", trackJetKinCuts, mAxisPt, mAxisMass, mAxisDTheta) FLUSH_DELTA_TRACKER("JetAndLambdaKinematicCuts", trackJetLambdaKinCuts, mAxisPt, mAxisMass, mAxisDTheta) } // end collisions + } // end of resampling loop for forceRandJet and forceDatalikeJet } PROCESS_SWITCH(lambdajetpolarizationionsderived, processPolarizationData, "Process derived data in Run 3 Data", true); From 3eb261bbe9f39cfa9b11f87a870d3a17c5e2a456 Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Fri, 7 Aug 2026 14:08:57 +0000 Subject: [PATCH 16/31] Major rework of the Ring Polarization datamodel, bug fixing in QA histograms of TableProducer, organizing Consumer and adding new histograms. - Datamodel: Added Zvtx and IR to event table. Removed jetNConstituents and simplified V0 table for a new format that doesn't store ambiguous Lambda/antiLambda candidates. - TableProducer: Adequation to new datamodel. Organized event selections, changed V0 selection defaults, fixed hEventSelection and hSelectionV0s - Consumer: Added 2D histograms for polarization vector-field plotting. Implemented per-family switches for histogram declaration and filling, in order to avoid the max 512 limit on histograms declared to a single HistogramRegistry. Adequations and optimizations to new datamodel where we don't store ambiguous Lambda/antiLambda candidates. To-do: QA the interaction rate in larger datasets (currently running). Check if the new "fetch IR in all collisions" philosophy does not impact performance in an unsustainable way. --- PWGLF/DataModel/lambdaJetPolarizationIons.h | 73 +++-- .../Strangeness/lambdaJetPolarizationIons.cxx | 301 +++++++++++------- .../lambdaJetPolarizationIonsDerived.cxx | 276 +++++++++++----- 3 files changed, 420 insertions(+), 230 deletions(-) diff --git a/PWGLF/DataModel/lambdaJetPolarizationIons.h b/PWGLF/DataModel/lambdaJetPolarizationIons.h index e25a4e8c992..e17c0250f62 100644 --- a/PWGLF/DataModel/lambdaJetPolarizationIons.h +++ b/PWGLF/DataModel/lambdaJetPolarizationIons.h @@ -34,13 +34,16 @@ namespace lambdajetpol DECLARE_SOA_COLUMN(CentFT0M, centFT0M, float); DECLARE_SOA_COLUMN(CentFT0C, centFT0C, float); DECLARE_SOA_COLUMN(CentFV0A, centFV0A, float); +DECLARE_SOA_COLUMN(Zvtx, zvtx, float); +DECLARE_SOA_COLUMN(InteractionRate, interactionRate, float); // DECLARE_SOA_COLUMN(MagField, magField, float); // Jet (and jet proxies) information: DECLARE_SOA_COLUMN(JetPt, jetPt, float); DECLARE_SOA_COLUMN(JetEta, jetEta, float); DECLARE_SOA_COLUMN(JetPhi, jetPhi, float); -DECLARE_SOA_COLUMN(JetNConstituents, jetNConstituents, int); +// DECLARE_SOA_COLUMN(JetNConstituents, jetNConstituents, uint16_t); // Currently removed from datamodel. + // Other variables can better reveal jet quenching and help identify good selection criteria for quenched jets proxies DECLARE_SOA_COLUMN(LeadParticlePt, leadParticlePt, float); DECLARE_SOA_COLUMN(LeadParticleEta, leadParticleEta, float); @@ -51,22 +54,27 @@ DECLARE_SOA_COLUMN(V0Pt, v0Pt, float); DECLARE_SOA_COLUMN(V0Eta, v0Eta, float); DECLARE_SOA_COLUMN(V0Phi, v0Phi, float); -DECLARE_SOA_COLUMN(IsLambda, isLambda, bool); -DECLARE_SOA_COLUMN(IsAntiLambda, isAntiLambda, bool); -DECLARE_SOA_COLUMN(MassLambda, massLambda, float); -DECLARE_SOA_COLUMN(MassAntiLambda, massAntiLambda, float); +DECLARE_SOA_COLUMN(IsLambda, isLambda, bool); //! 0: antiLambda, 1: Lambda. There are no ambiguous candidates stored (those that pass both Lambda-specific and AntiLambda-specific checks) +// DECLARE_SOA_COLUMN(IsAntiLambda, isAntiLambda, bool); +DECLARE_SOA_COLUMN(MassV0, massV0, float); +// DECLARE_SOA_COLUMN(MassLambda, massLambda, float); +// DECLARE_SOA_COLUMN(MassAntiLambda, massAntiLambda, float); -DECLARE_SOA_COLUMN(PosPt, posPt, float); +DECLARE_SOA_COLUMN(PosPt, posPt, float); // Could consider rewriting this as proton/pion-like Pt, as in TPCNSigma, instead of Pos/Neg DECLARE_SOA_COLUMN(PosEta, posEta, float); DECLARE_SOA_COLUMN(PosPhi, posPhi, float); DECLARE_SOA_COLUMN(NegPt, negPt, float); DECLARE_SOA_COLUMN(NegEta, negEta, float); DECLARE_SOA_COLUMN(NegPhi, negPhi, float); -DECLARE_SOA_COLUMN(PosTPCNSigmaPr, posTPCNSigmaPr, float); -DECLARE_SOA_COLUMN(PosTPCNSigmaPi, posTPCNSigmaPi, float); -DECLARE_SOA_COLUMN(NegTPCNSigmaPr, negTPCNSigmaPr, float); -DECLARE_SOA_COLUMN(NegTPCNSigmaPi, negTPCNSigmaPi, float); +// DECLARE_SOA_COLUMN(PosTPCNSigmaPr, posTPCNSigmaPr, float); +// DECLARE_SOA_COLUMN(PosTPCNSigmaPi, posTPCNSigmaPi, float); +// DECLARE_SOA_COLUMN(NegTPCNSigmaPr, negTPCNSigmaPr, float); +// DECLARE_SOA_COLUMN(NegTPCNSigmaPi, negTPCNSigmaPi, float); +// TPC Nsigma variables now stored only for non-ambiguous candidates (those that pass only the Lambda-specific or antiLambda-specific tests): +// (PrLike: proton-like daughter of the V0, either a proton (Lambda) or an antiproton (AntiLambda). PiLike is the pion daughter) +DECLARE_SOA_COLUMN(RoundPrLikeTPCNSigma, roundPrLikeTPCNSigma, int16_t); //! Stores nsigma with a precision of 0.01 nsigma. Following LFStrangenessPIDTables.h +DECLARE_SOA_COLUMN(RoundPiLikeTPCNSigma, roundPiLikeTPCNSigma, int16_t); DECLARE_SOA_COLUMN(V0CosPA, v0CosPA, float); DECLARE_SOA_COLUMN(V0Radius, v0Radius, float); @@ -75,38 +83,46 @@ DECLARE_SOA_COLUMN(DcaPosToPV, dcaPosToPV, float); DECLARE_SOA_COLUMN(DcaNegToPV, dcaNegToPV, float); // Dynamic columns for jets (Px,Py,Pz): -DECLARE_SOA_DYNAMIC_COLUMN(JetPx, jetPx, //! Jet px +DECLARE_SOA_DYNAMIC_COLUMN(JetPx, jetPx, [](float jetPt, float jetPhi) -> float { return jetPt * std::cos(jetPhi); }); -DECLARE_SOA_DYNAMIC_COLUMN(JetPy, jetPy, //! Jet py +DECLARE_SOA_DYNAMIC_COLUMN(JetPy, jetPy, [](float jetPt, float jetPhi) -> float { return jetPt * std::sin(jetPhi); }); -DECLARE_SOA_DYNAMIC_COLUMN(JetPz, jetPz, //! Jet pz +DECLARE_SOA_DYNAMIC_COLUMN(JetPz, jetPz, [](float jetPt, float jetEta) -> float { return jetPt * std::sinh(jetEta); }); // Same for leading particles: -DECLARE_SOA_DYNAMIC_COLUMN(LeadParticlePx, leadParticlePx, //! Leading particle px +DECLARE_SOA_DYNAMIC_COLUMN(LeadParticlePx, leadParticlePx, [](float leadParticlePt, float leadParticlePhi) -> float { return leadParticlePt * std::cos(leadParticlePhi); }); -DECLARE_SOA_DYNAMIC_COLUMN(LeadParticlePy, leadParticlePy, //! Leading particle py +DECLARE_SOA_DYNAMIC_COLUMN(LeadParticlePy, leadParticlePy, [](float leadParticlePt, float leadParticlePhi) -> float { return leadParticlePt * std::sin(leadParticlePhi); }); -DECLARE_SOA_DYNAMIC_COLUMN(LeadParticlePz, leadParticlePz, //! Leading particle pz +DECLARE_SOA_DYNAMIC_COLUMN(LeadParticlePz, leadParticlePz, [](float leadParticlePt, float leadParticleEta) -> float { return leadParticlePt * std::sinh(leadParticleEta); }); + +// Dynamic columns for retrieving rounded Nsigma columns: +DECLARE_SOA_DYNAMIC_COLUMN(PrLikeTPCNSigma, prLikeTPCNSigma, + [](int16_t packedValue) -> float { return packedValue / 100.f; }); +DECLARE_SOA_DYNAMIC_COLUMN(PiLikeTPCNSigma, piLikeTPCNSigma, + [](int16_t packedValue) -> float { return packedValue / 100.f; }); } // namespace lambdajetpol DECLARE_SOA_TABLE(RingCollisions, "AOD", "RINGCOLLISION", - o2::soa::Index<>, // self-index: auto-assigned row number + o2::soa::Index<>, //! self-index: auto-assigned row number lambdajetpol::CentFT0M, lambdajetpol::CentFT0C, - lambdajetpol::CentFV0A); + lambdajetpol::CentFV0A, + lambdajetpol::Zvtx, + lambdajetpol::InteractionRate); namespace lambdajetpol { -DECLARE_SOA_INDEX_COLUMN(RingCollision, ringCollision); // Declare index after table is available +DECLARE_SOA_INDEX_COLUMN(RingCollision, ringCollision); //! Declare index after table is available } // namespace lambdajetpol DECLARE_SOA_TABLE(RingJets, "AOD", "RINGJET", - lambdajetpol::RingCollisionId, // relational index -> RingCollisions + lambdajetpol::RingCollisionId, //! relational index -> RingCollisions lambdajetpol::JetPt, lambdajetpol::JetEta, lambdajetpol::JetPhi, - lambdajetpol::JetNConstituents, + // lambdajetpol::JetNConstituents, // Dynamic columns (explicitly bound to their static inputs): lambdajetpol::JetPx, lambdajetpol::JetPy, @@ -128,24 +144,23 @@ DECLARE_SOA_TABLE(RingLaV0s, "AOD", "RINGLAV0", lambdajetpol::V0Eta, lambdajetpol::V0Phi, lambdajetpol::IsLambda, - lambdajetpol::IsAntiLambda, - lambdajetpol::MassLambda, - lambdajetpol::MassAntiLambda, + lambdajetpol::MassV0, lambdajetpol::PosPt, lambdajetpol::PosEta, lambdajetpol::PosPhi, lambdajetpol::NegPt, lambdajetpol::NegEta, lambdajetpol::NegPhi, - lambdajetpol::PosTPCNSigmaPr, - lambdajetpol::PosTPCNSigmaPi, - lambdajetpol::NegTPCNSigmaPr, - lambdajetpol::NegTPCNSigmaPi, + lambdajetpol::RoundPrLikeTPCNSigma, + lambdajetpol::RoundPiLikeTPCNSigma, lambdajetpol::V0CosPA, lambdajetpol::V0Radius, lambdajetpol::DcaV0Daughters, lambdajetpol::DcaPosToPV, - lambdajetpol::DcaNegToPV); + lambdajetpol::DcaNegToPV, + // Dynamic columns: + lambdajetpol::PrLikeTPCNSigma, + lambdajetpol::PiLikeTPCNSigma); using RingCollision = RingCollisions::iterator; // Useful shorthand } // namespace o2::aod diff --git a/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx b/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx index be22ad11617..84e36b48c7c 100644 --- a/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx +++ b/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx @@ -178,30 +178,34 @@ struct lambdajetpolarizationions { 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"}; // part of sel8, actually + // Detector status: Configurable rejectITSROFBorder{"rejectITSROFBorder", true, "reject events at ITS ROF border (Run 3 only)"}; // part of sel8, actually Configurable rejectTFBorder{"rejectTFBorder", true, "reject events at TF border (Run 3 only)"}; // part of sel8, actually + Configurable requireGoodITSLayersAll{"requireGoodITSLayersAll", false, "require number of inactive chips on all ITS layers to be below maximum allowed values"}; // Based off ITS holes DPG AOT selection + // Vertex quality: Configurable requireIsVertexITSTPC{"requireIsVertexITSTPC", false, "require events with at least one ITS-TPC track (Run 3 only)"}; 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 (Run 3 only)"}; // o2::aod::evsel::kIsGoodZvtxFT0vsPV. Recommended for OO Configurable requireIsVertexTOFmatched{"requireIsVertexTOFmatched", false, "require events with at least one of vertex contributors matched to TOF (Run 3 only)"}; Configurable requireIsVertexTRDmatched{"requireIsVertexTRDmatched", false, "require events with at least one of vertex contributors matched to TRD (Run 3 only)"}; + Configurable maxZVtxPosition{"maxZVtxPosition", 10., "max Z vtx position [cm]"}; + // Pileup and cannibalism rejection: Configurable rejectSameBunchPileup{"rejectSameBunchPileup", true, "reject collisions in case of pileup with another collision in the same foundBC (Run 3 only)"}; // o2::aod::evsel::kNoSameBunchPileup. Recommended for OO 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 (Run 3 only)"}; Configurable requireNoCollInTimeRangeStrict{"requireNoCollInTimeRangeStrict", false, "reject collisions corrupted by the cannibalism, with other collisions within +/- 10 microseconds (Run 3 only)"}; Configurable requireNoCollInTimeRangeNarrow{"requireNoCollInTimeRangeNarrow", false, "reject collisions corrupted by the cannibalism, with other collisions within +/- 2 microseconds (Run 3 only)"}; Configurable requireNoCollInROFStd{"requireNoCollInROFStd", false, "reject collisions corrupted by the cannibalism, with other collisions within the same ITS ROF with mult. above a certain threshold (Run 3 only)"}; Configurable requireNoCollInROFStrict{"requireNoCollInROFStrict", false, "reject collisions corrupted by the cannibalism, with other collisions within the same ITS ROF (Run 3 only)"}; + // Physics selections: Configurable requireINEL0{"requireINEL0", true, "require INEL>0 event selection"}; // Only truly useful in pp Configurable requireINEL1{"requireINEL1", false, "require INEL>1 event selection"}; - - Configurable maxZVtxPosition{"maxZVtxPosition", 10., "max Z vtx position [cm]"}; - + // MC: 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", true, "Apply Z-vtx cut on the PV of the generated collision?"}; // I see no reason as to not do this by default + // Occupancy: Configurable useFT0CbasedOccupancy{"useFT0CbasedOccupancy", false, "Use sum of FT0-C amplitudes for estimating occupancy? (if not, use track-based definition)"}; - // fast check on occupancy Configurable minOccupancy{"minOccupancy", -1, "minimum occupancy from neighbouring collisions"}; Configurable maxOccupancy{"maxOccupancy", -1, "maximum occupancy from neighbouring collisions"}; - // fast check on interaction rate + // Interaction rate: Configurable minIR{"minIR", -1, "minimum IR collisions"}; Configurable maxIR{"maxIR", -1, "maximum IR collisions"}; } eventSelections; @@ -212,19 +216,19 @@ struct lambdajetpolarizationions { Configurable rapidityCut{"rapidityCut", 1.0f, "rapidity"}; // This is actually a physics cut, not a proper acceptance cut // Selection criteria: acceptance - Configurable daughterEtaCut{"daughterEtaCut", 0.9f, "max eta for daughters"}; // Default is 0.8. Changed to 0.9 to agree with jet selection. TODO: test the impact/biasing of this! + // Configurable daughterEtaCut{"daughterEtaCut", 0.9f, "max eta for daughters"}; // Default is 0.8. Changed to 0.9 to agree with jet selection. TODO: test the impact/biasing of this! // Standard 5 topological criteria -- Closed a bit more for the Lambda analysis Configurable v0cospa{"v0cospa", 0.995, "min V0 CosPA"}; // Default is 0.97 - Configurable dcav0dau{"dcav0dau", 1.2, "max DCA V0 Daughters (cm)"}; // Default is 1.0 + Configurable dcav0dau{"dcav0dau", 1.2, "max DCA V0 Daughters (cm)"}; // Default is 1.0 in v0builder // Configurable dcanegtopv{"dcanegtopv", .2, "min DCA Neg To PV (cm)"}; // Default is .05 // Configurable dcapostopv{"dcapostopv", .05, "min DCA Pos To PV (cm)"}; // Default is .05 // Renamed for better consistency of candidate selection (the cut is not determined by charge, but by mass and how deflected the daughter is): Configurable dcaPionToPV{"dcaPionToPV", .05, "min DCA pion-like daughter To PV (cm)"}; // .2 would suppress primary pion background, but we need to reduce fake-polarization signals enhanced by DCA-to-PV cuts Configurable dcaProtonToPV{"dcaProtonToPV", .05, "min DCA proton-like daughter To PV (cm)"}; // Default is .05 - Configurable v0radius{"v0radius", 1.0, "minimum V0 radius (cm)"}; // Default is 1.2 + Configurable v0radius{"v0radius", 1.0, "minimum V0 radius (cm)"}; // Default is 1.2 Configurable v0radiusMax{"v0radiusMax", 1E5, "maximum V0 radius (cm)"}; - Configurable lambdaLifetimeCut{"lambdaLifetimeCut", 30., "lifetime cut (c*tau) for Lambda (cm)"}; + Configurable lambdaLifetimeCut{"lambdaLifetimeCut", 30., "lifetime cut (c*tau) for Lambda (cm)"}; // For soft Lambdas, expect V0Radius < 30 cm. For hard ones, it can be > 30 cm. // invariant mass selection Configurable compMassRejection{"compMassRejection", -1, "Competing mass rejection (GeV/#it{c}^{2})"}; // This was creating bumps in the pp analysis code's invariant mass. Turned off for now. @@ -249,7 +253,7 @@ struct lambdajetpolarizationions { Configurable phiHighCut{"phiHighCut", "0.1/x+pi/18.0+0.06", "High azimuth cut parametrisation"}; // PID (TPC/TOF) - Configurable tpcPidNsigmaCut{"tpcPidNsigmaCut", 4, "tpcPidNsigmaCut"}; // Default is 5 + Configurable tpcPidNsigmaCut{"tpcPidNsigmaCut", 5, "tpcPidNsigmaCut"}; // Default is 5 Configurable tofPidNsigmaCutLaPr{"tofPidNsigmaCutLaPr", 1e+6, "tofPidNsigmaCutLaPr"}; Configurable tofPidNsigmaCutLaPi{"tofPidNsigmaCutLaPi", 1e+6, "tofPidNsigmaCutLaPi"}; @@ -265,7 +269,7 @@ struct lambdajetpolarizationions { // Run Condition Table (RCT) configurables struct : ConfigurableGroup { std::string prefix = "rctConfigurations"; // JSON group name - Configurable cfgRCTLabel{"cfgRCTLabel", "", "Which detector condition requirements? (CBT, CBT_hadronPID, CBT_electronPID, CBT_calo, CBT_muon, CBT_muon_glo)"}; + Configurable cfgRCTLabel{"cfgRCTLabel", "CBT", "Which detector condition requirements? (CBT, CBT_hadronPID, CBT_electronPID, CBT_calo, CBT_muon, CBT_muon_glo)"}; Configurable cfgCheckZDC{"cfgCheckZDC", false, "Include ZDC flags in the bit selection (for Pb-Pb only)"}; Configurable cfgTreatLimitedAcceptanceAsBad{"cfgTreatLimitedAcceptanceAsBad", false, "reject all events where the detectors relevant for the specified Runlist are flagged as LimitedAcceptance"}; } rctConfigurations; @@ -307,22 +311,21 @@ struct lambdajetpolarizationions { ConfigurableAxis axisPtCoarse{"axisPtCoarse", {VARIABLE_WIDTH, 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 7.0f, 10.0f, 15.0f}, "pt axis for QA"}; ConfigurableAxis axisLambdaMass{"axisLambdaMass", {450, 1.08f, 1.15f}, ""}; // Default is {200, 1.101f, 1.131f} ConfigurableAxis axisLambdaMassCoarse{"axisLambdaMassCoarse", {200, 1.101f, 1.131f}, ""}; + // Centrality/IR/Occupancy: ConfigurableAxis axisCentrality{"axisCentrality", {VARIABLE_WIDTH, 0.0f, 5.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 60.0f, 70.0f, 80.0f, 90.0f}, "Centrality"}; ConfigurableAxis axisNch{"axisNch", {500, 0.0f, +5000.0f}, "Number of charged particles"}; ConfigurableAxis axisIRBinning{"axisIRBinning", {500, 0, 50}, "Binning for the interaction rate (kHz)"}; ConfigurableAxis axisMultFT0M{"axisMultFT0M", {500, 0.0f, +100000.0f}, "Multiplicity FT0M"}; ConfigurableAxis axisMultFT0C{"axisMultFT0C", {500, 0.0f, +10000.0f}, "Multiplicity FT0C"}; ConfigurableAxis axisMultFV0A{"axisMultFV0A", {500, 0.0f, +100000.0f}, "Multiplicity FV0A"}; - ConfigurableAxis axisRawCentrality{"axisRawCentrality", {VARIABLE_WIDTH, 0.000f, 52.320f, 75.400f, 95.719f, 115.364f, 135.211f, 155.791f, 177.504f, 200.686f, 225.641f, 252.645f, 281.906f, 313.850f, 348.302f, 385.732f, 426.307f, 470.146f, 517.555f, 568.899f, 624.177f, 684.021f, 748.734f, 818.078f, 892.577f, 973.087f, 1058.789f, 1150.915f, 1249.319f, 1354.279f, 1465.979f, 1584.790f, 1710.778f, 1844.863f, 1985.746f, 2134.643f, 2291.610f, 2456.943f, 2630.653f, 2813.959f, 3006.631f, 3207.229f, 3417.641f, 3637.318f, 3865.785f, 4104.997f, 4354.938f, 4615.786f, 4885.335f, 5166.555f, 5458.021f, 5762.584f, 6077.881f, 6406.834f, 6746.435f, 7097.958f, 7462.579f, 7839.165f, 8231.629f, 8635.640f, 9052.000f, 9484.268f, 9929.111f, 10389.350f, 10862.059f, 11352.185f, 11856.823f, 12380.371f, 12920.401f, 13476.971f, 14053.087f, 14646.190f, 15258.426f, 15890.617f, 16544.433f, 17218.024f, 17913.465f, 18631.374f, 19374.983f, 20136.700f, 20927.783f, 21746.796f, 22590.880f, 23465.734f, 24372.274f, 25314.351f, 26290.488f, 27300.899f, 28347.512f, 29436.133f, 30567.840f, 31746.818f, 32982.664f, 34276.329f, 35624.859f, 37042.588f, 38546.609f, 40139.742f, 41837.980f, 43679.429f, 45892.130f, 400000.000f}, "raw centrality signal"}; // for QA - ConfigurableAxis axisOccupancy{"axisOccupancy", {VARIABLE_WIDTH, 0.0f, 250.0f, 500.0f, 750.0f, 1000.0f, 1500.0f, 2000.0f, 3000.0f, 4500.0f, 6000.0f, 8000.0f, 10000.0f, 50000.0f}, "Occupancy"}; - - // topological variable QA axes - ConfigurableAxis axisDCAtoPV{"axisDCAtoPV", {20, 0.0f, 1.0f}, "DCA (cm)"}; + // Topological variable QA axes: + ConfigurableAxis axisDCAtoPV{"axisDCAtoPV", {30, 0.0f, 2.0f}, "DCA (cm)"}; ConfigurableAxis axisDCAdau{"axisDCAdau", {20, 0.0f, 1.2f}, "DCA (cm)"}; // This should follow v0Selections.dcav0dau's default - ConfigurableAxis axisPointingAngle{"axisPointingAngle", {20, 0.0f, 2.0f}, "pointing angle (rad)"}; - ConfigurableAxis axisV0Radius{"axisV0Radius", {20, 0.0f, 60.0f}, "V0 2D radius (cm)"}; + ConfigurableAxis axisPointingAngle{"axisPointingAngle", {20, 0.0f, 0.4f}, "pointing angle (rad)"}; // cos(0.4 rad) is ~0.92 for cosPA + ConfigurableAxis axisV0Radius{"axisV0Radius", {30, 0.0f, 60.0f}, "V0 2D radius (cm)"}; + // PID: ConfigurableAxis axisNsigmaTPC{"axisNsigmaTPC", {200, -10.0f, 10.0f}, "N sigma TPC"}; ConfigurableAxis axisTPCsignal{"axisTPCsignal", {200, 0.0f, 200.0f}, "TPC signal"}; ConfigurableAxis axisNsigmaTOF{"axisNsigmaTOF", {200, -10.0f, 10.0f}, "N sigma TOF"}; @@ -331,30 +334,28 @@ struct lambdajetpolarizationions { ConfigurableAxis axisPhiMod{"axisPhiMod", {100, 0.0f, constants::math::TwoPI / 18}, "Azimuth angle wrt TPC sector (rad.)"}; ConfigurableAxis axisEta{"axisEta", {50, -1.0f, 1.0f}, "#eta"}; ConfigurableAxis axisRapidity{"axisRapidity", {50, -1.0f, 1.0f}, "y"}; - ConfigurableAxis axisITSchi2{"axisITSchi2", {100, 0.0f, 100.0f}, "#chi^{2} per ITS clusters"}; - ConfigurableAxis axisTPCchi2{"axisTPCchi2", {100, 0.0f, 100.0f}, "#chi^{2} per TPC clusters"}; - ConfigurableAxis axisTPCrowsOverFindable{"axisTPCrowsOverFindable", {120, 0.0f, 1.2f}, "Fraction of TPC crossed rows over findable clusters"}; - ConfigurableAxis axisTPCfoundOverFindable{"axisTPCfoundOverFindable", {120, 0.0f, 1.2f}, "Fraction of TPC found over findable clusters"}; - ConfigurableAxis axisTPCsharedClusters{"axisTPCsharedClusters", {101, -0.005f, 1.005f}, "Fraction of TPC shared clusters"}; - - // AP plot axes + // AP plot axes ConfigurableAxis axisAPAlpha{"axisAPAlpha", {220, -1.1f, 1.1f}, "V0 AP alpha"}; ConfigurableAxis axisAPQt{"axisAPQt", {220, 0.0f, 0.5f}, "V0 AP alpha"}; - // Track quality axes ConfigurableAxis axisTPCrows{"axisTPCrows", {160, 0.0f, 160.0f}, "N TPC rows"}; ConfigurableAxis axisITSclus{"axisITSclus", {7, 0.0f, 7.0f}, "N ITS Clusters"}; - ConfigurableAxis axisITScluMap{"axisITScluMap", {128, -0.5f, 127.5f}, "ITS Cluster map"}; - ConfigurableAxis axisDetMap{"axisDetMap", {16, -0.5f, 15.5f}, "Detector use map"}; - ConfigurableAxis axisITScluMapCoarse{"axisITScluMapCoarse", {16, -3.5f, 12.5f}, "ITS Coarse cluster map"}; - ConfigurableAxis axisDetMapCoarse{"axisDetMapCoarse", {5, -0.5f, 4.5f}, "Detector Coarse user map"}; + // ConfigurableAxis axisITScluMap{"axisITScluMap", {128, -0.5f, 127.5f}, "ITS Cluster map"}; + // ConfigurableAxis axisDetMap{"axisDetMap", {16, -0.5f, 15.5f}, "Detector use map"}; + // ConfigurableAxis axisITScluMapCoarse{"axisITScluMapCoarse", {16, -3.5f, 12.5f}, "ITS Coarse cluster map"}; + // ConfigurableAxis axisDetMapCoarse{"axisDetMapCoarse", {5, -0.5f, 4.5f}, "Detector Coarse user map"}; + ConfigurableAxis axisITSchi2{"axisITSchi2", {100, 0.0f, 100.0f}, "#chi^{2} per ITS clusters"}; + ConfigurableAxis axisTPCchi2{"axisTPCchi2", {100, 0.0f, 100.0f}, "#chi^{2} per TPC clusters"}; + // ConfigurableAxis axisTPCrowsOverFindable{"axisTPCrowsOverFindable", {120, 0.0f, 1.2f}, "Fraction of TPC crossed rows over findable clusters"}; + // ConfigurableAxis axisTPCfoundOverFindable{"axisTPCfoundOverFindable", {120, 0.0f, 1.2f}, "Fraction of TPC found over findable clusters"}; + // ConfigurableAxis axisTPCsharedClusters{"axisTPCsharedClusters", {101, -0.005f, 1.005f}, "Fraction of TPC shared clusters"}; // MC coll assoc QA axis ConfigurableAxis axisMonteCarloNch{"axisMonteCarloNch", {300, 0.0f, 3000.0f}, "N_{ch} MC"}; // Jet QA axes: - ConfigurableAxis JetsPerEvent{"JetsPerEvent", {20, 0, 20}, "Jets per event"}; - + ConfigurableAxis axisJetsPerEvent{"axisJetsPerEvent", {20, 0, 20}, "Jets per event"}; + ConfigurableAxis axisJetConstituents{"axisJetConstituents", {200, 0, 200}, "Number of jet constituents"}; ConfigurableAxis axisLeadingParticlePt{"axisLeadingParticlePt", {200, 0.f, 200.f}, "Leading particle p_{T} (GeV/c)"}; // Simpler version! ConfigurableAxis axisJetPt{"axisJetPt", {200, 0.f, 200.f}, "Jet p_{t} (GeV)"}; ConfigurableAxis axisCosTheta{"axisCosTheta", {50, -1.f, 1.f}, "cos(#Delta #theta_{jet})"}; @@ -368,7 +369,7 @@ struct lambdajetpolarizationions { // (TODO: create a reasonable track selection for full, photon, and Z-tagged jet tracks, including detector angular acceptance parameters for EMCal) struct : ConfigurableGroup { std::string prefix = "jetConfigurations"; // JSON group name - Configurable minJetPt{"minJetPt", 30.0f, "Minimum reconstructed pt of the jet (GeV/c)"}; // Something in between pp and PbPb minima. Change for bkgSubtraction true or false! + Configurable minJetPt{"minJetPt", 20.0f, "Minimum reconstructed pt of the jet (GeV/c)"}; // Something in between pp and PbPb minima. Change for bkgSubtraction true or false! Configurable radiusJet{"radiusJet", 0.4f, "Jet resolution parameter (R)"}; // (TODO: check if the JE people don't define this as a rescaled int to not lose precision for stricter selections) // Notice that the maximum Eta of the jet will then be 0.9 - R to keep the jet contained within the ITS+TPC barrel. @@ -478,70 +479,72 @@ struct lambdajetpolarizationions { rctFlagsChecker.init(rctConfigurations.cfgRCTLabel.value, rctConfigurations.cfgCheckZDC, rctConfigurations.cfgTreatLimitedAcceptanceAsBad); // Event Counters - histos.add("hEventSelection", "hEventSelection", kTH1D, {{23, -0.5f, +20.5f}}); + histos.add("hEventSelection", "hEventSelection", kTH1D, {{24, -0.5f, +23.5f}}); histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(1, "All collisions"); histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(2, "sel8 cut"); histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(3, "kIsTriggerTVX"); histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(4, "kNoITSROFrameBorder"); histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(5, "kNoTimeFrameBorder"); - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(6, "posZ cut"); - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(7, "kIsVertexITSTPC"); - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(8, "kIsGoodZvtxFT0vsPV"); - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(9, "kIsVertexTOFmatched"); - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(10, "kIsVertexTRDmatched"); - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(11, "kNoSameBunchPileup"); - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(12, "kNoCollInTimeRangeStd"); - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(13, "kNoCollInTimeRangeStrict"); - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(14, "kNoCollInTimeRangeNarrow"); - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(15, "kNoCollInRofStd"); - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(16, "kNoCollInRofStrict"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(6, "kIsGoodITSLayersAll"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(7, "posZ cut"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(8, "kIsVertexITSTPC"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(9, "kIsGoodZvtxFT0vsPV"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(10, "kIsVertexTOFmatched"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(11, "kIsVertexTRDmatched"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(12, "kNoSameBunchPileup"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(13, "kNoCollInTimeRangeStd"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(14, "kNoCollInTimeRangeStrict"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(15, "kNoCollInTimeRangeNarrow"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(16, "kNoCollInRofStd"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(17, "kNoCollInRofStrict"); if (doPPAnalysis) { - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(17, "INEL>0"); - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(18, "INEL>1"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(18, "INEL>0"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(19, "INEL>1"); } else { - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(17, "Below min occup."); - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(18, "Above max occup."); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(18, "Below min occup."); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(19, "Above max occup."); } - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(19, "Below min IR"); - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(20, "Above max IR"); - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(21, "RCT flags"); - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(22, "hasRingJet"); - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(23, "hasRingV0"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(20, "Below min IR"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(21, "Above max IR"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(22, "RCT flags"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(23, "hasRingJet"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(24, "hasRingV0"); // (notice we lack a hasRingJet AND hasRingV0 bin because the tasks run separately on all events!) // (this QA number can be obtained at derived data level with ease) histos.add("Centrality/hEventCentrality", "hEventCentrality", kTH1D, {{101, 0.0f, 101.0f}}); histos.add("Centrality/hCentralityVsNch", "hCentralityVsNch", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisNch}); if (doEventQA) { - histos.add("hEventSelectionVsCentrality", "hEventSelectionVsCentrality", kTH2D, {{23, -0.5f, +20.5f}, {101, 0.0f, 101.0f}}); + histos.add("hEventSelectionVsCentrality", "hEventSelectionVsCentrality", kTH2D, {{24, -0.5f, +23.5f}, {101, 0.0f, 101.0f}}); histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(1, "All collisions"); histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(2, "sel8 cut"); histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(3, "kIsTriggerTVX"); histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(4, "kNoITSROFrameBorder"); histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(5, "kNoTimeFrameBorder"); - histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(6, "posZ cut"); - histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(7, "kIsVertexITSTPC"); - histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(8, "kIsGoodZvtxFT0vsPV"); - histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(9, "kIsVertexTOFmatched"); - histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(10, "kIsVertexTRDmatched"); - histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(11, "kNoSameBunchPileup"); - histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(12, "kNoCollInTimeRangeStd"); - histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(13, "kNoCollInTimeRangeStrict"); - histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(14, "kNoCollInTimeRangeNarrow"); - histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(15, "kNoCollInRofStd"); - histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(16, "kNoCollInRofStrict"); + histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(6, "kIsGoodITSLayersAll"); + histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(7, "posZ cut"); + histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(8, "kIsVertexITSTPC"); + histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(9, "kIsGoodZvtxFT0vsPV"); + histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(10, "kIsVertexTOFmatched"); + histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(11, "kIsVertexTRDmatched"); + histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(12, "kNoSameBunchPileup"); + histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(13, "kNoCollInTimeRangeStd"); + histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(14, "kNoCollInTimeRangeStrict"); + histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(15, "kNoCollInTimeRangeNarrow"); + histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(16, "kNoCollInRofStd"); + histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(17, "kNoCollInRofStrict"); if (doPPAnalysis) { - histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(17, "INEL>0"); - histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(18, "INEL>1"); + histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(18, "INEL>0"); + histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(19, "INEL>1"); } else { - histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(17, "Below min occup."); - histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(18, "Above max occup."); + histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(18, "Below min occup."); + histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(19, "Above max occup."); } - histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(19, "Below min IR"); - histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(20, "Above max IR"); - histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(21, "RCT flags"); - histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(22, "hasRingJet"); - histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(23, "hasRingV0"); + histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(20, "Below min IR"); + histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(21, "Above max IR"); + histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(22, "RCT flags"); + histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(23, "hasRingJet"); + histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(24, "hasRingV0"); // Centrality: histos.add("Centrality/hEventCentVsMultFT0M", "hEventCentVsMultFT0M", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisMultFT0M}); @@ -622,7 +625,10 @@ struct lambdajetpolarizationions { {p + "TOF #Delta t #pi", v0Selections.maxDeltaTimePion < 1e+9}, {p + "TOF PID p", v0Selections.tofPidNsigmaCutLaPr < 1e+6}, {p + "TOF PID #pi", v0Selections.tofPidNsigmaCutLaPi < 1e+6}, - {p + "c#tau", v0Selections.lambdaLifetimeCut > 0}}); + {p + "c#tau", v0Selections.lambdaLifetimeCut > 0}, + {p + "Ambiguous rejection", true}, + {p + "Final accepted", true}} + ); }; constexpr bool Lambda = true; // Some constexpr to make it more readable (works at compile level) constexpr bool AntiLambda = false; // "false" is just a flag for this addHypothesis function! It just means fill "AntiLambda" labels @@ -689,8 +695,8 @@ struct lambdajetpolarizationions { if (analyseLambda) { histos.add("Lambda/h2dNbrOfLambdaVsCentrality", "h2dNbrOfLambdaVsCentrality", kTH2D, {axisConfigurations.axisCentrality, {10, -0.5f, 9.5f}}); histos.add("Lambda/h3dMassLambda", "h3dMassLambda", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisLambdaMass}); - // Non-UPC info - histos.add("Lambda/h3dMassLambdaHadronic", "h3dMassLambdaHadronic", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisLambdaMass}); + // // Non-UPC info + // histos.add("Lambda/h3dMassLambdaHadronic", "h3dMassLambdaHadronic", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisLambdaMass}); if (doTPCQA) { histos.add("Lambda/h3dPosNsigmaTPC", "h3dPosNsigmaTPC", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTPC}); histos.add("Lambda/h3dNegNsigmaTPC", "h3dNegNsigmaTPC", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTPC}); @@ -724,8 +730,8 @@ struct lambdajetpolarizationions { if (analyseAntiLambda) { histos.add("AntiLambda/h2dNbrOfAntiLambdaVsCentrality", "h2dNbrOfAntiLambdaVsCentrality", kTH2D, {axisConfigurations.axisCentrality, {10, -0.5f, 9.5f}}); histos.add("AntiLambda/h3dMassAntiLambda", "h3dMassAntiLambda", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisLambdaMass}); - // Non-UPC info - histos.add("AntiLambda/h3dMassAntiLambdaHadronic", "h3dMassAntiLambdaHadronic", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisLambdaMass}); + // // Non-UPC info + // histos.add("AntiLambda/h3dMassAntiLambdaHadronic", "h3dMassAntiLambdaHadronic", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisLambdaMass}); if (doTPCQA) { histos.add("AntiLambda/h3dPosNsigmaTPC", "h3dPosNsigmaTPC", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTPC}); histos.add("AntiLambda/h3dNegNsigmaTPC", "h3dNegNsigmaTPC", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTPC}); @@ -846,12 +852,14 @@ struct lambdajetpolarizationions { histos.add("GeneralQA/h2dArmenterosFullSelectedAmbiguous", "h2dArmenterosFullSelectedAmbiguous", kTH2D, {axisConfigurations.axisAPAlpha, axisConfigurations.axisAPQt}); // Jets histograms: - // Histogram that needs to be present even out of QA: - histos.add("hEventsWithJet", "hEventsWithJet", kTH1D, {{1, 0, 1}}); - histos.add("hJetsPerEvent", "hJetsPerEvent", kTH1D, {axisConfigurations.JetsPerEvent}); - // counter of events with jet (could be interesting to compare with the minimum pT cut or between the background subtraction vs no background subtraction cases) - // number of jets per event + // Histograms that need to be present even out of extensive QA-mode: + histos.add("hEventsWithJet", "hEventsWithJet", kTH1D, {{1, 0, 1}}); // counter of events with jet (could be interesting to compare with the minimum pT cut or between the background subtraction vs no background subtraction cases) + histos.add("hJetsPerEvent", "hJetsPerEvent", kTH1D, {axisConfigurations.axisJetsPerEvent}); // number of jets per event if (doJetKinematicsQA) { + // For probing the correlation between number of jet constituents and centrality: + histos.add("JetKinematicsQA/h2dConstituentsPerJetVsCentrality", "Constituents per Jet Vs Centrality", kTH2D, {axisConfigurations.axisJetConstituents, axisConfigurations.axisCentrality}); // Not quite kinematics, but kept it under this QAing switch for now + histos.add("JetKinematicsQA/h2dConstituentsPerJetVsJetPt", "Constituents per Jet Vs Jet Pt", kTH2D, {axisConfigurations.axisJetConstituents, axisConfigurations.axisJetPt}); + histos.add("JetKinematicsQA/hJetPt", "hJetPt", kTH1D, {axisConfigurations.axisJetPt}); histos.add("JetKinematicsQA/hJetEta", "hJetEta", kTH1D, {axisConfigurations.axisEta}); histos.add("JetKinematicsQA/hJetPhi", "hJetPhi", kTH1D, {axisConfigurations.axisPhi}); @@ -866,8 +874,8 @@ struct lambdajetpolarizationions { histos.add("JetKinematicsQA/hLeadingJetPhi", "hLeadingJetPhi", kTH1D, {axisConfigurations.axisPhi}); // 2D correlations: - histos.add("JetKinematicsQA/h2dJetsPerEventvsLeadJetPt", "h2dJetsPerEventvsLeadJetPt", kTH2D, {axisConfigurations.JetsPerEvent, axisConfigurations.axisJetPt}); - histos.add("JetKinematicsQA/h2dJetsPerEventvsJetPt", "h2dJetsPerEventvsJetPt", kTH2D, {axisConfigurations.JetsPerEvent, axisConfigurations.axisJetPt}); + histos.add("JetKinematicsQA/h2dJetsPerEventvsLeadJetPt", "h2dJetsPerEventvsLeadJetPt", kTH2D, {axisConfigurations.axisJetsPerEvent, axisConfigurations.axisJetPt}); + histos.add("JetKinematicsQA/h2dJetsPerEventvsJetPt", "h2dJetsPerEventvsJetPt", kTH2D, {axisConfigurations.axisJetsPerEvent, axisConfigurations.axisJetPt}); histos.add("JetKinematicsQA/h2dCosThetaToLeadvsDeltaPhiToLead", "h2dCosThetaToLeadvsDeltaPhiToLead", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisDeltaPhi}); histos.add("JetKinematicsQA/h2dCosThetaToLeadvsDeltaEtaToLead", "h2dCosThetaToLeadvsDeltaEtaToLead", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisDeltaEta}); histos.add("JetKinematicsQA/h2dCosThetaToLeadvsDeltaRToLead", "h2dCosThetaToLeadvsDeltaRToLead", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisDeltaR}); @@ -879,9 +887,9 @@ struct lambdajetpolarizationions { histos.add("JetKinematicsQA/h2dJetEnergyvsCosThetaToLead", "h2dJetEnergyvsCosThetaToLead", kTH2D, {axisConfigurations.axisEnergy, axisConfigurations.axisCosTheta}); // Jets per event vs correlation to lead jet - histos.add("JetKinematicsQA/h2dJetsPerEventvsDeltaPhiToLead", "h2dJetsPerEventvsDeltaPhiToLead", kTH2D, {axisConfigurations.JetsPerEvent, axisConfigurations.axisDeltaPhi}); - histos.add("JetKinematicsQA/h2dJetsPerEventvsDeltaEtaToLead", "h2dJetsPerEventvsDeltaEtaToLead", kTH2D, {axisConfigurations.JetsPerEvent, axisConfigurations.axisDeltaEta}); - histos.add("JetKinematicsQA/h2dJetsPerEventvsCosThetaToLead", "h2dJetsPerEventvsCosThetaToLead", kTH2D, {axisConfigurations.JetsPerEvent, axisConfigurations.axisCosTheta}); + histos.add("JetKinematicsQA/h2dJetsPerEventvsDeltaPhiToLead", "h2dJetsPerEventvsDeltaPhiToLead", kTH2D, {axisConfigurations.axisJetsPerEvent, axisConfigurations.axisDeltaPhi}); + histos.add("JetKinematicsQA/h2dJetsPerEventvsDeltaEtaToLead", "h2dJetsPerEventvsDeltaEtaToLead", kTH2D, {axisConfigurations.axisJetsPerEvent, axisConfigurations.axisDeltaEta}); + histos.add("JetKinematicsQA/h2dJetsPerEventvsCosThetaToLead", "h2dJetsPerEventvsCosThetaToLead", kTH2D, {axisConfigurations.axisJetsPerEvent, axisConfigurations.axisCosTheta}); //////////////////////////// // Leading particle 1D QA: @@ -898,12 +906,12 @@ struct lambdajetpolarizationions { histos.add("JetVsLeadingParticleQA/h2dDeltaPhiParticleToLeadvsDeltaEtaParticleToLead", "h2dDeltaPhiParticleToLeadvsDeltaEtaParticleToLead", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDeltaEta}); // Jets-per-event vs particle-to-lead correlations: - histos.add("JetVsLeadingParticleQA/h2dJetsPerEventvsDeltaPhiParticleToLead", "h2dJetsPerEventvsDeltaPhiParticleToLead", kTH2D, {axisConfigurations.JetsPerEvent, axisConfigurations.axisDeltaPhi}); - histos.add("JetVsLeadingParticleQA/h2dJetsPerEventvsDeltaEtaParticleToLead", "h2dJetsPerEventvsDeltaEtaParticleToLead", kTH2D, {axisConfigurations.JetsPerEvent, axisConfigurations.axisDeltaEta}); - histos.add("JetVsLeadingParticleQA/h2dJetsPerEventvsCosThetaParticleToLead", "h2dJetsPerEventvsCosThetaParticleToLead", kTH2D, {axisConfigurations.JetsPerEvent, axisConfigurations.axisCosTheta}); + histos.add("JetVsLeadingParticleQA/h2dJetsPerEventvsDeltaPhiParticleToLead", "h2dJetsPerEventvsDeltaPhiParticleToLead", kTH2D, {axisConfigurations.axisJetsPerEvent, axisConfigurations.axisDeltaPhi}); + histos.add("JetVsLeadingParticleQA/h2dJetsPerEventvsDeltaEtaParticleToLead", "h2dJetsPerEventvsDeltaEtaParticleToLead", kTH2D, {axisConfigurations.axisJetsPerEvent, axisConfigurations.axisDeltaEta}); + histos.add("JetVsLeadingParticleQA/h2dJetsPerEventvsCosThetaParticleToLead", "h2dJetsPerEventvsCosThetaParticleToLead", kTH2D, {axisConfigurations.axisJetsPerEvent, axisConfigurations.axisCosTheta}); // Main "Leading jet vs leading particle" correlations: - histos.add("JetVsLeadingParticleQA/h2dJetsPerEventvsLeadParticlePt", "h2dJetsPerEventvsLeadParticlePt", kTH2D, {axisConfigurations.JetsPerEvent, axisConfigurations.axisLeadingParticlePt}); + histos.add("JetVsLeadingParticleQA/h2dJetsPerEventvsLeadParticlePt", "h2dJetsPerEventvsLeadParticlePt", kTH2D, {axisConfigurations.axisJetsPerEvent, axisConfigurations.axisLeadingParticlePt}); histos.add("JetVsLeadingParticleQA/h2dLeadJetPtvsLeadParticlePt", "h2dLeadJetPtvsLeadParticlePt", kTH2D, {axisConfigurations.axisJetPt, axisConfigurations.axisLeadingParticlePt}); histos.add("JetVsLeadingParticleQA/h2dLeadJetPtvsCosThetaParticleToLead", "h2dLeadJetPtvsCosThetaParticleToLead", kTH2D, {axisConfigurations.axisJetPt, axisConfigurations.axisCosTheta}); histos.add("JetVsLeadingParticleQA/h2dLeadParticlePtvsCosThetaParticleToLead", "h2dLeadParticlePtvsCosThetaParticleToLead", kTH2D, {axisConfigurations.axisLeadingParticlePt, axisConfigurations.axisCosTheta}); @@ -975,9 +983,10 @@ struct lambdajetpolarizationions { }; V0SelectionFlowCounter V0SelCounter{-1, &histos}; // Could initialize with any index (resetForNewV0 is always called for a new V0 anyways) // Calculating some bins, for convenience: - int nGenericCuts = 31; // x=0 to x=30 - int nHypoCuts = 9; // per hypothesis (x=31..39 for Lambda) - int lambdaHypoEnd = nGenericCuts + nHypoCuts - 1; // x=39 + const int nGenericCuts = 31; // x=0 to x=30 (bins 1 and 31, as the lower-edge is inclusive) + const int nHypoCuts = 11; // per hypothesis (x=31..41 for Lambda) + const int lambdaHypoEnd = nGenericCuts + nHypoCuts - 1; // x=41 (bin 42) + const int antiLambdaHypoEnd = nGenericCuts + 2*nHypoCuts - 1; // x = 52 (bin 53) // Minimal helper to fill hSelectionJetTracks, mirroring V0SelectionFlowCounter. // Reset once per track candidate, fill once per passed cut stage. @@ -1053,10 +1062,16 @@ struct lambdajetpolarizationions { return dot / (magA * magB); } + const float NSigmaScale = 100.f; // The factor for packing the float as an int16_t + inline int16_t packNSigma(float x) + { + return static_cast(std::lround(x * NSigmaScale)); + } + ///////////////////////////////////////////// // Helper functions for event and candidate selection: template - bool isEventAccepted(TCollision const& collision, TBC const& bc, float centrality, bool fillHists) + bool isEventAccepted(TCollision const& collision, TBC const& bc, float centrality, double& interactionRate, bool fillHists) { // check whether the collision passes our collision selections int selectionIdx = 0; // To loop over QA histograms. First bin is already filled: first call will already increment this index (not actually the bin index, but a value in the X axis). if (eventSelections.requireSel8 && !collision.sel8()) @@ -1075,6 +1090,10 @@ struct lambdajetpolarizationions { return false; if (fillHists) fillEventSelectionQA(++selectionIdx, centrality); + if (eventSelections.requireGoodITSLayersAll && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) + return false; + if (fillHists) + fillEventSelectionQA(++selectionIdx, centrality); const float collisionPVz = collision.posZ(); if (std::abs(collisionPVz) > eventSelections.maxZVtxPosition) @@ -1151,8 +1170,11 @@ struct lambdajetpolarizationions { if (fillHists) fillEventSelectionQA(++selectionIdx, centrality); - // Fetch interaction rate only if required (in order to limit ccdb calls) - const double interactionRate = (eventSelections.minIR >= 0 || eventSelections.maxIR >= 0) ? rateFetcher.fetch(ccdb.service, bc.timestamp(), bc.runNumber(), irSource) * 1.e-3 : -1; + // Fetch interaction rate after all inexpensive cuts were performed: + interactionRate = rateFetcher.fetch(ccdb.service, bc.timestamp(), bc.runNumber(), irSource) * 1.e-3; + // Optionally, do this only when required by the minIR/maxIR cuts, to further limit ccdb calls: + // const double interactionRate = (eventSelections.minIR >= 0 || eventSelections.maxIR >= 0) ? rateFetcher.fetch(ccdb.service, bc.timestamp(), bc.runNumber(), irSource) * 1.e-3 : -1; + if (eventSelections.minIR >= 0 && interactionRate < eventSelections.minIR) return false; if (fillHists) @@ -1540,8 +1562,8 @@ struct lambdajetpolarizationions { tableJets(ringCollIdx, jetMinusBkg.pt(), jetMinusBkg.eta(), // Using eta instead of rapidity - jetMinusBkg.phi(), - jetMinusBkg.constituents().size()); + jetMinusBkg.phi()); + // jetMinusBkg.constituents().size()); // Finding the leading jet after subtraction (leading jet is NOT known a priori!): if (jetMinusBkg.pt() > leadingJetPt) { @@ -1555,9 +1577,11 @@ struct lambdajetpolarizationions { histos.fill(HIST("hEventsWithJet"), 0.5); // Another version of this counter, which is already integrated in the Event Selection flow: if (doEventQA) - fillEventSelectionQA(lastBinEvSel - 1, centrality); // hasRingJet passes + fillEventSelectionQA(lastBinEvSel - 2, centrality); // hasRingJet passes if (doJetKinematicsQA) { + histos.fill(HIST("JetKinematicsQA/h2dConstituentsPerJetVsCentrality"), leadingJetSub.constituents().size(), centrality); + histos.fill(HIST("JetKinematicsQA/h2dConstituentsPerJetVsJetPt"), leadingJetSub.constituents().size(), leadingJetSub.pt()); histos.fill(HIST("JetKinematicsQA/hLeadingJetPt"), leadingJetSub.pt()); histos.fill(HIST("JetKinematicsQA/hLeadingJetEta"), leadingJetSub.eta()); histos.fill(HIST("JetKinematicsQA/hLeadingJetPhi"), leadingJetSub.phi()); @@ -1638,7 +1662,7 @@ struct lambdajetpolarizationions { histos.fill(HIST("hEventsWithJet"), 0.5); // Another version of this counter, which is already integrated in the Event Selection flow: if (doEventQA) - fillEventSelectionQA(lastBinEvSel - 1, centrality); // hasRingJet passes + fillEventSelectionQA(lastBinEvSel - 2, centrality); // hasRingJet passes const auto& leadingJet = jets[0]; // A leading jet before eta cuts. Different type of QA from what we will be able to do down the consumer pipeline for (const auto& jet : jets) { @@ -1650,10 +1674,13 @@ struct lambdajetpolarizationions { tableJets(ringCollIdx, jet.pt(), jet_eta, // Using eta instead of rapidity - jet.phi(), - jet.constituents().size()); + jet.phi()); + // jet.constituents().size()); // Currently removed from datamodel. + // Other variables can better reveal jet quenching and help identify good selection criteria for quenched jets proxies if (doJetKinematicsQA) { + histos.fill(HIST("JetKinematicsQA/h2dConstituentsPerJetVsCentrality"), jet.constituents().size(), centrality); + histos.fill(HIST("JetKinematicsQA/h2dConstituentsPerJetVsJetPt"), jet.constituents().size(), jet.pt()); histos.fill(HIST("JetKinematicsQA/hJetPt"), jet.pt()); histos.fill(HIST("JetKinematicsQA/hJetEta"), jet_eta); histos.fill(HIST("JetKinematicsQA/hJetPhi"), jet.phi()); @@ -1688,7 +1715,9 @@ struct lambdajetpolarizationions { histos.fill(HIST("JetKinematicsQA/h2dJetsPerEventvsCosThetaToLead"), jetsInEvent, cosTheta); } } - if (doJetKinematicsQA) { // Fills even when the jet is outside of the (0.9f - jetConfigurations.radiusJet) eta window. + if (doJetKinematicsQA) { // Fills even when the jet is outside of the (0.9f - jetConfigurations.radiusJet) eta window. Fills at least for the leading jet. + histos.fill(HIST("JetKinematicsQA/h2dConstituentsPerJetVsCentrality"), leadingJet.constituents().size(), centrality); + histos.fill(HIST("JetKinematicsQA/h2dConstituentsPerJetVsJetPt"), leadingJet.constituents().size(), leadingJet.pt()); histos.fill(HIST("JetKinematicsQA/hLeadingJetPt"), leadingJet.pt()); histos.fill(HIST("JetKinematicsQA/hLeadingJetEta"), leadingJet.eta()); histos.fill(HIST("JetKinematicsQA/hLeadingJetPhi"), leadingJet.phi()); @@ -1739,7 +1768,11 @@ struct lambdajetpolarizationions { histos.fill(HIST("hEventSelectionVsCentrality"), 0. /* all collisions */, centrality); auto bc = bcs.iteratorAt(collision.bcId()); - if (!isEventAccepted(collision, bc, centrality, doEventQA)) + + // Initialize interactionRate with a sentinel value. It will be stored in the derived data afterwards: + // (We pass this by reference and update it inside isEventAccepted after all less expensive checks were already done) + double interactionRate = -1.0; + if (!isEventAccepted(collision, bc, centrality, interactionRate, doEventQA)) return; // Uses return instead of continue, as there is no explicit loop here if (doEventQA) @@ -1747,10 +1780,16 @@ struct lambdajetpolarizationions { if (v0Selections.rejectTPCsectorBoundary) initCCDB(bc); // Substituted call from collision to bc for raw data + // If the event passed but skipped the Pb-Pb block (e.g. it was a pp event), fetch the rate before saving in the datamodel: + if (interactionRate < 0) + interactionRate = rateFetcher.fetch(ccdb.service, bc.timestamp(), bc.runNumber(), irSource) * 1.e-3; + // Fill event table: tableCollisions(collision.centFT0M(), collision.centFT0C(), - collision.centFV0A()); // (TODO: add InteractionRate info and other useful cuts for later on in the analysis) + collision.centFV0A(), + collision.posZ(), + interactionRate); // Get the derived collision row index for this event: const int ringCollIdx = tableCollisions.lastIndex(); @@ -1781,12 +1820,12 @@ struct lambdajetpolarizationions { isLambda = passesLambdaLambdaBarHypothesis(v0, collision, true); if (analyseAntiLambda) { if (analyseLambda) // We only need to advance when the Lambda hypothesis had an early exit on the counters - V0SelCounter.advanceTo(lambdaHypoEnd + 1); // sync to bin 41 (x=40 means bin 41, the first #bar{#Lambda} bin) + V0SelCounter.advanceTo(lambdaHypoEnd + 1); // sync to bin 43 (x=42 means bin 43, the first #bar{#Lambda} bin) isAntiLambda = passesLambdaLambdaBarHypothesis(v0, collision, false); } if (!isLambda && !isAntiLambda) - continue; // Candidate is not considered to be a Lambda-like + continue; // Candidate is not considered to be Lambda-like if (isLambda) NLambdas++; @@ -1821,27 +1860,49 @@ struct lambdajetpolarizationions { } // if (lambdaIdx == kIsArmenterosK0) continue; // Should just skip this step then! - if (doEventQA) - fillEventSelectionQA(lastBinEvSel, centrality); // hasRingV0 passes - - // // Extra competing mass rejection of Lambdas // (TODO: test competing mass cuts) + // // Extra competing mass rejection of Lambdas // (TODO: test competing mass cuts and implement it here) // v0.mLambda() + // Bookkeeping for the candidates that passed eveything until the ambiguity check: + if (isLambda) { // Go back to Lambda bin to fill it as well: + V0SelCounter.advanceTo(lambdaHypoEnd-1); // Bin 41: Ambiguous Lambda rejection + V0SelCounter.fill(); + } + if (isAntiLambda) { + V0SelCounter.advanceTo(antiLambdaHypoEnd-1); // Bin 52: Ambiguous antiLambda rejection + V0SelCounter.fill(); + } + + // Removing ambiguous Lambda candidates for final storage, AFTER some QAing: + if (isLambda && isAntiLambda) + continue; + + // Updating selection counters after ambiguous candidate rejection: + if (isLambda) { + V0SelCounter.advanceTo(lambdaHypoEnd); // Bin 42: Final accepted Lambda V0s + V0SelCounter.fill(); + } else { // i.e., isAntiLambda + V0SelCounter.advanceTo(antiLambdaHypoEnd); // Bin 53: Final accepted anti-Lambda V0s + V0SelCounter.fill(); + } + // Saving the Lambdas into a derived data column: auto const v0pt = v0.pt(); const auto posTrackExtra = v0.template posTrack_as(); const auto negTrackExtra = v0.template negTrack_as(); tableV0s(ringCollIdx, v0pt, v0.eta(), v0.phi(), // Using eta instead of rapidity - isLambda, isAntiLambda, - v0.mLambda(), v0.mAntiLambda(), + isLambda, // 0: antiLambda, 1: Lambda. No ambiguous candidates (isLambda && isAntiLambda) are stored + isLambda ? v0.mLambda() : v0.mAntiLambda(), v0.positivept(), v0.positiveeta(), v0.positivephi(), v0.negativept(), v0.negativeeta(), v0.negativephi(), - posTrackExtra.tpcNSigmaPr(), posTrackExtra.tpcNSigmaPi(), - negTrackExtra.tpcNSigmaPr(), negTrackExtra.tpcNSigmaPi(), + packNSigma(isLambda ? posTrackExtra.tpcNSigmaPr() : negTrackExtra.tpcNSigmaPr()), // Proton-like track, packed into int16_t + packNSigma(isLambda ? negTrackExtra.tpcNSigmaPi() : posTrackExtra.tpcNSigmaPi()), // Pion-like track v0.v0cosPA(), v0.v0radius(), v0.dcaV0daughters(), v0.dcapostopv(), v0.dcanegtopv()); + + // Perform extensive QAs for non-ambiguous Lambda/antiLambda candidates only: if (doEventQA && !validV0AlreadyFound) - fillEventSelectionQA(lastBinEvSel, centrality); // hasRingV0 passes + fillEventSelectionQA(lastBinEvSel - 1, centrality); // hasRingV0 passes (lastBinEvSel would be bin 24, but x=23 for a correct fill in our convention) validV0AlreadyFound = true; if (doV0KinematicQA) { diff --git a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx index 06f06e4b725..83449f5064b 100644 --- a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx @@ -149,7 +149,9 @@ enum CentEstimator { /* 3D Profiles: Angle vs Mass vs Centrality */ \ X(FOLDER "/p3dRingObservableDeltaPhiVsMassVsCent", deltaPhiJet, v0LambdaLikeMass, centrality, ringObservable) \ X(FOLDER "/p3dRingObservableDeltaThetaVsMassVsCent", deltaThetaJet, v0LambdaLikeMass, centrality, ringObservable) \ - X(FOLDER "/pRingVsCentrality", centrality, ringObservable) + X(FOLDER "/pRingVsCentrality", centrality, ringObservable) \ + X(FOLDER "/p2dRingObservableVsPxPy", v0px, v0py, ringObservable) \ + X(FOLDER "/p2dRingObservableVsPzPx", v0pz, v0px, ringObservable) // (TODO: add counters for regular TH2Ds about centrality) // For leading particle @@ -246,7 +248,14 @@ enum CentEstimator { /* =============================== */ \ X(FOLDER "/QA/p2dPxStarDeltaPhiVsLambdaPt", deltaPhiJet, v0pt, PolStarX) \ X(FOLDER "/QA/p2dPyStarDeltaPhiVsLambdaPt", deltaPhiJet, v0pt, PolStarY) \ - X(FOLDER "/QA/p2dPzStarDeltaPhiVsLambdaPt", deltaPhiJet, v0pt, PolStarZ) + X(FOLDER "/QA/p2dPzStarDeltaPhiVsLambdaPt", deltaPhiJet, v0pt, PolStarZ) \ + /* 2D vector-field profiles vs Lambda momentum plane */ \ + X(FOLDER "/QA/p2dPxStar_vsPxPy", v0px, v0py, PolStarX) \ + X(FOLDER "/QA/p2dPyStar_vsPxPy", v0px, v0py, PolStarY) \ + X(FOLDER "/QA/p2dPzStar_vsPxPy", v0px, v0py, PolStarZ) \ + X(FOLDER "/QA/p2dPxStar_vsPzPx", v0pz, v0px, PolStarX) \ + X(FOLDER "/QA/p2dPyStar_vsPzPx", v0pz, v0px, PolStarY) \ + X(FOLDER "/QA/p2dPzStar_vsPzPx", v0pz, v0px, PolStarZ) // Apply the macros (notice I had to include the semicolon (";") after the function, so you don't need to // write that when calling this APPLY_HISTO_FILL. The code will look weird, but without this the compiler @@ -295,24 +304,39 @@ struct lambdajetpolarizationionsderived { // Define histogram registries: HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - // Master analysis switches + // Master analysis switches: Configurable analyseLambda{"analyseLambda", true, "process Lambda-like candidates"}; Configurable analyseAntiLambda{"analyseAntiLambda", false, "process AntiLambda-like candidates"}; Configurable analyseMagField{"analyseMagField", true, "analyse efficiency effects wrt magnetic field"}; // Older DerivedData lacks magField. "if constexpr (requires { collision.magField(); })" would only see the current header definition, so need a flag for retro-comp. // Configurable doPPAnalysis{"doPPAnalysis", false, "if in pp, set to true. Default is HI"}; + // Per-family histogram switches: + // (Each family books >100 histograms, so it is necessary to keep some of these switches off to avoid the HistogramRegistry limit) + struct : ConfigurableGroup { + std::string prefix = "familySwitches"; // JSON group name + Configurable doFamilyRing{"doFamilyRing", true, "Book and fill the 'Ring' family (no additional cuts). Keep this on for most passes."}; + Configurable doFamilyRingKinematicCuts{"doFamilyRingKinematicCuts", false, "Book and fill the 'RingKinematicCuts' family (Lambda kinematic cuts applied)."}; + Configurable doFamilyJetKinematicCuts{"doFamilyJetKinematicCuts", false, "Book and fill the 'JetKinematicCuts' family (jet kinematic cuts applied)."}; + Configurable doFamilyJetAndLambdaKinematicCuts{"doFamilyJetAndLambdaKinematicCuts", false, "Book and fill the 'JetAndLambdaKinematicCuts' family (both cuts applied)."}; + } familySwitches; + // Centrality: Configurable centralityEstimator{"centralityEstimator", kCentFT0M, "Run 3 centrality estimator (0:CentFT0C, 1:CentFT0M, 2:CentFV0A)"}; // Default is FT0M + Configurable maxZVtxPosition{"maxZVtxPosition", 10., "max Z vtx position [cm]"}; // An additional post-processing cut after derived data was written. Same default as lambdaJetPolarizationIons.cxx producer - // QAs that purposefully break the analysis + // QAs that purposefully "break" the analysis // -- All of these tests should give us zero signal if the source is truly Lambda Polarization from vortices - Configurable forcePolSignQA{"forcePolSignQA", false, "force antiLambda decay constant to be positive: should kill all the signal, if any. For QA"}; - Configurable forcePerpToJet{"forcePerpToJet", false, "force jet direction to be perpendicular to jet estimator. For QA"}; - Configurable forceJetDirectionSmudge{"forceJetDirectionSmudge", false, "fluctuate jet direction by 10% of R around original axis. For QA (tests sensibility)"}; - Configurable forceRandJet{"forceRandJet", false, "makes jet direction random. A QA for AEE fake signal and its removal"}; - Configurable forcePreviousJet{"forcePreviousJet", false, "uses previous event's jet direction instead of a random sample. A baseline for fake signal removal"}; - Configurable forceDatalikeJet{"forceDatalikeJet", false, "A compromise between forceRandJet and forcePreviousJet. Parameterized distribution from data"}; - Configurable nProxyResamples{"nProxyResamples", 1, "The amount of resamplings of jet direction per event. Use ONLY for forceRandJet and forceDatalikeJet"}; + struct : ConfigurableGroup { + std::string prefix = "fakePolSwitches"; // JSON group name + Configurable forcePolSignQA{"forcePolSignQA", false, "force antiLambda decay constant to be positive: should kill all the signal, if any. For QA"}; + Configurable forcePerpToJet{"forcePerpToJet", false, "force jet direction to be perpendicular to jet estimator. For QA"}; + Configurable forceJetDirectionSmudge{"forceJetDirectionSmudge", false, "fluctuate jet direction by 10% of R around original axis. For QA (tests sensibility)"}; + Configurable forceRandJet{"forceRandJet", false, "makes jet direction random. A QA for AEE fake signal and its removal"}; + Configurable forcePreviousJet{"forcePreviousJet", false, "uses previous event's jet direction instead of a random sample. A baseline for fake signal removal"}; + Configurable forceDatalikeJet{"forceDatalikeJet", false, "A compromise between forceRandJet and forcePreviousJet. Parameterized distribution from data"}; + Configurable nProxyResamples{"nProxyResamples", 1, "The amount of resamplings of jet direction per event. Use ONLY for forceRandJet and forceDatalikeJet"}; + } fakePolSwitches; + // Configurable jetRForSmudging{"jetRForSmudging", 0.4, "QA quantity: the chosen R scale for the jet direction smudge"}; // Superseeded by jetR: kept the same scale in analysis and QA Configurable jetR{"jetR", 0.4f, "Radius of the jet"}; // Provide manually, please. Configurable minLeadParticlePt{"minLeadParticlePt", 2.0f, "Minimum Pt for a lead track to be considered a valid proxy for a jet (may be more restrictive than TableProducer)"}; @@ -326,7 +350,12 @@ struct lambdajetpolarizationionsderived { std::string prefix = "axisConfigurations"; // JSON group name ConfigurableAxis axisPt{"axisPt", {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 analysis"}; ConfigurableAxis axisPtCoarseQA{"axisPtCoarseQA", {VARIABLE_WIDTH, 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 7.0f, 10.0f, 15.0f}, "pt axis for QA"}; - ConfigurableAxis axisLambdaMass{"axisLambdaMass", {450, 1.08f, 1.15f}, "Lambda mass in GeV/c"}; // Default is {200, 1.101f, 1.131f} + ConfigurableAxis axisLambdaMass{"axisLambdaMass", {450, 1.08f, 1.15f}, "#Lambda mass in GeV/c"}; // Default is {200, 1.101f, 1.131f} + + // Symmetric momentum-plane axes for the vector-field / ring 2D profiles: + ConfigurableAxis axisLambdaPx{"axisLambdaPx", {40, -3.0f, 3.0f}, "#Lambda p_{x} (GeV/c)"}; + ConfigurableAxis axisLambdaPy{"axisLambdaPy", {40, -3.0f, 3.0f}, "#Lambda p_{y} (GeV/c)"}; + ConfigurableAxis axisLambdaPz{"axisLambdaPz", {40, -4.0f, 4.0f}, "#Lambda p_{z} (GeV/c)"}; // Jet axes: // ConfigurableAxis axisLeadingParticlePt{"axisLeadingParticlePt", {100, 0.f, 200.f}, "Leading particle p_{T} (GeV/c)"}; // Simpler version! @@ -464,6 +493,24 @@ struct lambdajetpolarizationionsderived { histos.add((folder + "/QA/p2dPyStarDeltaPhiVsLambdaPt").c_str(), "_{y} vs #Delta#varphi_{jet} vs #it{p}_{T}^{#Lambda};#Delta#varphi_{jet};#it{p}_{T}^{#Lambda} (GeV/c);_{y}", kTProfile2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisPtSigExtract}); histos.add((folder + "/QA/p2dPzStarDeltaPhiVsLambdaPt").c_str(), "_{z} vs #Delta#varphi_{jet} vs #it{p}_{T}^{#Lambda};#Delta#varphi_{jet};#it{p}_{T}^{#Lambda} (GeV/c);_{z}", kTProfile2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisPtSigExtract}); + // =============================== + // Vector-field profiles: vs Lambda momentum plane: + // (a companion code plots these as a vector field) + // =============================== + histos.add((folder + "/QA/p2dPxStar_vsPxPy").c_str(), "_{x} vs (p_{x}^{#Lambda},p_{y}^{#Lambda});p_{x}^{#Lambda} (GeV/c);p_{y}^{#Lambda} (GeV/c);_{x}", kTProfile2D, {axisConfigurations.axisLambdaPx, axisConfigurations.axisLambdaPy}); + histos.add((folder + "/QA/p2dPyStar_vsPxPy").c_str(), "_{y} vs (p_{x}^{#Lambda},p_{y}^{#Lambda});p_{x}^{#Lambda} (GeV/c);p_{y}^{#Lambda} (GeV/c);_{y}", kTProfile2D, {axisConfigurations.axisLambdaPx, axisConfigurations.axisLambdaPy}); + histos.add((folder + "/QA/p2dPzStar_vsPxPy").c_str(), "_{z} vs (p_{x}^{#Lambda},p_{y}^{#Lambda}) [colormap];p_{x}^{#Lambda} (GeV/c);p_{y}^{#Lambda} (GeV/c);_{z}", kTProfile2D, {axisConfigurations.axisLambdaPx, axisConfigurations.axisLambdaPy}); + + histos.add((folder + "/QA/p2dPxStar_vsPzPx").c_str(), "_{x} vs (p_{z}^{#Lambda},p_{x}^{#Lambda});p_{z}^{#Lambda} (GeV/c);p_{x}^{#Lambda} (GeV/c);_{x}", kTProfile2D, {axisConfigurations.axisLambdaPz, axisConfigurations.axisLambdaPx}); + histos.add((folder + "/QA/p2dPyStar_vsPzPx").c_str(), "_{y} vs (p_{z}^{#Lambda},p_{x}^{#Lambda}) [colormap];p_{z}^{#Lambda} (GeV/c);p_{x}^{#Lambda} (GeV/c);_{y}", kTProfile2D, {axisConfigurations.axisLambdaPz, axisConfigurations.axisLambdaPx}); + histos.add((folder + "/QA/p2dPzStar_vsPzPx").c_str(), "_{z} vs (p_{z}^{#Lambda},p_{x}^{#Lambda});p_{z}^{#Lambda} (GeV/c);p_{x}^{#Lambda} (GeV/c);_{z}", kTProfile2D, {axisConfigurations.axisLambdaPz, axisConfigurations.axisLambdaPx}); + + // =============================== + // Ring observable, single scalar 2D profile: + // =============================== + histos.add((folder + "/p2dRingObservableVsPxPy").c_str(), "<#it{R}> vs (p_{x}^{#Lambda},p_{y}^{#Lambda});p_{x}^{#Lambda} (GeV/c);p_{y}^{#Lambda} (GeV/c);<#it{R}>", kTProfile2D, {axisConfigurations.axisLambdaPx, axisConfigurations.axisLambdaPy}); + histos.add((folder + "/p2dRingObservableVsPzPx").c_str(), "<#it{R}> vs (p_{z}^{#Lambda},p_{x}^{#Lambda});p_{z}^{#Lambda} (GeV/c);p_{x}^{#Lambda} (GeV/c);<#it{R}>", kTProfile2D, {axisConfigurations.axisLambdaPz, axisConfigurations.axisLambdaPx}); + // TProfiles with correct error bars:: // -- TProfiles will handle the error estimate of the Ring Observable via the variance, even though // they still lack the proper signal extraction and possible efficiency corrections in the current state @@ -634,10 +681,10 @@ struct lambdajetpolarizationionsderived { histos.add((folder + "/DeltaMethod/h2dMassVsDeltaComp").c_str(), "Delta Method vs Mass;m_{p#pi} (GeV/c^{2});Component", kTH2D, {axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisDeltaComponents}); }; // Execute local lambda to register histogram families: - addRingObservableFamily("Ring"); - addRingObservableFamily("RingKinematicCuts"); - addRingObservableFamily("JetKinematicCuts"); - addRingObservableFamily("JetAndLambdaKinematicCuts"); + if (familySwitches.doFamilyRing) addRingObservableFamily("Ring"); + if (familySwitches.doFamilyRingKinematicCuts) addRingObservableFamily("RingKinematicCuts"); + if (familySwitches.doFamilyJetKinematicCuts) addRingObservableFamily("JetKinematicCuts"); + if (familySwitches.doFamilyJetAndLambdaKinematicCuts) addRingObservableFamily("JetAndLambdaKinematicCuts"); histos.add("IntegratedCuts/pRingCuts", "pRingCuts; ;<#it{R}>", kTProfile, {{4, 0, 4}}); histos.get(HIST("IntegratedCuts/pRingCuts"))->GetXaxis()->SetBinLabel(1, "All #Lambda"); @@ -916,7 +963,8 @@ struct lambdajetpolarizationionsderived { // Integrated observable for events with NLambda+NAntiLambda V0s per event // (an interesting measurement of correlation between and Lambda-like V0s multiplicity. A proxy of covariance) // (calculated for leading jets only) - histos.add("IntegratedCuts/pRingVsNV0s", "pRingVsNV0s; N_{#Lambda}+N_{#bar{#Lambda}};<#it{R}>", kTProfile, {{20, 0, 20}}); + histos.add("IntegratedCuts/pRingVsNV0s", "pRingVsNV0s; N_{#Lambda}+N_{#bar{#Lambda}};<#it{R}>", kTProfile, {{20, 0, 20}}); // See hNV0sVsCentrality below for the correlation between number of V0s and centrality + histos.add("hNV0sVsCentrality", "hNV0sVsCentrality; N_{#Lambda}+N_{#bar{#Lambda}};Centrality (%)", kTH2D, {{20, 0, 20}, axisConfigurations.axisCentrality}); // Proxy Eta QA: histos.add("JetKinematicsQA/hLeadJetEta", "hLeadJetEta", kTH1D, {axisConfigurations.axisEta}); @@ -969,12 +1017,12 @@ struct lambdajetpolarizationionsderived { inline void applyProxyDistortion(bool& hasValidProxy, float& pt, float& eta, float& phi, XYZVector& unitVec, float minPtThreshold, bool& cacheHadProxy, float& cacheEta, float& cachePhi, std::discrete_distribution& etaDist, std::discrete_distribution& phiDist, std::mt19937& rng){ - if (!forcePerpToJet && !forceJetDirectionSmudge && !forceRandJet && !forcePreviousJet && !forceDatalikeJet)[[likely]] { + if (!fakePolSwitches.forcePerpToJet && !fakePolSwitches.forceJetDirectionSmudge && !fakePolSwitches.forceRandJet && !fakePolSwitches.forcePreviousJet && !fakePolSwitches.forceDatalikeJet)[[likely]] { return; // Skip this function if none of the modifications are actually being executed! } // QA block -- Purposefully changing the jet direction (should kill signal, if any): - if (forcePerpToJet) { + if (fakePolSwitches.forcePerpToJet) { // First, we build a vector perpendicular to the jet by picking an arbitrary vector not parallel to the jet XYZVector perpVec; if (std::abs(unitVec.X()) > 0.99) { @@ -987,7 +1035,7 @@ struct lambdajetpolarizationionsderived { // We will use Rodrigues' rotation formula (v_rot = v*cos(randomAngle) + (Jet \cross v)*sin(randomAngle)) const double randomAngle = randomGen.Uniform(0., o2::constants::math::TwoPI); unitVec = perpVec * std::cos(randomAngle) + unitVec.Cross(perpVec) * std::sin(randomAngle); - } else if (forceJetDirectionSmudge) { + } else if (fakePolSwitches.forceJetDirectionSmudge) { // Smear the jet direction by a small random angle to estimate sensitivity to // jet axis uncertainty. We rotate the jet axis by angle theta around a uniformly // random perpendicular axis -- this is isotropic and coordinate-independent, @@ -1022,7 +1070,7 @@ struct lambdajetpolarizationionsderived { // But the last term vanishes because smearAxis is perpendicular to unitVec: unitVec = unitVec * std::cos(smearAngle) + smearAxis.Cross(unitVec) * std::sin(smearAngle); // Also, rotation preserves the norm, so no re-normalisation is needed for this to be a unit vector. - } else if (forceRandJet) { + } else if (fakePolSwitches.forceRandJet) { // This randomization was made different for each proxy (LeadP, LeadJet, SubLeadJet): bear that in mind! // 1) Uniformly sample cos(theta) and phi to ensure an isotropic distribution (could also use TRandom::Sphere as well, but may be slower) // Notice that uniformly sampling theta would make the distribution non-isotropic, thus we use cos(theta)! @@ -1032,7 +1080,7 @@ struct lambdajetpolarizationionsderived { // 2) Construct the new random unit vector (there is no need to use the magnitude at all! We only need direction here): unitVec = XYZVector(sinTheta * std::cos(randPhi), sinTheta * std::sin(randPhi), cosTheta); - } else if (forcePreviousJet) { // Use the jet direction from the immediately preceding collision. The simplest event mixing + } else if (fakePolSwitches.forcePreviousJet) { // Use the jet direction from the immediately preceding collision. The simplest event mixing const bool usableProxy = cacheHadProxy; if (usableProxy) { const double inverseCoshEta = 1.0 / std::cosh(cacheEta); @@ -1049,7 +1097,7 @@ struct lambdajetpolarizationionsderived { // Current event cannot use previous-jet mixing if previous event lacked a proxy if (!usableProxy) hasValidProxy = false; - } else if (forceDatalikeJet){ // A compromise between forceRandJet and forcePreviousJet, using data-like weights for sampling jets + } else if (fakePolSwitches.forceDatalikeJet){ // A compromise between forceRandJet and forcePreviousJet, using data-like weights for sampling jets const float etaMin = -0.92f; const float etaBinWidth = 0.04f; constexpr float phiBinWidth = constants::math::TwoPI / 50.f; @@ -1071,7 +1119,7 @@ struct lambdajetpolarizationionsderived { // Recalculating pT, phi and eta after distortions: // (without this, later kinematic selections make no sense at all! In the forceRandJet case, the ring observable would always sum zero) if (hasValidProxy) { // If you don't check this flag here, the "if (!usableProxy)" change would be silently overwritten - if (!forcePreviousJet && !forceDatalikeJet){ // In these two cases, we already know the eta and phi variables. No need to recompute + if (!fakePolSwitches.forcePreviousJet && !fakePolSwitches.forceDatalikeJet){ // In these two cases, we already know the eta and phi variables. No need to recompute // Calculating total jet momentum, which should be preserved, just to recalculate the new jet Pt: const double mag = pt * std::cosh(eta); @@ -1112,11 +1160,15 @@ struct lambdajetpolarizationionsderived { o2::aod::RingLeadPs const& leadPs) { PrevJetCache prevJetCache{}; // Initializing struct before the collisions loop. This zero-initializes, so bools start as false + // This should not be used along nProxyResamples > 1, as it does not apply to that case. - for (int idxResampling = 0; idxResampling < nProxyResamples; idxResampling++){ // resampling loop for forceRandJet and forceDatalikeJet + for (int idxResampling = 0; idxResampling < fakePolSwitches.nProxyResamples; idxResampling++){ // resampling loop for forceRandJet and forceDatalikeJet for (auto const& collision : collisions) { + if (std::abs(collision.zvtx()) > maxZVtxPosition) // Additional Zvtx filtering + continue; + const auto collId = collision.globalIndex(); // The self-index accessor - const double centrality = getCentrality(collision); + const float centrality = getCentrality(collision); // Fetch magnetic field only if DataModel is in the latest version: float magField = 1.f; // Used this dummy for backwards compatibility, under the reasonable assumption that the field points always in the same *direction* in the used runs @@ -1134,7 +1186,11 @@ struct lambdajetpolarizationionsderived { // that collision, and a collision may not be filled when the jets // table is. Be mindful of that!) // 1) Require at least one V0: - if (!v0sInColl.size()) + const int nLambdaLikeV0s = v0sInColl.size(); // Caching this variable, as it will be reused in the loop + // In the latest datamodel format, only unambiguous V0s (Lambda XOR antiLambda) are saved, + // so the number of V0s in the collision table is the number of Lambdas/antiLambda identified + // by the table producer. + if (!nLambdaLikeV0s) continue; // 2) We require at least one leading particle: @@ -1181,7 +1237,7 @@ struct lambdajetpolarizationionsderived { // Fill distorted-proxy QA histograms: // Do not gate on the post-distortion hasValidLeadingP (a pT cut) value here! - if (!forcePreviousJet || hadPreviousProxy){ + if (!fakePolSwitches.forcePreviousJet || hadPreviousProxy){ histos.fill(HIST("JetKinematicsQA/hLeadPEta"), leadPEta); histos.fill(HIST("JetKinematicsQA/hLeadPPhi"), leadPPhi); histos.fill(HIST("JetKinematicsQA/hJetCounterPtLeadP"), leadPPt); @@ -1235,7 +1291,7 @@ struct lambdajetpolarizationionsderived { // Fill distorted-proxy QA histograms: // Do not gate on the post-distortion hasValidLeadingJet (a pT cut) value here! - if (!forcePreviousJet || hadPreviousProxy) { + if (!fakePolSwitches.forcePreviousJet || hadPreviousProxy) { histos.fill(HIST("JetKinematicsQA/hLeadJetEta"), leadingJetEta); histos.fill(HIST("JetKinematicsQA/hLeadJetPhi"), leadingJetPhi); histos.fill(HIST("JetKinematicsQA/hJetCounterPtJet"), leadingJetPt); @@ -1259,7 +1315,7 @@ struct lambdajetpolarizationionsderived { // Fill distorted-proxy QA histograms: // Do not gate on the post-distortion hasValidSubJet (a pT cut) value here! - if (!forcePreviousJet || hadPreviousProxy) { + if (!fakePolSwitches.forcePreviousJet || hadPreviousProxy) { histos.fill(HIST("JetKinematicsQA/hSubLeadJetEta"), subleadingJetEta); histos.fill(HIST("JetKinematicsQA/hSubLeadJetPhi"), subleadingJetPhi); histos.fill(HIST("JetKinematicsQA/hJetCounterPt2ndJet"), subleadingJetPt); @@ -1287,30 +1343,31 @@ struct lambdajetpolarizationionsderived { // const bool subJetEtaStrictNeg = !subJetEtaPos && subJetEtaStrict; // Fetching number of Lambda-like V0s in collision (must be known before full loop, to fill "pRingVsNV0s"): - int NLambdaLikeV0s = 0; - for (auto const& v0 : v0sInColl) { - if (v0.isLambda() ^ v0.isAntiLambda()){ // XOR (only the non-ambiguous candidates) - NLambdaLikeV0s++; - } - } + // int nLambdaLikeV0s = 0; + // for (auto const& v0 : v0sInColl) { + // if (v0.isLambda() ^ v0.isAntiLambda()){ // XOR (only the non-ambiguous candidates) + // nLambdaLikeV0s++; + // } + // } + // Code above was superseeded: new datamodel does not store ambiguous candidates! + // The new getter comes at the very start of processPolarizationData() now. // Initialize delta method accumulators: EventDeltaTracker trackRing, trackRingKinCuts, trackJetKinCuts, trackJetLambdaKinCuts; for (auto const& v0 : v0sInColl) { - const bool isLambda = v0.isLambda(); - const bool isAntiLambda = v0.isAntiLambda(); - // For now, removing the ambiguous candidates from the analysis. Derived data permits handling both. - // (From Podolanski-Armenteros plots, the population of ambiguous is ~2% without TOF, and without + const bool isLambda = v0.isLambda(); // true: is a Lambda. false: is an antiLambda. + // For now, removing the ambiguous candidates from the analysis. New datamodel does NOT save ambiguous candidates. + // (From Podolanski-Armenteros plots, the population of ambiguous is ~3.8% without TOF, and without // competing mass rejection. From those, ~99% seem to be K0s, so no real gain in considering the // ambiguous candidates in the analysis) - if (isLambda && isAntiLambda) - continue; + // const bool isAntiLambda = v0.isAntiLambda(); // No longer used! + // if (isLambda && isAntiLambda) continue; const float v0pt = v0.v0Pt(); const float v0eta = v0.v0Eta(); const float v0phi = v0.v0Phi(); const float dcaDau = v0.dcaV0Daughters(); - float v0LambdaLikeMass = 0; // Initialized just to catch any stray behavior + float v0LambdaLikeMass = v0.massV0(); float protonLikePt = 0; float protonLikeEta = 0; float protonLikePhi = 0; @@ -1319,16 +1376,14 @@ struct lambdajetpolarizationionsderived { if (isLambda) { if (!analyseLambda) continue; - v0LambdaLikeMass = v0.massLambda(); protonLikePt = v0.posPt(); protonLikeEta = v0.posEta(); protonLikePhi = v0.posPhi(); protonLikeDCADauToPV = v0.dcaPosToPV(); pionLikeDCADauToPV = v0.dcaNegToPV(); - } else if (isAntiLambda) { // (TODO: add a split histogram where you consider Lambda and AntiLambda polarization separately?) + } else { // Guaranteed to be an antiLambda candidate, not an ambiguous candidate if (!analyseAntiLambda) continue; - v0LambdaLikeMass = v0.massAntiLambda(); protonLikePt = v0.negPt(); protonLikeEta = v0.negEta(); protonLikePhi = v0.negPhi(); @@ -1350,6 +1405,12 @@ struct lambdajetpolarizationionsderived { auto lambdaLikeUnit3Vec = lambdaLike3Vec.Unit(); XYZVector protonLikeStarUnit3Vec = protonLike4VecStar.Vect().Unit(); + // Lab-frame Lambda momentum components -- Not for polarization, but for actual momenta plotting in XY and ZX planes: + // (Used for the (px,py) and (pz,px) polarization vector-field / ring 2D profiles) + const float v0px = lambdaLike3Vec.X(); + const float v0py = lambdaLike3Vec.Y(); + const float v0pz = lambdaLike3Vec.Z(); + // Calculating fake polarization ("negative helicity problem") estimator: // (this estimator is calculated outside of any gate, as it does not depend on jet proxy used) float cosFakePol = protonLikeStarUnit3Vec.Dot(lambdaLikeUnit3Vec); @@ -1382,7 +1443,7 @@ struct lambdajetpolarizationionsderived { histos.fill(HIST("HelicityEfficiencyQA/hLambdaMassDecayGeomRight"), v0LambdaLikeMass); else if (isLambda && !positiveGeom) histos.fill(HIST("HelicityEfficiencyQA/hLambdaMassDecayGeomLeft"), v0LambdaLikeMass); - else if (isAntiLambda && positiveGeom) + else if (!isLambda && positiveGeom) histos.fill(HIST("HelicityEfficiencyQA/hAntiLambdaMassDecayGeomRight"), v0LambdaLikeMass); else histos.fill(HIST("HelicityEfficiencyQA/hAntiLambdaMassDecayGeomLeft"), v0LambdaLikeMass); @@ -1415,7 +1476,7 @@ struct lambdajetpolarizationionsderived { XYZVector crossLeadP = leadPUnitVec.Cross(lambdaLike3Vec); ringObservableLeadP = protonLikeStarUnit3Vec.Dot(crossLeadP) / crossLeadP.R(); // Adding the prefactor related to the CP-violating decay (decay constants have different signs) - if (!forcePolSignQA) + if (!fakePolSwitches.forcePolSignQA) ringObservableLeadP *= (isLambda) ? polPrefactorLambda : polPrefactorAntiLambda; else ringObservableLeadP *= (isLambda) ? polPrefactorLambda : -1.0 * polPrefactorAntiLambda; @@ -1442,7 +1503,7 @@ struct lambdajetpolarizationionsderived { XYZVector cross = leadingJetUnitVec.Cross(lambdaLike3Vec); ringObservable = protonLikeStarUnit3Vec.Dot(cross) / cross.R(); // Adding prefactor - if (!forcePolSignQA) + if (!fakePolSwitches.forcePolSignQA) ringObservable *= (isLambda) ? polPrefactorLambda : polPrefactorAntiLambda; else ringObservable *= (isLambda) ? polPrefactorLambda : -1.0 * polPrefactorAntiLambda; @@ -1485,7 +1546,7 @@ struct lambdajetpolarizationionsderived { XYZVector cross2ndJet = subJetUnitVec.Cross(lambdaLike3Vec); ringObservable2ndJet = protonLikeStarUnit3Vec.Dot(cross2ndJet) / cross2ndJet.R(); // Adding prefactor - if (!forcePolSignQA) + if (!fakePolSwitches.forcePolSignQA) ringObservable2ndJet *= (isLambda) ? polPrefactorLambda : polPrefactorAntiLambda; else ringObservable2ndJet *= (isLambda) ? polPrefactorLambda : -1.0 * polPrefactorAntiLambda; @@ -1502,7 +1563,7 @@ struct lambdajetpolarizationionsderived { PolStarX = polPrefactorLambda * protonLikeStarUnit3Vec.X(); PolStarY = polPrefactorLambda * protonLikeStarUnit3Vec.Y(); PolStarZ = polPrefactorLambda * protonLikeStarUnit3Vec.Z(); - } else if (isAntiLambda) { + } else { PolStarX = polPrefactorAntiLambda * protonLikeStarUnit3Vec.X(); PolStarY = polPrefactorAntiLambda * protonLikeStarUnit3Vec.Y(); PolStarZ = polPrefactorAntiLambda * protonLikeStarUnit3Vec.Z(); @@ -1512,31 +1573,42 @@ struct lambdajetpolarizationionsderived { // Fill ring histograms: (1D, lambda 2D correlations and jet 2D correlations): if (hasValidLeadingP) { - RING_OBSERVABLE_LEADP_FILL_LIST(APPLY_HISTO_FILL, "Ring") // Notice the usage of macros! If you change the variable names, this WILL break the code! - // No, there should NOT be any ";" here! Read the macro definition for an explanation + if (familySwitches.doFamilyRing) { + RING_OBSERVABLE_LEADP_FILL_LIST(APPLY_HISTO_FILL, "Ring") // Notice the usage of macros! If you change the variable names, this WILL break the code! + // No, there should NOT be any ";" here! Read the macro definition for an explanation + } histos.fill(HIST("IntegratedCuts/pRingCutsLeadingP"), 0, ringObservableLeadP); // First bin of comparison histos.fill(HIST("IntegratedCuts/hCountCutsLeadingP"), 0); // Filling checks that rely on Eta>0 or Eta<0 checks for V0 and LeadingP eta: - RING_OBSERVABLE_LEADP_ETA_SPLIT_FILL_LIST("Ring", leadPEtaPos, lambdaEtaPos); + if (familySwitches.doFamilyRing) { + RING_OBSERVABLE_LEADP_ETA_SPLIT_FILL_LIST("Ring", leadPEtaPos, lambdaEtaPos); + } + } + if (familySwitches.doFamilyRing) { + POLARIZATION_PROFILE_FILL_LIST(APPLY_HISTO_FILL, "Ring") } - POLARIZATION_PROFILE_FILL_LIST(APPLY_HISTO_FILL, "Ring") // Binary search using the pre-fetched axes for delta method of error bar estimation: int binPt = 0; // Dummy declarations int binMass = 0; int binDTheta = 0; if (hasValidLeadingJet) { - RING_OBSERVABLE_FILL_LIST(APPLY_HISTO_FILL, "Ring") + if (familySwitches.doFamilyRing) { + RING_OBSERVABLE_FILL_LIST(APPLY_HISTO_FILL, "Ring") + } histos.fill(HIST("IntegratedCuts/pRingCuts"), 0, ringObservable); histos.fill(HIST("IntegratedCuts/hCountCuts"), 0); - histos.fill(HIST("IntegratedCuts/pRingVsNV0s"), NLambdaLikeV0s, ringObservable); + histos.fill(HIST("IntegratedCuts/pRingVsNV0s"), nLambdaLikeV0s, ringObservable); + histos.fill(HIST("hNV0sVsCentrality"), nLambdaLikeV0s, centrality); // Properly fetching values as they are needed: binPt = mAxisPt->FindBin(v0pt); binMass = mAxisMass->FindBin(v0LambdaLikeMass); binDTheta = mAxisDTheta->FindBin(deltaThetaJet); - trackRing.addV0(ringObservable, binPt, binMass, binDTheta); + if (familySwitches.doFamilyRing) { + trackRing.addV0(ringObservable, binPt, binMass, binDTheta); + } // Measuring the AEE differentially (azimuthal efficiency effect, which causes different V0 topologies to be enhanced/suppressed): if (isLambda) @@ -1573,7 +1645,9 @@ struct lambdajetpolarizationionsderived { histos.fill(HIST("HelicityEfficiencyQA/pRingOverJetZcomponent_VsJetEtaVsPhiStar"), leadingJetEta, phiStar, ringObservableOverJetZ); } if (hasValidSubJet) { - RING_OBSERVABLE_2NDJET_FILL_LIST(APPLY_HISTO_FILL, "Ring") + if (familySwitches.doFamilyRing) { + RING_OBSERVABLE_2NDJET_FILL_LIST(APPLY_HISTO_FILL, "Ring") + } histos.fill(HIST("IntegratedCuts/pRingCutsSubLeadingJet"), 0, ringObservable2ndJet); histos.fill(HIST("IntegratedCuts/hCountCutsSubLeadingJet"), 0); } @@ -1735,22 +1809,34 @@ struct lambdajetpolarizationionsderived { // Extra kinematic criteria for Lambda candidates (removes polarization background): if (kinematicLambdaCheck) { if (hasValidLeadingP) { - RING_OBSERVABLE_LEADP_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts") + if (familySwitches.doFamilyRingKinematicCuts) { + RING_OBSERVABLE_LEADP_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts") + } histos.fill(HIST("IntegratedCuts/pRingCutsLeadingP"), 1, ringObservableLeadP); histos.fill(HIST("IntegratedCuts/hCountCutsLeadingP"), 1); // Filling checks that rely on Eta>0 or Eta<0 checks for V0 and LeadingP eta: - RING_OBSERVABLE_LEADP_ETA_SPLIT_FILL_LIST("RingKinematicCuts", leadPEtaPos, lambdaEtaPos); + if (familySwitches.doFamilyRingKinematicCuts) { + RING_OBSERVABLE_LEADP_ETA_SPLIT_FILL_LIST("RingKinematicCuts", leadPEtaPos, lambdaEtaPos); + } + } + if (familySwitches.doFamilyRingKinematicCuts) { + POLARIZATION_PROFILE_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts") } - POLARIZATION_PROFILE_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts") if (hasValidLeadingJet) { - RING_OBSERVABLE_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts") + if (familySwitches.doFamilyRingKinematicCuts) { + RING_OBSERVABLE_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts") + } histos.fill(HIST("IntegratedCuts/pRingCuts"), 1, ringObservable); histos.fill(HIST("IntegratedCuts/hCountCuts"), 1); - trackRingKinCuts.addV0(ringObservable, binPt, binMass, binDTheta); + if (familySwitches.doFamilyRingKinematicCuts) { + trackRingKinCuts.addV0(ringObservable, binPt, binMass, binDTheta); + } } if (hasValidSubJet) { - RING_OBSERVABLE_2NDJET_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts") + if (familySwitches.doFamilyRingKinematicCuts) { + RING_OBSERVABLE_2NDJET_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts") + } histos.fill(HIST("IntegratedCuts/pRingCutsSubLeadingJet"), 1, ringObservable2ndJet); histos.fill(HIST("IntegratedCuts/hCountCutsSubLeadingJet"), 1); } @@ -1759,57 +1845,85 @@ struct lambdajetpolarizationionsderived { // Extra selection criteria on jet candidates: // (redundant for jets with R=0.4, but for jets with R<0.4 the leading jet may be farther in eta) if (kinematicJetCheck) { // Already includes hasValidLeadingJet in the bool! (no need to check again) - RING_OBSERVABLE_FILL_LIST(APPLY_HISTO_FILL, "JetKinematicCuts") + if (familySwitches.doFamilyJetKinematicCuts) { + RING_OBSERVABLE_FILL_LIST(APPLY_HISTO_FILL, "JetKinematicCuts") + } histos.fill(HIST("IntegratedCuts/pRingCuts"), 2, ringObservable); histos.fill(HIST("IntegratedCuts/hCountCuts"), 2); - POLARIZATION_PROFILE_FILL_LIST(APPLY_HISTO_FILL, "JetKinematicCuts") - trackJetKinCuts.addV0(ringObservable, binPt, binMass, binDTheta); + if (familySwitches.doFamilyJetKinematicCuts) { + POLARIZATION_PROFILE_FILL_LIST(APPLY_HISTO_FILL, "JetKinematicCuts") + trackJetKinCuts.addV0(ringObservable, binPt, binMass, binDTheta); + } } // Extra selection criteria on both Lambda and jet candidates: if (kinematicLambdaCheck && kinematicJetCheck) { - RING_OBSERVABLE_FILL_LIST(APPLY_HISTO_FILL, "JetAndLambdaKinematicCuts") + if (familySwitches.doFamilyJetAndLambdaKinematicCuts) { + RING_OBSERVABLE_FILL_LIST(APPLY_HISTO_FILL, "JetAndLambdaKinematicCuts") + } histos.fill(HIST("IntegratedCuts/pRingCuts"), 3, ringObservable); histos.fill(HIST("IntegratedCuts/hCountCuts"), 3); - POLARIZATION_PROFILE_FILL_LIST(APPLY_HISTO_FILL, "JetAndLambdaKinematicCuts") - trackJetLambdaKinCuts.addV0(ringObservable, binPt, binMass, binDTheta); + if (familySwitches.doFamilyJetAndLambdaKinematicCuts) { + POLARIZATION_PROFILE_FILL_LIST(APPLY_HISTO_FILL, "JetAndLambdaKinematicCuts") + trackJetLambdaKinCuts.addV0(ringObservable, binPt, binMass, binDTheta); + } } // Same variations for the leading particle and for the subleading jet: // (kinematicLeadPCheck already encodes hasValidLeadingP, so no extra gate needed here) if (kinematicLeadPCheck) { - RING_OBSERVABLE_LEADP_FILL_LIST(APPLY_HISTO_FILL, "JetKinematicCuts") + if (familySwitches.doFamilyJetKinematicCuts) { + RING_OBSERVABLE_LEADP_FILL_LIST(APPLY_HISTO_FILL, "JetKinematicCuts") + } histos.fill(HIST("IntegratedCuts/pRingCutsLeadingP"), 2, ringObservableLeadP); histos.fill(HIST("IntegratedCuts/hCountCutsLeadingP"), 2); // Filling checks that rely on Eta>0 or Eta<0 checks for V0 and LeadingP eta: - RING_OBSERVABLE_LEADP_ETA_SPLIT_FILL_LIST("JetKinematicCuts", leadPEtaPos, lambdaEtaPos); + if (familySwitches.doFamilyJetKinematicCuts) { + RING_OBSERVABLE_LEADP_ETA_SPLIT_FILL_LIST("JetKinematicCuts", leadPEtaPos, lambdaEtaPos); + } } if (kinematic2ndJetCheck) { - RING_OBSERVABLE_2NDJET_FILL_LIST(APPLY_HISTO_FILL, "JetKinematicCuts") + if (familySwitches.doFamilyJetKinematicCuts) { + RING_OBSERVABLE_2NDJET_FILL_LIST(APPLY_HISTO_FILL, "JetKinematicCuts") + } histos.fill(HIST("IntegratedCuts/pRingCutsSubLeadingJet"), 2, ringObservable2ndJet); histos.fill(HIST("IntegratedCuts/hCountCutsSubLeadingJet"), 2); } if (kinematicLambdaCheck && kinematicLeadPCheck) { - RING_OBSERVABLE_LEADP_FILL_LIST(APPLY_HISTO_FILL, "JetAndLambdaKinematicCuts") + if (familySwitches.doFamilyJetAndLambdaKinematicCuts) { + RING_OBSERVABLE_LEADP_FILL_LIST(APPLY_HISTO_FILL, "JetAndLambdaKinematicCuts") + } histos.fill(HIST("IntegratedCuts/pRingCutsLeadingP"), 3, ringObservableLeadP); histos.fill(HIST("IntegratedCuts/hCountCutsLeadingP"), 3); // Filling checks that rely on Eta>0 or Eta<0 checks for V0 and LeadingP eta: - RING_OBSERVABLE_LEADP_ETA_SPLIT_FILL_LIST("JetAndLambdaKinematicCuts", leadPEtaPos, lambdaEtaPos); + if (familySwitches.doFamilyJetAndLambdaKinematicCuts) { + RING_OBSERVABLE_LEADP_ETA_SPLIT_FILL_LIST("JetAndLambdaKinematicCuts", leadPEtaPos, lambdaEtaPos); + } } if (kinematicLambdaCheck && kinematic2ndJetCheck) { - RING_OBSERVABLE_2NDJET_FILL_LIST(APPLY_HISTO_FILL, "JetAndLambdaKinematicCuts") + if (familySwitches.doFamilyJetAndLambdaKinematicCuts) { + RING_OBSERVABLE_2NDJET_FILL_LIST(APPLY_HISTO_FILL, "JetAndLambdaKinematicCuts") + } histos.fill(HIST("IntegratedCuts/pRingCutsSubLeadingJet"), 3, ringObservable2ndJet); histos.fill(HIST("IntegratedCuts/hCountCutsSubLeadingJet"), 3); } } // end v0s loop // Flush trackers to the actual O2 histograms (via macros, so that O2 compiles properly): - FLUSH_DELTA_TRACKER("Ring", trackRing, mAxisPt, mAxisMass, mAxisDTheta) - FLUSH_DELTA_TRACKER("RingKinematicCuts", trackRingKinCuts, mAxisPt, mAxisMass, mAxisDTheta) - FLUSH_DELTA_TRACKER("JetKinematicCuts", trackJetKinCuts, mAxisPt, mAxisMass, mAxisDTheta) - FLUSH_DELTA_TRACKER("JetAndLambdaKinematicCuts", trackJetLambdaKinCuts, mAxisPt, mAxisMass, mAxisDTheta) + if (familySwitches.doFamilyRing) { + FLUSH_DELTA_TRACKER("Ring", trackRing, mAxisPt, mAxisMass, mAxisDTheta) + } + if (familySwitches.doFamilyRingKinematicCuts) { + FLUSH_DELTA_TRACKER("RingKinematicCuts", trackRingKinCuts, mAxisPt, mAxisMass, mAxisDTheta) + } + if (familySwitches.doFamilyJetKinematicCuts) { + FLUSH_DELTA_TRACKER("JetKinematicCuts", trackJetKinCuts, mAxisPt, mAxisMass, mAxisDTheta) + } + if (familySwitches.doFamilyJetAndLambdaKinematicCuts) { + FLUSH_DELTA_TRACKER("JetAndLambdaKinematicCuts", trackJetLambdaKinCuts, mAxisPt, mAxisMass, mAxisDTheta) + } } // end collisions } // end of resampling loop for forceRandJet and forceDatalikeJet } From 0b902d501caef21d81444f94423b8879e8e55843 Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Fri, 7 Aug 2026 19:50:04 +0000 Subject: [PATCH 17/31] Adding doxygen documentation to macros --- .../Strangeness/lambdaJetPolarizationIons.cxx | 21 +++++++++++++++++-- .../lambdaJetPolarizationIonsDerived.cxx | 7 +++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx b/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx index 84e36b48c7c..328917e9fb1 100644 --- a/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx +++ b/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx @@ -468,6 +468,7 @@ struct lambdajetpolarizationions { JetBkgSubUtils backgroundSub; + /// \brief Books every histogram used by the producer and sets up the CCDB/RCT services. void init(InitContext const&) { // setting CCDB service @@ -923,6 +924,7 @@ struct lambdajetpolarizationions { histos.print(); } + /// \brief Returns the collision centrality for the configured estimator (FT0M/FT0C/FV0A). Returns -1 if none matched. template auto getCentrality(TCollision const& collision) { @@ -935,6 +937,7 @@ struct lambdajetpolarizationions { return -1.f; } + /// \brief Fetches the magnetic field for the current run from CCDB (or uses customMagField, if set). No-op if already initialized for this run. template void initCCDB(TBC const& bc) { @@ -1027,6 +1030,8 @@ struct lambdajetpolarizationions { ///////////////////////////////////////////// // Computation helper functions: + /// \brief Folds phi into the repeating ~20-degree TPC sector pattern used by the sector-boundary cut functions below. + /// \param sign track charge sign (+1 or -1) -- the fold direction depends on charge and field polarity. double computePhiMod(double phi, int sign) // Compute phi wrt to a TPC sector // Calculation taken from CF: https://github.com/AliceO2Group/O2Physics/blob/376392cb87349886a300c75fa2492b50b7f46725/PWGCF/Flow/Tasks/flowAnalysisGF.cxx#L470 @@ -1042,6 +1047,8 @@ struct lambdajetpolarizationions { return fmod(phi, o2::constants::math::PI / 9.0); } + /// \brief Checks the track against the TPC sector-boundary cut (see computePhiMod()). + /// \return true if the track is far enough from the boundary to be kept; false rejects it. bool isTrackFarFromTPCBoundary(double trackPt, double trackPhi, int sign) // check whether the track passes close to a TPC sector boundary { @@ -1070,6 +1077,9 @@ struct lambdajetpolarizationions { ///////////////////////////////////////////// // Helper functions for event and candidate selection: + /// \brief Runs the full event-selection cutflow. returns false at the first failed cut (in configured order). + /// \param interactionRate output-only: filled with the fetched IR. + /// \param fillHists if true, also fills the per-stage QA histograms and the occupancy/IR/vertex histograms. template bool isEventAccepted(TCollision const& collision, TBC const& bc, float centrality, double& interactionRate, bool fillHists) { // check whether the collision passes our collision selections @@ -1205,6 +1215,8 @@ struct lambdajetpolarizationions { return true; } + /// \brief Track-level cutflow gate for FastJet pseudojet candidates (quality + kinematics + optional DCA cuts). + /// \note Mirrors isEventAccepted()'s early-exit pattern, but for tracks. template bool isCandidateForChargedPseudojetAccepted(JetCandidate const& track) { // (TODO: add an equivalent for photon jets and Z jets, which don't consider charged particles) @@ -1262,6 +1274,8 @@ struct lambdajetpolarizationions { } // Lambda selections: + /// \brief Charge-independent V0 cuts (topology, ITS/TPC track quality) shared by both the Lambda and AntiLambda hypotheses. + /// \note See passesLambdaLambdaBarHypothesis() for the hypothesis-specific cuts. template bool passesGenericV0Cuts(TV0 const& v0) { @@ -1381,7 +1395,8 @@ struct lambdajetpolarizationions { return true; } - // Tests the hypothesis of the V0 being a Lambda or of it being an antiLambda. + /// \brief Tests the hypothesis of the V0 being a Lambda or of it being an antiLambda. + /// \param Lambda_hypothesis "true" tests the Lambda (proton+/pion-) hypothesis. "false" tests AntiLambda. template bool passesLambdaLambdaBarHypothesis(TV0 const& v0, TCollision const& collision, bool Lambda_hypothesis) { @@ -1486,6 +1501,7 @@ struct lambdajetpolarizationions { // else return -1; // AntiLambda // } + /// \brief Clusters charged tracks into jets (FastJet, with optional background subtraction), fills the RingJets/RingLeadPs tables for this collision, and fills the jet-kinematics QA. template void jetsProcess(TJetTracks const& tracks, const int ringCollIdx, const float centrality) { @@ -1753,7 +1769,8 @@ struct lambdajetpolarizationions { } } - // No longer use a separate JetTracks joined table -- it was mostly a subset of DauTracks + TracksIU (which was not used) + /// \brief Main process function: applies event selection, fills the RingCollisions table, calls jetsProcess(), then tests every V0 against the Lambda/AntiLambda hypotheses and fills RingLaV0s. + /// \note No longer use a separate JetTracks joined table -- it was mostly a subset of DauTracks + TracksIU (which was not used) template void dataProcess(TCollision const& collision, TV0Candidates const& V0s, TDaughterTracks const& V0DauTracks, aod::BCsWithTimestamps const& bcs) { diff --git a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx index 83449f5064b..0f1d096a3f4 100644 --- a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx @@ -1013,6 +1013,12 @@ struct lambdajetpolarizationionsderived { std::discrete_distribution etaLeadPDist{etaLeadPWeights.begin(),etaLeadPWeights.end()}; // Will be passed as the etaDist variable std::discrete_distribution phiLeadPDist{phiLeadPWeights.begin(), phiLeadPWeights.end()}; + /// \brief Applies whichever fakePolSwitches distortion is active to a jet-proxy direction, in place. No-op if none are on. + /// \param hasValidProxy in/out (edited in-place): whether the proxy being distorted is usable. Re-evaluated against minPtThreshold after distortion. + /// \param pt,eta,phi in/out (edited in-place): the proxy's kinematics, overwritten by whichever distortion is active. + /// \param unitVec in/out (edited in-place): the proxy direction as a unit vector. + /// \param cacheHadProxy,cacheEta,cachePhi the previous collision's proxy, used (and updated) by forcePreviousJet. + /// \param etaDist,phiDist,rng sampling distributions/generator for forceRandJet and forceDatalikeJet. // Helper to modify the jet direction for QA and for spurious signal baseline removal tests: inline void applyProxyDistortion(bool& hasValidProxy, float& pt, float& eta, float& phi, XYZVector& unitVec, float minPtThreshold, bool& cacheHadProxy, float& cacheEta, float& cachePhi, @@ -1156,6 +1162,7 @@ struct lambdajetpolarizationionsderived { Preslice perColJets = o2::aod::lambdajetpol::ringCollisionId; Preslice perColV0s = o2::aod::lambdajetpol::ringCollisionId; Preslice perColLeadPs = o2::aod::lambdajetpol::ringCollisionId; + /// \brief Main analysis loop: for each collision, rebuilds the leading jet/particle proxies (with optional fakePolSwitches distortions), then loops over V0s computing the ring observable and polarization-vector profiles for every enabled kinematic-cut family. void processPolarizationData(o2::aod::RingCollisions const& collisions, o2::aod::RingJets const& jets, o2::aod::RingLaV0s const& v0s, o2::aod::RingLeadPs const& leadPs) { From 88564fce68014361124ecb25859e7c44421c7537 Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Mon, 10 Aug 2026 13:36:06 +0000 Subject: [PATCH 18/31] General title and axis fixes --- .../Strangeness/lambdaJetPolarizationIons.cxx | 342 +++++++++--------- .../lambdaJetPolarizationIonsDerived.cxx | 16 +- 2 files changed, 181 insertions(+), 177 deletions(-) diff --git a/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx b/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx index 328917e9fb1..6470eb8958b 100644 --- a/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx +++ b/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx @@ -356,7 +356,7 @@ struct lambdajetpolarizationions { // Jet QA axes: ConfigurableAxis axisJetsPerEvent{"axisJetsPerEvent", {20, 0, 20}, "Jets per event"}; ConfigurableAxis axisJetConstituents{"axisJetConstituents", {200, 0, 200}, "Number of jet constituents"}; - ConfigurableAxis axisLeadingParticlePt{"axisLeadingParticlePt", {200, 0.f, 200.f}, "Leading particle p_{T} (GeV/c)"}; // Simpler version! + ConfigurableAxis axisLeadingParticlePt{"axisLeadingParticlePt", {200, 0.f, 200.f}, "Leading particle p_{T} [GeV/c]"}; // Simpler version! ConfigurableAxis axisJetPt{"axisJetPt", {200, 0.f, 200.f}, "Jet p_{t} (GeV)"}; ConfigurableAxis axisCosTheta{"axisCosTheta", {50, -1.f, 1.f}, "cos(#Delta #theta_{jet})"}; ConfigurableAxis axisDeltaPhi{"axisDeltaPhi", {50, -constants::math::PI, constants::math::PI}, "#Delta #phi"}; @@ -480,7 +480,7 @@ struct lambdajetpolarizationions { rctFlagsChecker.init(rctConfigurations.cfgRCTLabel.value, rctConfigurations.cfgCheckZDC, rctConfigurations.cfgTreatLimitedAcceptanceAsBad); // Event Counters - histos.add("hEventSelection", "hEventSelection", kTH1D, {{24, -0.5f, +23.5f}}); + histos.add("hEventSelection", "hEventSelection;;Counts", kTH1D, {{24, -0.5f, +23.5f}}); histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(1, "All collisions"); histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(2, "sel8 cut"); histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(3, "kIsTriggerTVX"); @@ -513,10 +513,10 @@ struct lambdajetpolarizationions { // (notice we lack a hasRingJet AND hasRingV0 bin because the tasks run separately on all events!) // (this QA number can be obtained at derived data level with ease) - histos.add("Centrality/hEventCentrality", "hEventCentrality", kTH1D, {{101, 0.0f, 101.0f}}); - histos.add("Centrality/hCentralityVsNch", "hCentralityVsNch", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisNch}); + histos.add("Centrality/hEventCentrality", "hEventCentrality;Centrality (%);Counts", kTH1D, {{101, 0.0f, 101.0f}}); + histos.add("Centrality/hCentralityVsNch", "hCentralityVsNch;Centrality (%);N_{ch}", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisNch}); if (doEventQA) { - histos.add("hEventSelectionVsCentrality", "hEventSelectionVsCentrality", kTH2D, {{24, -0.5f, +23.5f}, {101, 0.0f, 101.0f}}); + histos.add("hEventSelectionVsCentrality", "hEventSelectionVsCentrality;;Centrality (%)", kTH2D, {{24, -0.5f, +23.5f}, {101, 0.0f, 101.0f}}); histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(1, "All collisions"); histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(2, "sel8 cut"); histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(3, "kIsTriggerTVX"); @@ -548,22 +548,22 @@ struct lambdajetpolarizationions { histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(24, "hasRingV0"); // Centrality: - histos.add("Centrality/hEventCentVsMultFT0M", "hEventCentVsMultFT0M", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisMultFT0M}); - histos.add("Centrality/hEventCentVsMultFT0C", "hEventCentVsMultFT0C", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisMultFT0C}); - histos.add("Centrality/hEventCentVsMultFV0A", "hEventCentVsMultFV0A", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisMultFV0A}); - histos.add("Centrality/hEventMultFT0CvsMultFV0A", "hEventMultFT0CvsMultFV0A", kTH2D, {axisConfigurations.axisMultFT0C, axisConfigurations.axisMultFV0A}); + histos.add("Centrality/hEventCentVsMultFT0M", "hEventCentVsMultFT0M;Centrality (%);Mult. FT0M", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisMultFT0M}); + histos.add("Centrality/hEventCentVsMultFT0C", "hEventCentVsMultFT0C;Centrality (%);Mult. FT0C", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisMultFT0C}); + histos.add("Centrality/hEventCentVsMultFV0A", "hEventCentVsMultFV0A;Centrality (%);Mult. FV0A", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisMultFV0A}); + histos.add("Centrality/hEventMultFT0CvsMultFV0A", "hEventMultFT0CvsMultFV0A;Mult. FT0C;Mult. FV0A", kTH2D, {axisConfigurations.axisMultFT0C, axisConfigurations.axisMultFV0A}); } - histos.add("hEventPVz", "hEventPVz", kTH1D, {{100, -20.0f, +20.0f}}); - histos.add("hCentralityVsPVz", "hCentralityVsPVz", kTH2D, {{101, 0.0f, 101.0f}, {100, -20.0f, +20.0f}}); + histos.add("hEventPVz", "hEventPVz;PV_{z} [cm];Counts", kTH1D, {{100, -20.0f, +20.0f}}); + histos.add("hCentralityVsPVz", "hCentralityVsPVz;Centrality (%);PV_{z} [cm]", kTH2D, {{101, 0.0f, 101.0f}, {100, -20.0f, +20.0f}}); // (TODO: add MC centrality vs PVz histos) - histos.add("hEventOccupancy", "hEventOccupancy", kTH1D, {axisConfigurations.axisOccupancy}); - histos.add("hCentralityVsOccupancy", "hCentralityVsOccupancy", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisOccupancy}); - histos.add("hInteractionRate", "hInteractionRate", kTH1D, {axisConfigurations.axisIRBinning}); - histos.add("hCentralityVsInteractionRate", "hCentralityVsInteractionRate", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisIRBinning}); - histos.add("hInteractionRateVsOccupancy", "hInteractionRateVsOccupancy", kTH2D, {axisConfigurations.axisIRBinning, axisConfigurations.axisOccupancy}); + histos.add("hEventOccupancy", "hEventOccupancy;Occupancy;Counts", kTH1D, {axisConfigurations.axisOccupancy}); + histos.add("hCentralityVsOccupancy", "hCentralityVsOccupancy;Centrality (%);Occupancy", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisOccupancy}); + histos.add("hInteractionRate", "hInteractionRate;Interaction Rate;Counts", kTH1D, {axisConfigurations.axisIRBinning}); + histos.add("hCentralityVsInteractionRate", "hCentralityVsInteractionRate;Centrality (%);Interaction Rate", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisIRBinning}); + histos.add("hInteractionRateVsOccupancy", "hInteractionRateVsOccupancy;Interaction Rate;Occupancy", kTH2D, {axisConfigurations.axisIRBinning, axisConfigurations.axisOccupancy}); // for QA and test purposes // auto hRawCentrality = histos.add("Centrality/hRawCentrality", "hRawCentrality", kTH1D, {axisConfigurations.axisRawCentrality}); @@ -694,230 +694,231 @@ struct lambdajetpolarizationions { // Histograms versus mass: if (analyseLambda) { - histos.add("Lambda/h2dNbrOfLambdaVsCentrality", "h2dNbrOfLambdaVsCentrality", kTH2D, {axisConfigurations.axisCentrality, {10, -0.5f, 9.5f}}); - histos.add("Lambda/h3dMassLambda", "h3dMassLambda", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisLambdaMass}); + histos.add("Lambda/h2dNbrOfLambdaVsCentrality", "h2dNbrOfLambdaVsCentrality;Centrality (%);N_{#Lambda} per event", kTH2D, {axisConfigurations.axisCentrality, {10, -0.5f, 9.5f}}); + histos.add("Lambda/h3dMassLambda", "h3dMassLambda;Centrality (%);p_{T} [GeV/c];M(p#pi) [GeV/c]", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisLambdaMass}); // // Non-UPC info - // histos.add("Lambda/h3dMassLambdaHadronic", "h3dMassLambdaHadronic", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisLambdaMass}); + // histos.add("Lambda/h3dMassLambdaHadronic", "h3dMassLambdaHadronic;Centrality (%);p_{T} [GeV/c];M(p#pi) [GeV/c]", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisLambdaMass}); if (doTPCQA) { - histos.add("Lambda/h3dPosNsigmaTPC", "h3dPosNsigmaTPC", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTPC}); - histos.add("Lambda/h3dNegNsigmaTPC", "h3dNegNsigmaTPC", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTPC}); - histos.add("Lambda/h3dPosTPCsignal", "h3dPosTPCsignal", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTPCsignal}); - histos.add("Lambda/h3dNegTPCsignal", "h3dNegTPCsignal", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTPCsignal}); - histos.add("Lambda/h3dPosNsigmaTPCvsTrackPtot", "h3dPosNsigmaTPCvsTrackPtot", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTPC}); - histos.add("Lambda/h3dNegNsigmaTPCvsTrackPtot", "h3dNegNsigmaTPCvsTrackPtot", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTPC}); - histos.add("Lambda/h3dPosTPCsignalVsTrackPtot", "h3dPosTPCsignalVsTrackPtot", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTPCsignal}); - histos.add("Lambda/h3dNegTPCsignalVsTrackPtot", "h3dNegTPCsignalVsTrackPtot", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTPCsignal}); - histos.add("Lambda/h3dPosNsigmaTPCvsTrackPt", "h3dPosNsigmaTPCvsTrackPt", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTPC}); - histos.add("Lambda/h3dNegNsigmaTPCvsTrackPt", "h3dNegNsigmaTPCvsTrackPt", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTPC}); - histos.add("Lambda/h3dPosTPCsignalVsTrackPt", "h3dPosTPCsignalVsTrackPt", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTPCsignal}); - histos.add("Lambda/h3dNegTPCsignalVsTrackPt", "h3dNegTPCsignalVsTrackPt", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTPCsignal}); + histos.add("Lambda/h3dPosNsigmaTPC", "h3dPosNsigmaTPC;Centrality (%);p_{T} [GeV/c];N#sigma_{TPC}", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTPC}); + histos.add("Lambda/h3dNegNsigmaTPC", "h3dNegNsigmaTPC;Centrality (%);p_{T} [GeV/c];N#sigma_{TPC}", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTPC}); + histos.add("Lambda/h3dPosTPCsignal", "h3dPosTPCsignal;Centrality (%);p_{T} [GeV/c];TPC signal", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTPCsignal}); + histos.add("Lambda/h3dNegTPCsignal", "h3dNegTPCsignal;Centrality (%);p_{T} [GeV/c];TPC signal", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTPCsignal}); + histos.add("Lambda/h3dPosNsigmaTPCvsTrackPtot", "h3dPosNsigmaTPCvsTrackPtot;Centrality (%);p_{T} [GeV/c];N#sigma_{TPC}", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTPC}); + histos.add("Lambda/h3dNegNsigmaTPCvsTrackPtot", "h3dNegNsigmaTPCvsTrackPtot;Centrality (%);p_{T} [GeV/c];N#sigma_{TPC}", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTPC}); + histos.add("Lambda/h3dPosTPCsignalVsTrackPtot", "h3dPosTPCsignalVsTrackPtot;Centrality (%);p_{T} [GeV/c];TPC signal", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTPCsignal}); + histos.add("Lambda/h3dNegTPCsignalVsTrackPtot", "h3dNegTPCsignalVsTrackPtot;Centrality (%);p_{T} [GeV/c];TPC signal", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTPCsignal}); + histos.add("Lambda/h3dPosNsigmaTPCvsTrackPt", "h3dPosNsigmaTPCvsTrackPt;Centrality (%);p_{T} [GeV/c];N#sigma_{TPC}", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTPC}); + histos.add("Lambda/h3dNegNsigmaTPCvsTrackPt", "h3dNegNsigmaTPCvsTrackPt;Centrality (%);p_{T} [GeV/c];N#sigma_{TPC}", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTPC}); + histos.add("Lambda/h3dPosTPCsignalVsTrackPt", "h3dPosTPCsignalVsTrackPt;Centrality (%);p_{T} [GeV/c];TPC signal", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTPCsignal}); + histos.add("Lambda/h3dNegTPCsignalVsTrackPt", "h3dNegTPCsignalVsTrackPt;Centrality (%);p_{T} [GeV/c];TPC signal", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTPCsignal}); } if (doTOFQA) { - histos.add("Lambda/h3dPosNsigmaTOF", "h3dPosNsigmaTOF", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTOF}); - histos.add("Lambda/h3dNegNsigmaTOF", "h3dNegNsigmaTOF", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTOF}); - histos.add("Lambda/h3dPosTOFdeltaT", "h3dPosTOFdeltaT", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTOFdeltaT}); - histos.add("Lambda/h3dNegTOFdeltaT", "h3dNegTOFdeltaT", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTOFdeltaT}); - histos.add("Lambda/h3dPosNsigmaTOFvsTrackPtot", "h3dPosNsigmaTOFvsTrackPtot", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTOF}); - histos.add("Lambda/h3dNegNsigmaTOFvsTrackPtot", "h3dNegNsigmaTOFvsTrackPtot", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTOF}); - histos.add("Lambda/h3dPosTOFdeltaTvsTrackPtot", "h3dPosTOFdeltaTvsTrackPtot", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTOFdeltaT}); - histos.add("Lambda/h3dNegTOFdeltaTvsTrackPtot", "h3dNegTOFdeltaTvsTrackPtot", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTOFdeltaT}); - histos.add("Lambda/h3dPosNsigmaTOFvsTrackPt", "h3dPosNsigmaTOFvsTrackPt", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTOF}); - histos.add("Lambda/h3dNegNsigmaTOFvsTrackPt", "h3dNegNsigmaTOFvsTrackPt", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTOF}); - histos.add("Lambda/h3dPosTOFdeltaTvsTrackPt", "h3dPosTOFdeltaTvsTrackPt", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTOFdeltaT}); - histos.add("Lambda/h3dNegTOFdeltaTvsTrackPt", "h3dNegTOFdeltaTvsTrackPt", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTOFdeltaT}); + histos.add("Lambda/h3dPosNsigmaTOF", "h3dPosNsigmaTOF;Centrality (%);p_{T} [GeV/c];N#sigma_{TOF}", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTOF}); + histos.add("Lambda/h3dNegNsigmaTOF", "h3dNegNsigmaTOF;Centrality (%);p_{T} [GeV/c];N#sigma_{TOF}", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTOF}); + histos.add("Lambda/h3dPosTOFdeltaT", "h3dPosTOFdeltaT;Centrality (%);p_{T} [GeV/c];TOF #Delta t [ps]", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTOFdeltaT}); + histos.add("Lambda/h3dNegTOFdeltaT", "h3dNegTOFdeltaT;Centrality (%);p_{T} [GeV/c];TOF #Delta t [ps]", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTOFdeltaT}); + histos.add("Lambda/h3dPosNsigmaTOFvsTrackPtot", "h3dPosNsigmaTOFvsTrackPtot;Centrality (%);p_{T} [GeV/c];N#sigma_{TOF}", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTOF}); + histos.add("Lambda/h3dNegNsigmaTOFvsTrackPtot", "h3dNegNsigmaTOFvsTrackPtot;Centrality (%);p_{T} [GeV/c];N#sigma_{TOF}", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTOF}); + histos.add("Lambda/h3dPosTOFdeltaTvsTrackPtot", "h3dPosTOFdeltaTvsTrackPtot;Centrality (%);p_{T} [GeV/c];TOF #Delta t [ps]", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTOFdeltaT}); + histos.add("Lambda/h3dNegTOFdeltaTvsTrackPtot", "h3dNegTOFdeltaTvsTrackPtot;Centrality (%);p_{T} [GeV/c];TOF #Delta t [ps]", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTOFdeltaT}); + histos.add("Lambda/h3dPosNsigmaTOFvsTrackPt", "h3dPosNsigmaTOFvsTrackPt;Centrality (%);p_{T} [GeV/c];N#sigma_{TOF}", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTOF}); + histos.add("Lambda/h3dNegNsigmaTOFvsTrackPt", "h3dNegNsigmaTOFvsTrackPt;Centrality (%);p_{T} [GeV/c];N#sigma_{TOF}", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTOF}); + histos.add("Lambda/h3dPosTOFdeltaTvsTrackPt", "h3dPosTOFdeltaTvsTrackPt;Centrality (%);p_{T} [GeV/c];TOF #Delta t [ps]", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTOFdeltaT}); + histos.add("Lambda/h3dNegTOFdeltaTvsTrackPt", "h3dNegTOFdeltaTvsTrackPt;Centrality (%);p_{T} [GeV/c];TOF #Delta t [ps]", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTOFdeltaT}); } // (TODO: add collision association capabilities in MC) } if (analyseAntiLambda) { - histos.add("AntiLambda/h2dNbrOfAntiLambdaVsCentrality", "h2dNbrOfAntiLambdaVsCentrality", kTH2D, {axisConfigurations.axisCentrality, {10, -0.5f, 9.5f}}); - histos.add("AntiLambda/h3dMassAntiLambda", "h3dMassAntiLambda", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisLambdaMass}); + histos.add("AntiLambda/h2dNbrOfAntiLambdaVsCentrality", "h2dNbrOfAntiLambdaVsCentrality;Centrality (%);N_{#bar{#Lambda}} per event", kTH2D, {axisConfigurations.axisCentrality, {10, -0.5f, 9.5f}}); + histos.add("AntiLambda/h3dMassAntiLambda", "h3dMassAntiLambda;Centrality (%);p_{T} [GeV/c];M(p#pi) [GeV/c]", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisLambdaMass}); // // Non-UPC info - // histos.add("AntiLambda/h3dMassAntiLambdaHadronic", "h3dMassAntiLambdaHadronic", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisLambdaMass}); + // histos.add("AntiLambda/h3dMassAntiLambdaHadronic", "h3dMassAntiLambdaHadronic;Centrality (%);p_{T} [GeV/c];M(p#pi) [GeV/c]", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisLambdaMass}); if (doTPCQA) { - histos.add("AntiLambda/h3dPosNsigmaTPC", "h3dPosNsigmaTPC", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTPC}); - histos.add("AntiLambda/h3dNegNsigmaTPC", "h3dNegNsigmaTPC", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTPC}); - histos.add("AntiLambda/h3dPosTPCsignal", "h3dPosTPCsignal", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTPCsignal}); - histos.add("AntiLambda/h3dNegTPCsignal", "h3dNegTPCsignal", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTPCsignal}); - histos.add("AntiLambda/h3dPosNsigmaTPCvsTrackPtot", "h3dPosNsigmaTPCvsTrackPtot", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTPC}); - histos.add("AntiLambda/h3dNegNsigmaTPCvsTrackPtot", "h3dNegNsigmaTPCvsTrackPtot", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTPC}); - histos.add("AntiLambda/h3dPosTPCsignalVsTrackPtot", "h3dPosTPCsignalVsTrackPtot", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTPCsignal}); - histos.add("AntiLambda/h3dNegTPCsignalVsTrackPtot", "h3dNegTPCsignalVsTrackPtot", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTPCsignal}); - histos.add("AntiLambda/h3dPosNsigmaTPCvsTrackPt", "h3dPosNsigmaTPCvsTrackPt", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTPC}); - histos.add("AntiLambda/h3dNegNsigmaTPCvsTrackPt", "h3dNegNsigmaTPCvsTrackPt", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTPC}); - histos.add("AntiLambda/h3dPosTPCsignalVsTrackPt", "h3dPosTPCsignalVsTrackPt", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTPCsignal}); - histos.add("AntiLambda/h3dNegTPCsignalVsTrackPt", "h3dNegTPCsignalVsTrackPt", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTPCsignal}); + histos.add("AntiLambda/h3dPosNsigmaTPC", "h3dPosNsigmaTPC;Centrality (%);p_{T} [GeV/c];N#sigma_{TPC}", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTPC}); + histos.add("AntiLambda/h3dNegNsigmaTPC", "h3dNegNsigmaTPC;Centrality (%);p_{T} [GeV/c];N#sigma_{TPC}", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTPC}); + histos.add("AntiLambda/h3dPosTPCsignal", "h3dPosTPCsignal;Centrality (%);p_{T} [GeV/c];TPC signal", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTPCsignal}); + histos.add("AntiLambda/h3dNegTPCsignal", "h3dNegTPCsignal;Centrality (%);p_{T} [GeV/c];TPC signal", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTPCsignal}); + histos.add("AntiLambda/h3dPosNsigmaTPCvsTrackPtot", "h3dPosNsigmaTPCvsTrackPtot;Centrality (%);p_{T} [GeV/c];N#sigma_{TPC}", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTPC}); + histos.add("AntiLambda/h3dNegNsigmaTPCvsTrackPtot", "h3dNegNsigmaTPCvsTrackPtot;Centrality (%);p_{T} [GeV/c];N#sigma_{TPC}", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTPC}); + histos.add("AntiLambda/h3dPosTPCsignalVsTrackPtot", "h3dPosTPCsignalVsTrackPtot;Centrality (%);p_{T} [GeV/c];TPC signal", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTPCsignal}); + histos.add("AntiLambda/h3dNegTPCsignalVsTrackPtot", "h3dNegTPCsignalVsTrackPtot;Centrality (%);p_{T} [GeV/c];TPC signal", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTPCsignal}); + histos.add("AntiLambda/h3dPosNsigmaTPCvsTrackPt", "h3dPosNsigmaTPCvsTrackPt;Centrality (%);p_{T} [GeV/c];N#sigma_{TPC}", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTPC}); + histos.add("AntiLambda/h3dNegNsigmaTPCvsTrackPt", "h3dNegNsigmaTPCvsTrackPt;Centrality (%);p_{T} [GeV/c];N#sigma_{TPC}", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTPC}); + histos.add("AntiLambda/h3dPosTPCsignalVsTrackPt", "h3dPosTPCsignalVsTrackPt;Centrality (%);p_{T} [GeV/c];TPC signal", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTPCsignal}); + histos.add("AntiLambda/h3dNegTPCsignalVsTrackPt", "h3dNegTPCsignalVsTrackPt;Centrality (%);p_{T} [GeV/c];TPC signal", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTPCsignal}); } if (doTOFQA) { - histos.add("AntiLambda/h3dPosNsigmaTOF", "h3dPosNsigmaTOF", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTOF}); - histos.add("AntiLambda/h3dNegNsigmaTOF", "h3dNegNsigmaTOF", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTOF}); - histos.add("AntiLambda/h3dPosTOFdeltaT", "h3dPosTOFdeltaT", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTOFdeltaT}); - histos.add("AntiLambda/h3dNegTOFdeltaT", "h3dNegTOFdeltaT", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTOFdeltaT}); - histos.add("AntiLambda/h3dPosNsigmaTOFvsTrackPtot", "h3dPosNsigmaTOFvsTrackPtot", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTOF}); - histos.add("AntiLambda/h3dNegNsigmaTOFvsTrackPtot", "h3dNegNsigmaTOFvsTrackPtot", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTOF}); - histos.add("AntiLambda/h3dPosTOFdeltaTvsTrackPtot", "h3dPosTOFdeltaTvsTrackPtot", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTOFdeltaT}); - histos.add("AntiLambda/h3dNegTOFdeltaTvsTrackPtot", "h3dNegTOFdeltaTvsTrackPtot", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTOFdeltaT}); - histos.add("AntiLambda/h3dPosNsigmaTOFvsTrackPt", "h3dPosNsigmaTOFvsTrackPt", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTOF}); - histos.add("AntiLambda/h3dNegNsigmaTOFvsTrackPt", "h3dNegNsigmaTOFvsTrackPt", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTOF}); - histos.add("AntiLambda/h3dPosTOFdeltaTvsTrackPt", "h3dPosTOFdeltaTvsTrackPt", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTOFdeltaT}); - histos.add("AntiLambda/h3dNegTOFdeltaTvsTrackPt", "h3dNegTOFdeltaTvsTrackPt", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTOFdeltaT}); + histos.add("AntiLambda/h3dPosNsigmaTOF", "h3dPosNsigmaTOF;Centrality (%);p_{T} [GeV/c];N#sigma_{TOF}", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTOF}); + histos.add("AntiLambda/h3dNegNsigmaTOF", "h3dNegNsigmaTOF;Centrality (%);p_{T} [GeV/c];N#sigma_{TOF}", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTOF}); + histos.add("AntiLambda/h3dPosTOFdeltaT", "h3dPosTOFdeltaT;Centrality (%);p_{T} [GeV/c];TOF #Delta t [ps]", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTOFdeltaT}); + histos.add("AntiLambda/h3dNegTOFdeltaT", "h3dNegTOFdeltaT;Centrality (%);p_{T} [GeV/c];TOF #Delta t [ps]", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTOFdeltaT}); + histos.add("AntiLambda/h3dPosNsigmaTOFvsTrackPtot", "h3dPosNsigmaTOFvsTrackPtot;Centrality (%);p_{T} [GeV/c];N#sigma_{TOF}", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTOF}); + histos.add("AntiLambda/h3dNegNsigmaTOFvsTrackPtot", "h3dNegNsigmaTOFvsTrackPtot;Centrality (%);p_{T} [GeV/c];N#sigma_{TOF}", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTOF}); + histos.add("AntiLambda/h3dPosTOFdeltaTvsTrackPtot", "h3dPosTOFdeltaTvsTrackPtot;Centrality (%);p_{T} [GeV/c];TOF #Delta t [ps]", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTOFdeltaT}); + histos.add("AntiLambda/h3dNegTOFdeltaTvsTrackPtot", "h3dNegTOFdeltaTvsTrackPtot;Centrality (%);p_{T} [GeV/c];TOF #Delta t [ps]", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTOFdeltaT}); + histos.add("AntiLambda/h3dPosNsigmaTOFvsTrackPt", "h3dPosNsigmaTOFvsTrackPt;Centrality (%);p_{T} [GeV/c];N#sigma_{TOF}", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTOF}); + histos.add("AntiLambda/h3dNegNsigmaTOFvsTrackPt", "h3dNegNsigmaTOFvsTrackPt;Centrality (%);p_{T} [GeV/c];N#sigma_{TOF}", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTOF}); + histos.add("AntiLambda/h3dPosTOFdeltaTvsTrackPt", "h3dPosTOFdeltaTvsTrackPt;Centrality (%);p_{T} [GeV/c];TOF #Delta t [ps]", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTOFdeltaT}); + histos.add("AntiLambda/h3dNegTOFdeltaTvsTrackPt", "h3dNegTOFdeltaTvsTrackPt;Centrality (%);p_{T} [GeV/c];TOF #Delta t [ps]", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTOFdeltaT}); } } if (analyseLambda) { - histos.add("hMassLambda", "hMassLambda", kTH1D, {axisConfigurations.axisLambdaMass}); - histos.add("Lambda/hLambdasPerEvent", "hLambdasPerEvent", kTH1D, {{15, 0, 15}}); + histos.add("hMassLambda", "hMassLambda;M(p#pi) [GeV/c];Counts", kTH1D, {axisConfigurations.axisLambdaMass}); + histos.add("Lambda/hLambdasPerEvent", "hLambdasPerEvent;N_{#Lambda} per event;Counts", kTH1D, {{15, 0, 15}}); } if (analyseAntiLambda) { - histos.add("hMassAntiLambda", "hMassAntiLambda", kTH1D, {axisConfigurations.axisLambdaMass}); - histos.add("AntiLambda/hAntiLambdasPerEvent", "hAntiLambdasPerEvent", kTH1D, {{15, 0, 15}}); + histos.add("hMassAntiLambda", "hMassAntiLambda;M(p#pi) [GeV/c];Counts", kTH1D, {axisConfigurations.axisLambdaMass}); + histos.add("AntiLambda/hAntiLambdasPerEvent", "hAntiLambdasPerEvent;N_{#bar{#Lambda}} per event;Counts", kTH1D, {{15, 0, 15}}); } if (analyseLambda && analyseAntiLambda) { - histos.add("hAmbiguousLambdaCandidates", "hAmbiguousLambdaCandidates", kTH1D, {{1, 0, 1}}); - histos.add("hAmbiguousPerEvent", "hAmbiguousPerEvent", kTH1D, {{15, 0, 15}}); - histos.add("hNonAmbiguousPerEvent", "hNonAmbiguousPerEvent", kTH1D, {{25, 0, 25}}); // To understand the population of correlated Lambda-likes per event - histos.add("hLambdasAndAntiLambdasPerEvent", "hLambdasAndAntiLambdasPerEvent", kTH1D, {{25, 0, 25}}); // Alternative check that shows how bad is the possibly correlated full population (Ambig+NonAmbig) + histos.add("hAmbiguousLambdaCandidates", "hAmbiguousLambdaCandidates;Integrated counts;Counts", kTH1D, {{1, 0, 1}}); + histos.add("hAmbiguousPerEvent", "hAmbiguousPerEvent;N_{ambiguous} per event;Counts", kTH1D, {{15, 0, 15}}); + histos.add("hNonAmbiguousPerEvent", "hNonAmbiguousPerEvent;N_{non-ambiguous} per event;Counts", kTH1D, {{25, 0, 25}}); // To understand the population of correlated Lambda-likes per event + histos.add("hLambdasAndAntiLambdasPerEvent", "hLambdasAndAntiLambdasPerEvent;N_{#Lambda}+N_{#bar{#Lambda}} per event;Counts", kTH1D, {{25, 0, 25}}); // Alternative check that shows how bad is the possibly correlated full population (Ambig+NonAmbig) } // QA histograms if requested if (doV0KinematicQA) { if (analyseLambda) { // --- Basic kinematics --- - histos.add("V0KinematicQA/Lambda/hPt", "Lambda p_{T}", kTH1D, {axisConfigurations.axisPt}); - histos.add("V0KinematicQA/Lambda/hY", "Lambda rapidity", kTH1D, {axisConfigurations.axisRapidity}); - histos.add("V0KinematicQA/Lambda/hPhi", "Lambda #varphi", kTH1D, {axisConfigurations.axisPhi}); + histos.add("V0KinematicQA/Lambda/hPt", "Lambda p_{T};p_{T} [GeV/c];Counts", kTH1D, {axisConfigurations.axisPt}); + histos.add("V0KinematicQA/Lambda/hY", "Lambda rapidity;y;Counts", kTH1D, {axisConfigurations.axisRapidity}); + histos.add("V0KinematicQA/Lambda/hPhi", "Lambda #varphi;#varphi;Counts", kTH1D, {axisConfigurations.axisPhi}); // --- Mass correlations --- - histos.add("V0KinematicQA/Lambda/hMassVsPt", "Lambda mass vs p_{T}", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisLambdaMass}); - histos.add("V0KinematicQA/Lambda/hMassVsY", "Lambda mass vs y", kTH2D, {axisConfigurations.axisRapidity, axisConfigurations.axisLambdaMass}); - histos.add("V0KinematicQA/Lambda/hMassVsPhi", "Lambda mass vs #varphi", kTH2D, {axisConfigurations.axisPhi, axisConfigurations.axisLambdaMass}); + histos.add("V0KinematicQA/Lambda/hMassVsPt", "Lambda mass vs p_{T};p_{T} [GeV/c];M(p#pi) [GeV/c]", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisLambdaMass}); + histos.add("V0KinematicQA/Lambda/hMassVsY", "Lambda mass vs y;y;M(p#pi) [GeV/c]", kTH2D, {axisConfigurations.axisRapidity, axisConfigurations.axisLambdaMass}); + histos.add("V0KinematicQA/Lambda/hMassVsPhi", "Lambda mass vs #varphi;#varphi;M(p#pi) [GeV/c]", kTH2D, {axisConfigurations.axisPhi, axisConfigurations.axisLambdaMass}); // --- Kinematic correlations --- - histos.add("V0KinematicQA/Lambda/hYVsPt", "Lambda y vs p_{T}", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisRapidity}); - histos.add("V0KinematicQA/Lambda/hPhiVsPt", "Lambda #varphi vs p_{T}", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisPhi}); + histos.add("V0KinematicQA/Lambda/hYVsPt", "Lambda y vs p_{T};p_{T} [GeV/c];y", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisRapidity}); + histos.add("V0KinematicQA/Lambda/hPhiVsPt", "Lambda #varphi vs p_{T};p_{T} [GeV/c];#varphi", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisPhi}); } if (analyseAntiLambda) { // --- Basic kinematics --- - histos.add("V0KinematicQA/AntiLambda/hPt", "AntiLambda p_{T}", kTH1D, {axisConfigurations.axisPt}); - histos.add("V0KinematicQA/AntiLambda/hY", "AntiLambda rapidity", kTH1D, {axisConfigurations.axisRapidity}); - histos.add("V0KinematicQA/AntiLambda/hPhi", "AntiLambda #varphi", kTH1D, {axisConfigurations.axisPhi}); + histos.add("V0KinematicQA/AntiLambda/hPt", "AntiLambda p_{T};p_{T} [GeV/c];Counts", kTH1D, {axisConfigurations.axisPt}); + histos.add("V0KinematicQA/AntiLambda/hY", "AntiLambda rapidity;y;Counts", kTH1D, {axisConfigurations.axisRapidity}); + histos.add("V0KinematicQA/AntiLambda/hPhi", "AntiLambda #varphi;#varphi;Counts", kTH1D, {axisConfigurations.axisPhi}); // --- Mass correlations --- - histos.add("V0KinematicQA/AntiLambda/hMassVsPt", "AntiLambda mass vs p_{T}", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisLambdaMass}); - histos.add("V0KinematicQA/AntiLambda/hMassVsY", "AntiLambda mass vs y", kTH2D, {axisConfigurations.axisRapidity, axisConfigurations.axisLambdaMass}); - histos.add("V0KinematicQA/AntiLambda/hMassVsPhi", "AntiLambda mass vs #varphi", kTH2D, {axisConfigurations.axisPhi, axisConfigurations.axisLambdaMass}); + histos.add("V0KinematicQA/AntiLambda/hMassVsPt", "AntiLambda mass vs p_{T};p_{T} [GeV/c];M(p#pi) [GeV/c]", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisLambdaMass}); + histos.add("V0KinematicQA/AntiLambda/hMassVsY", "AntiLambda mass vs y;y;M(p#pi) [GeV/c]", kTH2D, {axisConfigurations.axisRapidity, axisConfigurations.axisLambdaMass}); + histos.add("V0KinematicQA/AntiLambda/hMassVsPhi", "AntiLambda mass vs #varphi;#varphi;M(p#pi) [GeV/c]", kTH2D, {axisConfigurations.axisPhi, axisConfigurations.axisLambdaMass}); // --- Kinematic correlations --- - histos.add("V0KinematicQA/AntiLambda/hYVsPt", "AntiLambda y vs p_{T}", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisRapidity}); - histos.add("V0KinematicQA/AntiLambda/hPhiVsPt", "AntiLambda #varphi vs p_{T}", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisPhi}); + histos.add("V0KinematicQA/AntiLambda/hYVsPt", "AntiLambda y vs p_{T};p_{T} [GeV/c];y", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisRapidity}); + histos.add("V0KinematicQA/AntiLambda/hPhiVsPt", "AntiLambda #varphi vs p_{T};p_{T} [GeV/c];#varphi", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisPhi}); } } if (doTopoQA) { // For all received candidates: - histos.add("V0KinematicQA/hPosDCAToPV", "hPosDCAToPV", kTH1D, {axisConfigurations.axisDCAtoPV}); - histos.add("V0KinematicQA/hNegDCAToPV", "hNegDCAToPV", kTH1D, {axisConfigurations.axisDCAtoPV}); - histos.add("V0KinematicQA/hDCADaughters", "hDCADaughters", kTH1D, {axisConfigurations.axisDCAdau}); - histos.add("V0KinematicQA/hPointingAngle", "hPointingAngle", kTH1D, {axisConfigurations.axisPointingAngle}); - histos.add("V0KinematicQA/hV0Radius", "hV0Radius", kTH1D, {axisConfigurations.axisV0Radius}); - histos.add("V0KinematicQA/h2dPositiveITSvsTPCpts", "h2dPositiveITSvsTPCpts", kTH2D, {axisConfigurations.axisTPCrows, axisConfigurations.axisITSclus}); - histos.add("V0KinematicQA/h2dNegativeITSvsTPCpts", "h2dNegativeITSvsTPCpts", kTH2D, {axisConfigurations.axisTPCrows, axisConfigurations.axisITSclus}); - histos.add("V0KinematicQA/h2dPositivePtVsPhi", "h2dPositivePtVsPhi", kTH2D, {axisConfigurations.axisPtCoarse, axisConfigurations.axisPhiMod}); - histos.add("V0KinematicQA/h2dNegativePtVsPhi", "h2dNegativePtVsPhi", kTH2D, {axisConfigurations.axisPtCoarse, axisConfigurations.axisPhiMod}); + histos.add("V0KinematicQA/hPosDCAToPV", "hPosDCAToPV;DCAtoPV [cm];Counts", kTH1D, {axisConfigurations.axisDCAtoPV}); + histos.add("V0KinematicQA/hNegDCAToPV", "hNegDCAToPV;DCAtoPV [cm];Counts", kTH1D, {axisConfigurations.axisDCAtoPV}); + histos.add("V0KinematicQA/hDCADaughters", "hDCADaughters;DCAdau [cm];Counts", kTH1D, {axisConfigurations.axisDCAdau}); + histos.add("V0KinematicQA/hPointingAngle", "hPointingAngle;Pointing angle [rad];Counts", kTH1D, {axisConfigurations.axisPointingAngle}); + histos.add("V0KinematicQA/hV0Radius", "hV0Radius;V0 radius [cm];Counts", kTH1D, {axisConfigurations.axisV0Radius}); + histos.add("V0KinematicQA/h2dPositiveITSvsTPCpts", "h2dPositiveITSvsTPCpts;N TPC crossed rows;N ITS clusters", kTH2D, {axisConfigurations.axisTPCrows, axisConfigurations.axisITSclus}); + histos.add("V0KinematicQA/h2dNegativeITSvsTPCpts", "h2dNegativeITSvsTPCpts;N TPC crossed rows;N ITS clusters", kTH2D, {axisConfigurations.axisTPCrows, axisConfigurations.axisITSclus}); + histos.add("V0KinematicQA/h2dPositivePtVsPhi", "h2dPositivePtVsPhi;p_{T} [GeV/c];#varphi mod TPC sector", kTH2D, {axisConfigurations.axisPtCoarse, axisConfigurations.axisPhiMod}); + histos.add("V0KinematicQA/h2dNegativePtVsPhi", "h2dNegativePtVsPhi;p_{T} [GeV/c];#varphi mod TPC sector", kTH2D, {axisConfigurations.axisPtCoarse, axisConfigurations.axisPhiMod}); if (analyseLambda) { - histos.add("Lambda/hPosDCAToPV", "hPosDCAToPV", kTH1D, {axisConfigurations.axisDCAtoPV}); - histos.add("Lambda/hNegDCAToPV", "hNegDCAToPV", kTH1D, {axisConfigurations.axisDCAtoPV}); - histos.add("Lambda/hDCADaughters", "hDCADaughters", kTH1D, {axisConfigurations.axisDCAdau}); - histos.add("Lambda/hPointingAngle", "hPointingAngle", kTH1D, {axisConfigurations.axisPointingAngle}); - histos.add("Lambda/hV0Radius", "hV0Radius", kTH1D, {axisConfigurations.axisV0Radius}); - histos.add("Lambda/h2dPositiveITSvsTPCpts", "h2dPositiveITSvsTPCpts", kTH2D, {axisConfigurations.axisTPCrows, axisConfigurations.axisITSclus}); - histos.add("Lambda/h2dNegativeITSvsTPCpts", "h2dNegativeITSvsTPCpts", kTH2D, {axisConfigurations.axisTPCrows, axisConfigurations.axisITSclus}); - histos.add("Lambda/h2dPositivePtVsPhi", "h2dPositivePtVsPhi", kTH2D, {axisConfigurations.axisPtCoarse, axisConfigurations.axisPhiMod}); - histos.add("Lambda/h2dNegativePtVsPhi", "h2dNegativePtVsPhi", kTH2D, {axisConfigurations.axisPtCoarse, axisConfigurations.axisPhiMod}); + histos.add("Lambda/hPosDCAToPV", "hPosDCAToPV;DCAtoPV [cm];Counts", kTH1D, {axisConfigurations.axisDCAtoPV}); + histos.add("Lambda/hNegDCAToPV", "hNegDCAToPV;DCAtoPV [cm];Counts", kTH1D, {axisConfigurations.axisDCAtoPV}); + histos.add("Lambda/hDCADaughters", "hDCADaughters;DCAdau [cm];Counts", kTH1D, {axisConfigurations.axisDCAdau}); + histos.add("Lambda/hPointingAngle", "hPointingAngle;Pointing angle [rad];Counts", kTH1D, {axisConfigurations.axisPointingAngle}); + histos.add("Lambda/hV0Radius", "hV0Radius;V0 radius [cm];Counts", kTH1D, {axisConfigurations.axisV0Radius}); + histos.add("Lambda/h2dPositiveITSvsTPCpts", "h2dPositiveITSvsTPCpts;N TPC crossed rows;N ITS clusters", kTH2D, {axisConfigurations.axisTPCrows, axisConfigurations.axisITSclus}); + histos.add("Lambda/h2dNegativeITSvsTPCpts", "h2dNegativeITSvsTPCpts;N TPC crossed rows;N ITS clusters", kTH2D, {axisConfigurations.axisTPCrows, axisConfigurations.axisITSclus}); + histos.add("Lambda/h2dPositivePtVsPhi", "h2dPositivePtVsPhi;p_{T} [GeV/c];#varphi mod TPC sector", kTH2D, {axisConfigurations.axisPtCoarse, axisConfigurations.axisPhiMod}); + histos.add("Lambda/h2dNegativePtVsPhi", "h2dNegativePtVsPhi;p_{T} [GeV/c];#varphi mod TPC sector", kTH2D, {axisConfigurations.axisPtCoarse, axisConfigurations.axisPhiMod}); } if (analyseAntiLambda) { - histos.add("AntiLambda/hPosDCAToPV", "hPosDCAToPV", kTH1D, {axisConfigurations.axisDCAtoPV}); - histos.add("AntiLambda/hNegDCAToPV", "hNegDCAToPV", kTH1D, {axisConfigurations.axisDCAtoPV}); - histos.add("AntiLambda/hDCADaughters", "hDCADaughters", kTH1D, {axisConfigurations.axisDCAdau}); - histos.add("AntiLambda/hPointingAngle", "hPointingAngle", kTH1D, {axisConfigurations.axisPointingAngle}); - histos.add("AntiLambda/hV0Radius", "hV0Radius", kTH1D, {axisConfigurations.axisV0Radius}); - histos.add("AntiLambda/h2dPositiveITSvsTPCpts", "h2dPositiveITSvsTPCpts", kTH2D, {axisConfigurations.axisTPCrows, axisConfigurations.axisITSclus}); - histos.add("AntiLambda/h2dNegativeITSvsTPCpts", "h2dNegativeITSvsTPCpts", kTH2D, {axisConfigurations.axisTPCrows, axisConfigurations.axisITSclus}); - histos.add("AntiLambda/h2dPositivePtVsPhi", "h2dPositivePtVsPhi", kTH2D, {axisConfigurations.axisPtCoarse, axisConfigurations.axisPhiMod}); - histos.add("AntiLambda/h2dNegativePtVsPhi", "h2dNegativePtVsPhi", kTH2D, {axisConfigurations.axisPtCoarse, axisConfigurations.axisPhiMod}); + histos.add("AntiLambda/hPosDCAToPV", "hPosDCAToPV;DCAtoPV [cm];Counts", kTH1D, {axisConfigurations.axisDCAtoPV}); + histos.add("AntiLambda/hNegDCAToPV", "hNegDCAToPV;DCAtoPV [cm];Counts", kTH1D, {axisConfigurations.axisDCAtoPV}); + histos.add("AntiLambda/hDCADaughters", "hDCADaughters;DCAdau [cm];Counts", kTH1D, {axisConfigurations.axisDCAdau}); + histos.add("AntiLambda/hPointingAngle", "hPointingAngle;Pointing angle [rad];Counts", kTH1D, {axisConfigurations.axisPointingAngle}); + histos.add("AntiLambda/hV0Radius", "hV0Radius;V0 radius [cm];Counts", kTH1D, {axisConfigurations.axisV0Radius}); + histos.add("AntiLambda/h2dPositiveITSvsTPCpts", "h2dPositiveITSvsTPCpts;N TPC crossed rows;N ITS clusters", kTH2D, {axisConfigurations.axisTPCrows, axisConfigurations.axisITSclus}); + histos.add("AntiLambda/h2dNegativeITSvsTPCpts", "h2dNegativeITSvsTPCpts;N TPC crossed rows;N ITS clusters", kTH2D, {axisConfigurations.axisTPCrows, axisConfigurations.axisITSclus}); + histos.add("AntiLambda/h2dPositivePtVsPhi", "h2dPositivePtVsPhi;p_{T} [GeV/c];#varphi mod TPC sector", kTH2D, {axisConfigurations.axisPtCoarse, axisConfigurations.axisPhiMod}); + histos.add("AntiLambda/h2dNegativePtVsPhi", "h2dNegativePtVsPhi;p_{T} [GeV/c];#varphi mod TPC sector", kTH2D, {axisConfigurations.axisPtCoarse, axisConfigurations.axisPhiMod}); } } // Check ambiguous candidates in AP space: - histos.add("GeneralQA/h2dArmenterosAll", "h2dArmenterosAll", kTH2D, {axisConfigurations.axisAPAlpha, axisConfigurations.axisAPQt}); - histos.add("GeneralQA/h2dArmenterosKinematicSelected", "h2dArmenterosKinematicSelected", kTH2D, {axisConfigurations.axisAPAlpha, axisConfigurations.axisAPQt}); - histos.add("GeneralQA/h2dArmenterosFullSelected", "h2dArmenterosFullSelected", kTH2D, {axisConfigurations.axisAPAlpha, axisConfigurations.axisAPQt}); - histos.add("GeneralQA/h2dArmenterosFullSelectedLambda", "h2dArmenterosFullSelectedLambda", kTH2D, {axisConfigurations.axisAPAlpha, axisConfigurations.axisAPQt}); - histos.add("GeneralQA/h2dArmenterosFullSelectedAntiLambda", "h2dArmenterosFullSelectedAntiLambda", kTH2D, {axisConfigurations.axisAPAlpha, axisConfigurations.axisAPQt}); - histos.add("GeneralQA/h2dArmenterosFullSelectedNonAmbiguous", "h2dArmenterosFullSelectedNonAmbiguous", kTH2D, {axisConfigurations.axisAPAlpha, axisConfigurations.axisAPQt}); - histos.add("GeneralQA/h2dArmenterosFullSelectedAmbiguous", "h2dArmenterosFullSelectedAmbiguous", kTH2D, {axisConfigurations.axisAPAlpha, axisConfigurations.axisAPQt}); + histos.add("GeneralQA/h2dArmenterosAll", "h2dArmenterosAll;Armenteros #alpha;Armenteros q_{T} (GeV/c)", kTH2D, {axisConfigurations.axisAPAlpha, axisConfigurations.axisAPQt}); + histos.add("GeneralQA/h2dArmenterosKinematicSelected", "h2dArmenterosKinematicSelected;Armenteros #alpha;Armenteros q_{T} (GeV/c)", kTH2D, {axisConfigurations.axisAPAlpha, axisConfigurations.axisAPQt}); + histos.add("GeneralQA/h2dArmenterosFullSelected", "h2dArmenterosFullSelected;Armenteros #alpha;Armenteros q_{T} (GeV/c)", kTH2D, {axisConfigurations.axisAPAlpha, axisConfigurations.axisAPQt}); + histos.add("GeneralQA/h2dArmenterosFullSelectedLambda", "h2dArmenterosFullSelectedLambda;Armenteros #alpha;Armenteros q_{T} (GeV/c)", kTH2D, {axisConfigurations.axisAPAlpha, axisConfigurations.axisAPQt}); + histos.add("GeneralQA/h2dArmenterosFullSelectedAntiLambda", "h2dArmenterosFullSelectedAntiLambda;Armenteros #alpha;Armenteros q_{T} (GeV/c)", kTH2D, {axisConfigurations.axisAPAlpha, axisConfigurations.axisAPQt}); + histos.add("GeneralQA/h2dArmenterosFullSelectedNonAmbiguous", "h2dArmenterosFullSelectedNonAmbiguous;Armenteros #alpha;Armenteros q_{T} (GeV/c)", kTH2D, {axisConfigurations.axisAPAlpha, axisConfigurations.axisAPQt}); + histos.add("GeneralQA/h2dArmenterosFullSelectedAmbiguous", "h2dArmenterosFullSelectedAmbiguous;Armenteros #alpha;Armenteros q_{T} (GeV/c)", kTH2D, {axisConfigurations.axisAPAlpha, axisConfigurations.axisAPQt}); // Jets histograms: // Histograms that need to be present even out of extensive QA-mode: - histos.add("hEventsWithJet", "hEventsWithJet", kTH1D, {{1, 0, 1}}); // counter of events with jet (could be interesting to compare with the minimum pT cut or between the background subtraction vs no background subtraction cases) - histos.add("hJetsPerEvent", "hJetsPerEvent", kTH1D, {axisConfigurations.axisJetsPerEvent}); // number of jets per event + histos.add("hEventsWithJet", "hEventsWithJet;Integrated counts;Counts", kTH1D, {{1, 0, 1}}); // counter of events with jet (could be interesting to compare with the minimum pT cut or between the background subtraction vs no background subtraction cases) + histos.add("hJetsPerEvent", "hJetsPerEvent;Jets per event;Counts", kTH1D, {axisConfigurations.axisJetsPerEvent}); // number of jets per event if (doJetKinematicsQA) { // For probing the correlation between number of jet constituents and centrality: - histos.add("JetKinematicsQA/h2dConstituentsPerJetVsCentrality", "Constituents per Jet Vs Centrality", kTH2D, {axisConfigurations.axisJetConstituents, axisConfigurations.axisCentrality}); // Not quite kinematics, but kept it under this QAing switch for now - histos.add("JetKinematicsQA/h2dConstituentsPerJetVsJetPt", "Constituents per Jet Vs Jet Pt", kTH2D, {axisConfigurations.axisJetConstituents, axisConfigurations.axisJetPt}); + histos.add("JetKinematicsQA/h2dConstituentsPerJetVsCentrality", "Constituents per Jet Vs Centrality;N constituents;Centrality (%)", kTH2D, {axisConfigurations.axisJetConstituents, axisConfigurations.axisCentrality}); // Not quite kinematics, but kept it under this QAing switch for now + histos.add("JetKinematicsQA/h2dConstituentsPerJetVsJetPt", "Constituents per Jet Vs Jet Pt;N constituents;Jet p_{T} [GeV/c]", kTH2D, {axisConfigurations.axisJetConstituents, axisConfigurations.axisJetPt}); - histos.add("JetKinematicsQA/hJetPt", "hJetPt", kTH1D, {axisConfigurations.axisJetPt}); - histos.add("JetKinematicsQA/hJetEta", "hJetEta", kTH1D, {axisConfigurations.axisEta}); - histos.add("JetKinematicsQA/hJetPhi", "hJetPhi", kTH1D, {axisConfigurations.axisPhi}); + histos.add("JetKinematicsQA/hJetPt", "hJetPt;Jet p_{T} [GeV/c];Counts", kTH1D, {axisConfigurations.axisJetPt}); + histos.add("JetKinematicsQA/hJetEta", "hJetEta;#eta;Counts", kTH1D, {axisConfigurations.axisEta}); + histos.add("JetKinematicsQA/hJetPhi", "hJetPhi;#varphi;Counts", kTH1D, {axisConfigurations.axisPhi}); + histos.add("JetKinematicsQA/h2dJetEtaVsJetPhi", "h2dJetEtaVsJetPhi;#eta;#varphi", kTH2D, {axisConfigurations.axisEta, axisConfigurations.axisPhi}); // An occupancy map of sorts. Checks for detector-dependent jet clustering artifacts - histos.add("JetKinematicsQA/hCosThetaToLeadingJet", "hCosThetaToLeadingJet", kTH1D, {axisConfigurations.axisCosTheta}); - histos.add("JetKinematicsQA/hDeltaPhiToLeadingJet", "hDeltaPhiToLeadingJet", kTH1D, {axisConfigurations.axisDeltaPhi}); - histos.add("JetKinematicsQA/hDeltaEtaToLeadingJet", "hDeltaEtaToLeadingJet", kTH1D, {axisConfigurations.axisDeltaEta}); - histos.add("JetKinematicsQA/hDeltaRToLeadingJet", "hDeltaRToLeadingJet", kTH1D, {axisConfigurations.axisDeltaR}); + histos.add("JetKinematicsQA/hCosThetaToLeadingJet", "hCosThetaToLeadingJet;cos(#Delta#theta_{jet});Counts", kTH1D, {axisConfigurations.axisCosTheta}); + histos.add("JetKinematicsQA/hDeltaPhiToLeadingJet", "hDeltaPhiToLeadingJet;#Delta#varphi;Counts", kTH1D, {axisConfigurations.axisDeltaPhi}); + histos.add("JetKinematicsQA/hDeltaEtaToLeadingJet", "hDeltaEtaToLeadingJet;#Delta#eta;Counts", kTH1D, {axisConfigurations.axisDeltaEta}); + histos.add("JetKinematicsQA/hDeltaRToLeadingJet", "hDeltaRToLeadingJet;#Delta R;Counts", kTH1D, {axisConfigurations.axisDeltaR}); - histos.add("JetKinematicsQA/hLeadingJetPt", "hLeadingJetPt", kTH1D, {axisConfigurations.axisJetPt}); - histos.add("JetKinematicsQA/hLeadingJetEta", "hLeadingJetEta", kTH1D, {axisConfigurations.axisEta}); - histos.add("JetKinematicsQA/hLeadingJetPhi", "hLeadingJetPhi", kTH1D, {axisConfigurations.axisPhi}); + histos.add("JetKinematicsQA/hLeadingJetPt", "hLeadingJetPt;Jet p_{T} [GeV/c];Counts", kTH1D, {axisConfigurations.axisJetPt}); + histos.add("JetKinematicsQA/hLeadingJetEta", "hLeadingJetEta;#eta;Counts", kTH1D, {axisConfigurations.axisEta}); + histos.add("JetKinematicsQA/hLeadingJetPhi", "hLeadingJetPhi;#varphi;Counts", kTH1D, {axisConfigurations.axisPhi}); // 2D correlations: - histos.add("JetKinematicsQA/h2dJetsPerEventvsLeadJetPt", "h2dJetsPerEventvsLeadJetPt", kTH2D, {axisConfigurations.axisJetsPerEvent, axisConfigurations.axisJetPt}); - histos.add("JetKinematicsQA/h2dJetsPerEventvsJetPt", "h2dJetsPerEventvsJetPt", kTH2D, {axisConfigurations.axisJetsPerEvent, axisConfigurations.axisJetPt}); - histos.add("JetKinematicsQA/h2dCosThetaToLeadvsDeltaPhiToLead", "h2dCosThetaToLeadvsDeltaPhiToLead", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisDeltaPhi}); - histos.add("JetKinematicsQA/h2dCosThetaToLeadvsDeltaEtaToLead", "h2dCosThetaToLeadvsDeltaEtaToLead", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisDeltaEta}); - histos.add("JetKinematicsQA/h2dCosThetaToLeadvsDeltaRToLead", "h2dCosThetaToLeadvsDeltaRToLead", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisDeltaR}); - histos.add("JetKinematicsQA/h2dDeltaPhiToLeadvsDeltaEtaToLead", "h2dDeltaPhiToLeadvsDeltaEtaToLead", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDeltaEta}); // to see existence of back-to-back jets, and in which window + histos.add("JetKinematicsQA/h2dJetsPerEventvsLeadJetPt", "h2dJetsPerEventvsLeadJetPt;Jets per event;Jet p_{T} [GeV/c]", kTH2D, {axisConfigurations.axisJetsPerEvent, axisConfigurations.axisJetPt}); + histos.add("JetKinematicsQA/h2dJetsPerEventvsJetPt", "h2dJetsPerEventvsJetPt;Jets per event;Jet p_{T} [GeV/c]", kTH2D, {axisConfigurations.axisJetsPerEvent, axisConfigurations.axisJetPt}); + histos.add("JetKinematicsQA/h2dCosThetaToLeadvsDeltaPhiToLead", "h2dCosThetaToLeadvsDeltaPhiToLead;cos(#Delta#theta_{jet});#Delta#varphi", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisDeltaPhi}); + histos.add("JetKinematicsQA/h2dCosThetaToLeadvsDeltaEtaToLead", "h2dCosThetaToLeadvsDeltaEtaToLead;cos(#Delta#theta_{jet});#Delta#eta", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisDeltaEta}); + histos.add("JetKinematicsQA/h2dCosThetaToLeadvsDeltaRToLead", "h2dCosThetaToLeadvsDeltaRToLead;cos(#Delta#theta_{jet});#Delta R", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisDeltaR}); + histos.add("JetKinematicsQA/h2dDeltaPhiToLeadvsDeltaEtaToLead", "h2dDeltaPhiToLeadvsDeltaEtaToLead;#Delta#varphi;#Delta#eta", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDeltaEta}); // to see existence of back-to-back jets, and in which window // Comparisons to jet energy: - histos.add("JetKinematicsQA/h2dJetPtvsDeltaPhiToLead", "h2dJetPtvsDeltaPhiToLead", kTH2D, {axisConfigurations.axisJetPt, axisConfigurations.axisDeltaPhi}); - histos.add("JetKinematicsQA/h2dJetEnergyvsDeltaPhiToLead", "h2dJetEnergyvsDeltaPhiToLead", kTH2D, {axisConfigurations.axisEnergy, axisConfigurations.axisDeltaPhi}); - histos.add("JetKinematicsQA/h2dJetEnergyvsCosThetaToLead", "h2dJetEnergyvsCosThetaToLead", kTH2D, {axisConfigurations.axisEnergy, axisConfigurations.axisCosTheta}); + histos.add("JetKinematicsQA/h2dJetPtvsDeltaPhiToLead", "h2dJetPtvsDeltaPhiToLead;Jet p_{T} [GeV/c];#Delta#varphi", kTH2D, {axisConfigurations.axisJetPt, axisConfigurations.axisDeltaPhi}); + histos.add("JetKinematicsQA/h2dJetEnergyvsDeltaPhiToLead", "h2dJetEnergyvsDeltaPhiToLead;E_{jet} [GeV];#Delta#varphi", kTH2D, {axisConfigurations.axisEnergy, axisConfigurations.axisDeltaPhi}); + histos.add("JetKinematicsQA/h2dJetEnergyvsCosThetaToLead", "h2dJetEnergyvsCosThetaToLead;E_{jet} [GeV];cos(#Delta#theta_{jet})", kTH2D, {axisConfigurations.axisEnergy, axisConfigurations.axisCosTheta}); // Jets per event vs correlation to lead jet - histos.add("JetKinematicsQA/h2dJetsPerEventvsDeltaPhiToLead", "h2dJetsPerEventvsDeltaPhiToLead", kTH2D, {axisConfigurations.axisJetsPerEvent, axisConfigurations.axisDeltaPhi}); - histos.add("JetKinematicsQA/h2dJetsPerEventvsDeltaEtaToLead", "h2dJetsPerEventvsDeltaEtaToLead", kTH2D, {axisConfigurations.axisJetsPerEvent, axisConfigurations.axisDeltaEta}); - histos.add("JetKinematicsQA/h2dJetsPerEventvsCosThetaToLead", "h2dJetsPerEventvsCosThetaToLead", kTH2D, {axisConfigurations.axisJetsPerEvent, axisConfigurations.axisCosTheta}); + histos.add("JetKinematicsQA/h2dJetsPerEventvsDeltaPhiToLead", "h2dJetsPerEventvsDeltaPhiToLead;Jets per event;#Delta#varphi", kTH2D, {axisConfigurations.axisJetsPerEvent, axisConfigurations.axisDeltaPhi}); + histos.add("JetKinematicsQA/h2dJetsPerEventvsDeltaEtaToLead", "h2dJetsPerEventvsDeltaEtaToLead;Jets per event;#Delta#eta", kTH2D, {axisConfigurations.axisJetsPerEvent, axisConfigurations.axisDeltaEta}); + histos.add("JetKinematicsQA/h2dJetsPerEventvsCosThetaToLead", "h2dJetsPerEventvsCosThetaToLead;Jets per event;cos(#Delta#theta_{jet})", kTH2D, {axisConfigurations.axisJetsPerEvent, axisConfigurations.axisCosTheta}); //////////////////////////// // Leading particle 1D QA: - histos.add("JetVsLeadingParticleQA/hLeadingParticlePt", "hLeadingParticlePt", kTH1D, {axisConfigurations.axisLeadingParticlePt}); - histos.add("JetVsLeadingParticleQA/hLeadingParticleEta", "hLeadingParticleEta", kTH1D, {axisConfigurations.axisEta}); - histos.add("JetVsLeadingParticleQA/hLeadingParticlePhi", "hLeadingParticlePhi", kTH1D, {axisConfigurations.axisPhi}); + histos.add("JetVsLeadingParticleQA/hLeadingParticlePt", "hLeadingParticlePt;Leading particle p_{T} [GeV/c];Counts", kTH1D, {axisConfigurations.axisLeadingParticlePt}); + histos.add("JetVsLeadingParticleQA/hLeadingParticleEta", "hLeadingParticleEta;#eta;Counts", kTH1D, {axisConfigurations.axisEta}); + histos.add("JetVsLeadingParticleQA/hLeadingParticlePhi", "hLeadingParticlePhi;#varphi;Counts", kTH1D, {axisConfigurations.axisPhi}); // 1D correlations to lead jet: - histos.add("JetVsLeadingParticleQA/hCosThetaLeadParticleToJet", "hCosThetaLeadParticleToJet", kTH1D, {axisConfigurations.axisCosTheta}); - histos.add("JetVsLeadingParticleQA/hDeltaPhiLeadParticleToJet", "hDeltaPhiLeadParticleToJet", kTH1D, {axisConfigurations.axisDeltaPhi}); - histos.add("JetVsLeadingParticleQA/hDeltaEtaToLeadParticleToJet", "hDeltaEtaToLeadParticleToJet", kTH1D, {axisConfigurations.axisDeltaEta}); + histos.add("JetVsLeadingParticleQA/hCosThetaLeadParticleToJet", "hCosThetaLeadParticleToJet;cos(#Delta#theta_{jet});Counts", kTH1D, {axisConfigurations.axisCosTheta}); + histos.add("JetVsLeadingParticleQA/hDeltaPhiLeadParticleToJet", "hDeltaPhiLeadParticleToJet;#Delta#varphi;Counts", kTH1D, {axisConfigurations.axisDeltaPhi}); + histos.add("JetVsLeadingParticleQA/hDeltaEtaToLeadParticleToJet", "hDeltaEtaToLeadParticleToJet;#Delta#eta;Counts", kTH1D, {axisConfigurations.axisDeltaEta}); // Leading particle correlations: - histos.add("JetVsLeadingParticleQA/h2dDeltaPhiParticleToLeadvsDeltaEtaParticleToLead", "h2dDeltaPhiParticleToLeadvsDeltaEtaParticleToLead", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDeltaEta}); + histos.add("JetVsLeadingParticleQA/h2dDeltaPhiParticleToLeadvsDeltaEtaParticleToLead", "h2dDeltaPhiParticleToLeadvsDeltaEtaParticleToLead;#Delta#varphi;#Delta#eta", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDeltaEta}); // Jets-per-event vs particle-to-lead correlations: - histos.add("JetVsLeadingParticleQA/h2dJetsPerEventvsDeltaPhiParticleToLead", "h2dJetsPerEventvsDeltaPhiParticleToLead", kTH2D, {axisConfigurations.axisJetsPerEvent, axisConfigurations.axisDeltaPhi}); - histos.add("JetVsLeadingParticleQA/h2dJetsPerEventvsDeltaEtaParticleToLead", "h2dJetsPerEventvsDeltaEtaParticleToLead", kTH2D, {axisConfigurations.axisJetsPerEvent, axisConfigurations.axisDeltaEta}); - histos.add("JetVsLeadingParticleQA/h2dJetsPerEventvsCosThetaParticleToLead", "h2dJetsPerEventvsCosThetaParticleToLead", kTH2D, {axisConfigurations.axisJetsPerEvent, axisConfigurations.axisCosTheta}); + histos.add("JetVsLeadingParticleQA/h2dJetsPerEventvsDeltaPhiParticleToLead", "h2dJetsPerEventvsDeltaPhiParticleToLead;Jets per event;#Delta#varphi", kTH2D, {axisConfigurations.axisJetsPerEvent, axisConfigurations.axisDeltaPhi}); + histos.add("JetVsLeadingParticleQA/h2dJetsPerEventvsDeltaEtaParticleToLead", "h2dJetsPerEventvsDeltaEtaParticleToLead;Jets per event;#Delta#eta", kTH2D, {axisConfigurations.axisJetsPerEvent, axisConfigurations.axisDeltaEta}); + histos.add("JetVsLeadingParticleQA/h2dJetsPerEventvsCosThetaParticleToLead", "h2dJetsPerEventvsCosThetaParticleToLead;Jets per event;cos(#Delta#theta_{jet})", kTH2D, {axisConfigurations.axisJetsPerEvent, axisConfigurations.axisCosTheta}); // Main "Leading jet vs leading particle" correlations: - histos.add("JetVsLeadingParticleQA/h2dJetsPerEventvsLeadParticlePt", "h2dJetsPerEventvsLeadParticlePt", kTH2D, {axisConfigurations.axisJetsPerEvent, axisConfigurations.axisLeadingParticlePt}); - histos.add("JetVsLeadingParticleQA/h2dLeadJetPtvsLeadParticlePt", "h2dLeadJetPtvsLeadParticlePt", kTH2D, {axisConfigurations.axisJetPt, axisConfigurations.axisLeadingParticlePt}); - histos.add("JetVsLeadingParticleQA/h2dLeadJetPtvsCosThetaParticleToLead", "h2dLeadJetPtvsCosThetaParticleToLead", kTH2D, {axisConfigurations.axisJetPt, axisConfigurations.axisCosTheta}); - histos.add("JetVsLeadingParticleQA/h2dLeadParticlePtvsCosThetaParticleToLead", "h2dLeadParticlePtvsCosThetaParticleToLead", kTH2D, {axisConfigurations.axisLeadingParticlePt, axisConfigurations.axisCosTheta}); - histos.add("JetVsLeadingParticleQA/h2dLeadJetPtvsDeltaPhiParticleToLead", "h2dLeadJetPtvsDeltaPhiParticleToLead", kTH2D, {axisConfigurations.axisJetPt, axisConfigurations.axisDeltaPhi}); - histos.add("JetVsLeadingParticleQA/h2dLeadParticlePtvsDeltaPhiParticleToLead", "h2dLeadParticlePtvsDeltaPhiParticleToLead", kTH2D, {axisConfigurations.axisLeadingParticlePt, axisConfigurations.axisDeltaPhi}); + histos.add("JetVsLeadingParticleQA/h2dJetsPerEventvsLeadParticlePt", "h2dJetsPerEventvsLeadParticlePt;Jets per event;Leading particle p_{T} [GeV/c]", kTH2D, {axisConfigurations.axisJetsPerEvent, axisConfigurations.axisLeadingParticlePt}); + histos.add("JetVsLeadingParticleQA/h2dLeadJetPtvsLeadParticlePt", "h2dLeadJetPtvsLeadParticlePt;Jet p_{T} [GeV/c];Leading particle p_{T} [GeV/c]", kTH2D, {axisConfigurations.axisJetPt, axisConfigurations.axisLeadingParticlePt}); + histos.add("JetVsLeadingParticleQA/h2dLeadJetPtvsCosThetaParticleToLead", "h2dLeadJetPtvsCosThetaParticleToLead;Jet p_{T} [GeV/c];cos(#Delta#theta_{jet})", kTH2D, {axisConfigurations.axisJetPt, axisConfigurations.axisCosTheta}); + histos.add("JetVsLeadingParticleQA/h2dLeadParticlePtvsCosThetaParticleToLead", "h2dLeadParticlePtvsCosThetaParticleToLead;Leading particle p_{T} [GeV/c];cos(#Delta#theta_{jet})", kTH2D, {axisConfigurations.axisLeadingParticlePt, axisConfigurations.axisCosTheta}); + histos.add("JetVsLeadingParticleQA/h2dLeadJetPtvsDeltaPhiParticleToLead", "h2dLeadJetPtvsDeltaPhiParticleToLead;Jet p_{T} [GeV/c];#Delta#varphi", kTH2D, {axisConfigurations.axisJetPt, axisConfigurations.axisDeltaPhi}); + histos.add("JetVsLeadingParticleQA/h2dLeadParticlePtvsDeltaPhiParticleToLead", "h2dLeadParticlePtvsDeltaPhiParticleToLead;Leading particle p_{T} [GeV/c];#Delta#varphi", kTH2D, {axisConfigurations.axisLeadingParticlePt, axisConfigurations.axisDeltaPhi}); } // inspect histogram sizes, please @@ -1700,6 +1701,7 @@ struct lambdajetpolarizationions { histos.fill(HIST("JetKinematicsQA/hJetPt"), jet.pt()); histos.fill(HIST("JetKinematicsQA/hJetEta"), jet_eta); histos.fill(HIST("JetKinematicsQA/hJetPhi"), jet.phi()); + histos.fill(HIST("JetKinematicsQA/h2dJetEtaVsJetPhi"), jet_eta, jet.phi()); // Calculate angle to leading jet: float cosTheta = cosThetaJets(leadingJet, jet); diff --git a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx index 0f1d096a3f4..1fe0ad36088 100644 --- a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx @@ -92,6 +92,7 @@ enum CentEstimator { /* Counters */ \ X(FOLDER "/QA/h2dDeltaPhiVsLambdaPt", deltaPhiJet, v0pt) \ X(FOLDER "/QA/h2dDeltaThetaVsLambdaPt", deltaThetaJet, v0pt) \ + X(FOLDER "/QA/hDeltaPhiVsLeadJetPhi", deltaPhiJet, leadingJetPhi) \ /* Additional plots for instant gratification - 1D Profiles */ \ X(FOLDER "/hRingObservableCounts", ringObservable) \ X(FOLDER "/pRingObservableDeltaPhi", deltaPhiJet, ringObservable) \ @@ -317,7 +318,7 @@ struct lambdajetpolarizationionsderived { Configurable doFamilyRing{"doFamilyRing", true, "Book and fill the 'Ring' family (no additional cuts). Keep this on for most passes."}; Configurable doFamilyRingKinematicCuts{"doFamilyRingKinematicCuts", false, "Book and fill the 'RingKinematicCuts' family (Lambda kinematic cuts applied)."}; Configurable doFamilyJetKinematicCuts{"doFamilyJetKinematicCuts", false, "Book and fill the 'JetKinematicCuts' family (jet kinematic cuts applied)."}; - Configurable doFamilyJetAndLambdaKinematicCuts{"doFamilyJetAndLambdaKinematicCuts", false, "Book and fill the 'JetAndLambdaKinematicCuts' family (both cuts applied)."}; + Configurable doFamilyJetAndLambdaKinematicCuts{"doFamilyJetAndLambdaKinematicCuts", true, "Book and fill the 'JetAndLambdaKinematicCuts' family (both cuts applied)."}; } familySwitches; // Centrality: @@ -455,6 +456,7 @@ struct lambdajetpolarizationionsderived { // =============================== histos.add((folder + "/QA/hDeltaPhi").c_str(), "#Delta#varphi_{jet};#Delta#varphi_{jet};Counts", kTH1D, {axisConfigurations.axisDeltaPhi}); histos.add((folder + "/QA/hDeltaPhiVsDeltaEta").c_str(), "#Delta#varphi_{jet};#Delta#varphi_{jet}; #eta_{#Lambda}-#eta_{Jet};Counts", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDeltaEtaCoarse}); + histos.add((folder + "/QA/hDeltaPhiVsLeadJetPhi").c_str(), "#Delta#varphi_{jet};#varphi_{Jet};#varphi_{Jet};Counts", kTH2D, {axisConfigurations.axisPhi, axisConfigurations.axisDeltaPhi}); histos.add((folder + "/QA/hDeltaTheta").c_str(), "#Delta#theta_{jet};#Delta#theta_{jet};Counts", kTH1D, {axisConfigurations.axisDeltaTheta}); histos.add((folder + "/QA/hCosDeltaTheta").c_str(), "cos(#Delta#theta_{jet});cos(#Delta#theta_{jet});Counts", kTH1D, {axisConfigurations.axisCosTheta}); // Should actually be flat due to the geometry histos.add((folder + "/QA/hIntegrated").c_str(), "Integrated counts; ;Counts", kTH1D, {{1, -0.5, 0.5}}); @@ -967,14 +969,14 @@ struct lambdajetpolarizationionsderived { histos.add("hNV0sVsCentrality", "hNV0sVsCentrality; N_{#Lambda}+N_{#bar{#Lambda}};Centrality (%)", kTH2D, {{20, 0, 20}, axisConfigurations.axisCentrality}); // Proxy Eta QA: - histos.add("JetKinematicsQA/hLeadJetEta", "hLeadJetEta", kTH1D, {axisConfigurations.axisEta}); - histos.add("JetKinematicsQA/hSubLeadJetEta", "hSubLeadJetEta", kTH1D, {axisConfigurations.axisEta}); - histos.add("JetKinematicsQA/hLeadPEta", "hLeadPEta", kTH1D, {axisConfigurations.axisEta}); + histos.add("JetKinematicsQA/hLeadJetEta", "hLeadJetEta;#eta;Counts", kTH1D, {axisConfigurations.axisEta}); + histos.add("JetKinematicsQA/hSubLeadJetEta", "hSubLeadJetEta;#eta;Counts", kTH1D, {axisConfigurations.axisEta}); + histos.add("JetKinematicsQA/hLeadPEta", "hLeadPEta;#eta;Counts", kTH1D, {axisConfigurations.axisEta}); // Proxy Phi QA: - histos.add("JetKinematicsQA/hLeadJetPhi", "hLeadJetPhi", kTH1D, {axisConfigurations.axisPhi}); - histos.add("JetKinematicsQA/hSubLeadJetPhi", "hSubLeadJetPhi", kTH1D, {axisConfigurations.axisPhi}); - histos.add("JetKinematicsQA/hLeadPPhi", "hLeadPPhi", kTH1D, {axisConfigurations.axisPhi}); + histos.add("JetKinematicsQA/hLeadJetPhi", "hLeadJetPhi;#varphi;Counts", kTH1D, {axisConfigurations.axisPhi}); + histos.add("JetKinematicsQA/hSubLeadJetPhi", "hSubLeadJetPhi;#varphi;Counts", kTH1D, {axisConfigurations.axisPhi}); + histos.add("JetKinematicsQA/hLeadPPhi", "hLeadPPhi;#varphi;Counts", kTH1D, {axisConfigurations.axisPhi}); // Counting the number of jets/proxies themselves (these count at most once per event) -- Similar to the FOLDER/QA/hPtJet counters: histos.add("JetKinematicsQA/hJetCounterPtJet", "hJetCounterPtJet; p_{T}^{Jet} (GeV/c)", kTH1D, {axisConfigurations.axisJetPt}); From 8de7279ebde0d2921ab19342a30d8582c2131d3f Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Fri, 14 Aug 2026 23:32:23 +0000 Subject: [PATCH 19/31] - TableProducer: Added PVz-related counters to probe possible data asymmetries. Renamed QA TFolders - Consumer: Added simple Ring Vs PVz plots. Included inexpensive mass-window (in Vs out of window) checks as a naive signal-extraction estimator for QAing. - Introduced structure for event mixing of leading particle direction. Some QA plots implemented, still need to run. - TODO: still have to implement event mixing for leading jets and subleading jets - TODO: possible performance improvements over sliceBy --- .../Strangeness/lambdaJetPolarizationIons.cxx | 149 +++++++++------ .../lambdaJetPolarizationIonsDerived.cxx | 174 ++++++++++++++++-- 2 files changed, 251 insertions(+), 72 deletions(-) diff --git a/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx b/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx index 6470eb8958b..7c7312a5ef9 100644 --- a/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx +++ b/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx @@ -311,6 +311,8 @@ struct lambdajetpolarizationions { ConfigurableAxis axisPtCoarse{"axisPtCoarse", {VARIABLE_WIDTH, 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 7.0f, 10.0f, 15.0f}, "pt axis for QA"}; ConfigurableAxis axisLambdaMass{"axisLambdaMass", {450, 1.08f, 1.15f}, ""}; // Default is {200, 1.101f, 1.131f} ConfigurableAxis axisLambdaMassCoarse{"axisLambdaMassCoarse", {200, 1.101f, 1.131f}, ""}; + ConfigurableAxis axisPVz{"axisPVz", {100, -20.0f, +20.0f}, "Primary Vertex Z [cm]"}; + ConfigurableAxis axisPVzCoarse{"axisPVzCoarse", {20, -20.0f, +20.0f}, "Primary Vertex Z [cm]"}; // Centrality/IR/Occupancy: ConfigurableAxis axisCentrality{"axisCentrality", {VARIABLE_WIDTH, 0.0f, 5.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 60.0f, 70.0f, 80.0f, 90.0f}, "Centrality"}; ConfigurableAxis axisNch{"axisNch", {500, 0.0f, +5000.0f}, "Number of charged particles"}; @@ -554,8 +556,8 @@ struct lambdajetpolarizationions { histos.add("Centrality/hEventMultFT0CvsMultFV0A", "hEventMultFT0CvsMultFV0A;Mult. FT0C;Mult. FV0A", kTH2D, {axisConfigurations.axisMultFT0C, axisConfigurations.axisMultFV0A}); } - histos.add("hEventPVz", "hEventPVz;PV_{z} [cm];Counts", kTH1D, {{100, -20.0f, +20.0f}}); - histos.add("hCentralityVsPVz", "hCentralityVsPVz;Centrality (%);PV_{z} [cm]", kTH2D, {{101, 0.0f, 101.0f}, {100, -20.0f, +20.0f}}); + histos.add("hEventPVz", "hEventPVz;PV_{z} [cm];Counts", kTH1D, {axisConfigurations.axisPVz}); + histos.add("hCentralityVsPVz", "hCentralityVsPVz;Centrality (%);PV_{z} [cm]", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisPVz}); // (TODO: add MC centrality vs PVz histos) @@ -857,10 +859,6 @@ struct lambdajetpolarizationions { histos.add("hEventsWithJet", "hEventsWithJet;Integrated counts;Counts", kTH1D, {{1, 0, 1}}); // counter of events with jet (could be interesting to compare with the minimum pT cut or between the background subtraction vs no background subtraction cases) histos.add("hJetsPerEvent", "hJetsPerEvent;Jets per event;Counts", kTH1D, {axisConfigurations.axisJetsPerEvent}); // number of jets per event if (doJetKinematicsQA) { - // For probing the correlation between number of jet constituents and centrality: - histos.add("JetKinematicsQA/h2dConstituentsPerJetVsCentrality", "Constituents per Jet Vs Centrality;N constituents;Centrality (%)", kTH2D, {axisConfigurations.axisJetConstituents, axisConfigurations.axisCentrality}); // Not quite kinematics, but kept it under this QAing switch for now - histos.add("JetKinematicsQA/h2dConstituentsPerJetVsJetPt", "Constituents per Jet Vs Jet Pt;N constituents;Jet p_{T} [GeV/c]", kTH2D, {axisConfigurations.axisJetConstituents, axisConfigurations.axisJetPt}); - histos.add("JetKinematicsQA/hJetPt", "hJetPt;Jet p_{T} [GeV/c];Counts", kTH1D, {axisConfigurations.axisJetPt}); histos.add("JetKinematicsQA/hJetEta", "hJetEta;#eta;Counts", kTH1D, {axisConfigurations.axisEta}); histos.add("JetKinematicsQA/hJetPhi", "hJetPhi;#varphi;Counts", kTH1D, {axisConfigurations.axisPhi}); @@ -893,32 +891,44 @@ struct lambdajetpolarizationions { histos.add("JetKinematicsQA/h2dJetsPerEventvsDeltaEtaToLead", "h2dJetsPerEventvsDeltaEtaToLead;Jets per event;#Delta#eta", kTH2D, {axisConfigurations.axisJetsPerEvent, axisConfigurations.axisDeltaEta}); histos.add("JetKinematicsQA/h2dJetsPerEventvsCosThetaToLead", "h2dJetsPerEventvsCosThetaToLead;Jets per event;cos(#Delta#theta_{jet})", kTH2D, {axisConfigurations.axisJetsPerEvent, axisConfigurations.axisCosTheta}); + // For probing the correlation between number of jet constituents and centrality: + histos.add("JetKinematicsQA/h2dConstituentsPerJetVsCentrality", "Constituents per Jet Vs Centrality;N constituents;Centrality (%)", kTH2D, {axisConfigurations.axisJetConstituents, axisConfigurations.axisCentrality}); // Not quite kinematics, but kept it under this QAing switch for now + histos.add("JetKinematicsQA/h2dConstituentsPerJetVsJetPt", "Constituents per Jet Vs Jet Pt;N constituents;Jet p_{T} [GeV/c]", kTH2D, {axisConfigurations.axisJetConstituents, axisConfigurations.axisJetPt}); + + // Correlation with PVz: + histos.add("JetKinematicsQA/h2dJetEtaVsPVz", "Jet #eta Vs Primary Vertex Z;Jet #eta;PVz [cm]", kTH2D, {axisConfigurations.axisEta, axisConfigurations.axisPVzCoarse}); + histos.add("JetKinematicsQA/h2dJetPhiVsPVz", "Jet #phi Vs Primary Vertex Z;Jet #phi;PVz [cm]", kTH2D, {axisConfigurations.axisPhi, axisConfigurations.axisPVzCoarse}); + //////////////////////////// // Leading particle 1D QA: - histos.add("JetVsLeadingParticleQA/hLeadingParticlePt", "hLeadingParticlePt;Leading particle p_{T} [GeV/c];Counts", kTH1D, {axisConfigurations.axisLeadingParticlePt}); - histos.add("JetVsLeadingParticleQA/hLeadingParticleEta", "hLeadingParticleEta;#eta;Counts", kTH1D, {axisConfigurations.axisEta}); - histos.add("JetVsLeadingParticleQA/hLeadingParticlePhi", "hLeadingParticlePhi;#varphi;Counts", kTH1D, {axisConfigurations.axisPhi}); + histos.add("LeadingParticleQA/hLeadingParticlePt", "hLeadingParticlePt;Leading particle p_{T} [GeV/c];Counts", kTH1D, {axisConfigurations.axisLeadingParticlePt}); + histos.add("LeadingParticleQA/hLeadingParticleEta", "hLeadingParticleEta;#eta;Counts", kTH1D, {axisConfigurations.axisEta}); + histos.add("LeadingParticleQA/hLeadingParticlePhi", "hLeadingParticlePhi;#varphi;Counts", kTH1D, {axisConfigurations.axisPhi}); // 1D correlations to lead jet: - histos.add("JetVsLeadingParticleQA/hCosThetaLeadParticleToJet", "hCosThetaLeadParticleToJet;cos(#Delta#theta_{jet});Counts", kTH1D, {axisConfigurations.axisCosTheta}); - histos.add("JetVsLeadingParticleQA/hDeltaPhiLeadParticleToJet", "hDeltaPhiLeadParticleToJet;#Delta#varphi;Counts", kTH1D, {axisConfigurations.axisDeltaPhi}); - histos.add("JetVsLeadingParticleQA/hDeltaEtaToLeadParticleToJet", "hDeltaEtaToLeadParticleToJet;#Delta#eta;Counts", kTH1D, {axisConfigurations.axisDeltaEta}); + histos.add("LeadingParticleQA/hCosThetaLeadParticleToJet", "hCosThetaLeadParticleToJet;cos(#Delta#theta_{jet});Counts", kTH1D, {axisConfigurations.axisCosTheta}); + histos.add("LeadingParticleQA/hDeltaPhiLeadParticleToJet", "hDeltaPhiLeadParticleToJet;#Delta#varphi;Counts", kTH1D, {axisConfigurations.axisDeltaPhi}); + histos.add("LeadingParticleQA/hDeltaEtaToLeadParticleToJet", "hDeltaEtaToLeadParticleToJet;#Delta#eta;Counts", kTH1D, {axisConfigurations.axisDeltaEta}); // Leading particle correlations: - histos.add("JetVsLeadingParticleQA/h2dDeltaPhiParticleToLeadvsDeltaEtaParticleToLead", "h2dDeltaPhiParticleToLeadvsDeltaEtaParticleToLead;#Delta#varphi;#Delta#eta", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDeltaEta}); + histos.add("LeadingParticleQA/h2dDeltaPhiParticleToLeadvsDeltaEtaParticleToLead", "h2dDeltaPhiParticleToLeadvsDeltaEtaParticleToLead;#Delta#varphi;#Delta#eta", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDeltaEta}); // Jets-per-event vs particle-to-lead correlations: - histos.add("JetVsLeadingParticleQA/h2dJetsPerEventvsDeltaPhiParticleToLead", "h2dJetsPerEventvsDeltaPhiParticleToLead;Jets per event;#Delta#varphi", kTH2D, {axisConfigurations.axisJetsPerEvent, axisConfigurations.axisDeltaPhi}); - histos.add("JetVsLeadingParticleQA/h2dJetsPerEventvsDeltaEtaParticleToLead", "h2dJetsPerEventvsDeltaEtaParticleToLead;Jets per event;#Delta#eta", kTH2D, {axisConfigurations.axisJetsPerEvent, axisConfigurations.axisDeltaEta}); - histos.add("JetVsLeadingParticleQA/h2dJetsPerEventvsCosThetaParticleToLead", "h2dJetsPerEventvsCosThetaParticleToLead;Jets per event;cos(#Delta#theta_{jet})", kTH2D, {axisConfigurations.axisJetsPerEvent, axisConfigurations.axisCosTheta}); + histos.add("LeadingParticleQA/h2dJetsPerEventvsDeltaPhiParticleToLead", "h2dJetsPerEventvsDeltaPhiParticleToLead;Jets per event;#Delta#varphi", kTH2D, {axisConfigurations.axisJetsPerEvent, axisConfigurations.axisDeltaPhi}); + histos.add("LeadingParticleQA/h2dJetsPerEventvsDeltaEtaParticleToLead", "h2dJetsPerEventvsDeltaEtaParticleToLead;Jets per event;#Delta#eta", kTH2D, {axisConfigurations.axisJetsPerEvent, axisConfigurations.axisDeltaEta}); + histos.add("LeadingParticleQA/h2dJetsPerEventvsCosThetaParticleToLead", "h2dJetsPerEventvsCosThetaParticleToLead;Jets per event;cos(#Delta#theta_{jet})", kTH2D, {axisConfigurations.axisJetsPerEvent, axisConfigurations.axisCosTheta}); // Main "Leading jet vs leading particle" correlations: - histos.add("JetVsLeadingParticleQA/h2dJetsPerEventvsLeadParticlePt", "h2dJetsPerEventvsLeadParticlePt;Jets per event;Leading particle p_{T} [GeV/c]", kTH2D, {axisConfigurations.axisJetsPerEvent, axisConfigurations.axisLeadingParticlePt}); - histos.add("JetVsLeadingParticleQA/h2dLeadJetPtvsLeadParticlePt", "h2dLeadJetPtvsLeadParticlePt;Jet p_{T} [GeV/c];Leading particle p_{T} [GeV/c]", kTH2D, {axisConfigurations.axisJetPt, axisConfigurations.axisLeadingParticlePt}); - histos.add("JetVsLeadingParticleQA/h2dLeadJetPtvsCosThetaParticleToLead", "h2dLeadJetPtvsCosThetaParticleToLead;Jet p_{T} [GeV/c];cos(#Delta#theta_{jet})", kTH2D, {axisConfigurations.axisJetPt, axisConfigurations.axisCosTheta}); - histos.add("JetVsLeadingParticleQA/h2dLeadParticlePtvsCosThetaParticleToLead", "h2dLeadParticlePtvsCosThetaParticleToLead;Leading particle p_{T} [GeV/c];cos(#Delta#theta_{jet})", kTH2D, {axisConfigurations.axisLeadingParticlePt, axisConfigurations.axisCosTheta}); - histos.add("JetVsLeadingParticleQA/h2dLeadJetPtvsDeltaPhiParticleToLead", "h2dLeadJetPtvsDeltaPhiParticleToLead;Jet p_{T} [GeV/c];#Delta#varphi", kTH2D, {axisConfigurations.axisJetPt, axisConfigurations.axisDeltaPhi}); - histos.add("JetVsLeadingParticleQA/h2dLeadParticlePtvsDeltaPhiParticleToLead", "h2dLeadParticlePtvsDeltaPhiParticleToLead;Leading particle p_{T} [GeV/c];#Delta#varphi", kTH2D, {axisConfigurations.axisLeadingParticlePt, axisConfigurations.axisDeltaPhi}); + histos.add("LeadingParticleQA/h2dJetsPerEventvsLeadParticlePt", "h2dJetsPerEventvsLeadParticlePt;Jets per event;Leading particle p_{T} [GeV/c]", kTH2D, {axisConfigurations.axisJetsPerEvent, axisConfigurations.axisLeadingParticlePt}); + histos.add("LeadingParticleQA/h2dLeadJetPtvsLeadParticlePt", "h2dLeadJetPtvsLeadParticlePt;Jet p_{T} [GeV/c];Leading particle p_{T} [GeV/c]", kTH2D, {axisConfigurations.axisJetPt, axisConfigurations.axisLeadingParticlePt}); + histos.add("LeadingParticleQA/h2dLeadJetPtvsCosThetaParticleToLead", "h2dLeadJetPtvsCosThetaParticleToLead;Jet p_{T} [GeV/c];cos(#Delta#theta_{jet})", kTH2D, {axisConfigurations.axisJetPt, axisConfigurations.axisCosTheta}); + histos.add("LeadingParticleQA/h2dLeadParticlePtvsCosThetaParticleToLead", "h2dLeadParticlePtvsCosThetaParticleToLead;Leading particle p_{T} [GeV/c];cos(#Delta#theta_{jet})", kTH2D, {axisConfigurations.axisLeadingParticlePt, axisConfigurations.axisCosTheta}); + histos.add("LeadingParticleQA/h2dLeadJetPtvsDeltaPhiParticleToLead", "h2dLeadJetPtvsDeltaPhiParticleToLead;Jet p_{T} [GeV/c];#Delta#varphi", kTH2D, {axisConfigurations.axisJetPt, axisConfigurations.axisDeltaPhi}); + histos.add("LeadingParticleQA/h2dLeadParticlePtvsDeltaPhiParticleToLead", "h2dLeadParticlePtvsDeltaPhiParticleToLead;Leading particle p_{T} [GeV/c];#Delta#varphi", kTH2D, {axisConfigurations.axisLeadingParticlePt, axisConfigurations.axisDeltaPhi}); + + // Correlation with PVz: + histos.add("LeadingParticleQA/h2dLeadPEtaVsPVz", "LeadPtc #eta Vs Primary Vertex Z;LeadPtc #eta;PVz [cm]", kTH2D, {axisConfigurations.axisEta, axisConfigurations.axisPVzCoarse}); + histos.add("LeadingParticleQA/h2dLeadPPhiVsPVz", "LeadPtc #phi Vs Primary Vertex Z;LeadPtc #phi;PVz [cm]", kTH2D, {axisConfigurations.axisPhi, axisConfigurations.axisPVzCoarse}); } // inspect histogram sizes, please @@ -1504,7 +1514,7 @@ struct lambdajetpolarizationions { /// \brief Clusters charged tracks into jets (FastJet, with optional background subtraction), fills the RingJets/RingLeadPs tables for this collision, and fills the jet-kinematics QA. template - void jetsProcess(TJetTracks const& tracks, const int ringCollIdx, const float centrality) + void jetsProcess(TJetTracks const& tracks, const int ringCollIdx, const float centrality, const float collisionPVz) { // Loop over reconstructed tracks: std::vector fjParticles; @@ -1637,34 +1647,42 @@ struct lambdajetpolarizationions { histos.fill(HIST("JetKinematicsQA/h2dJetsPerEventvsDeltaPhiToLead"), selectedJets, deltaPhi); histos.fill(HIST("JetKinematicsQA/h2dJetsPerEventvsDeltaEtaToLead"), selectedJets, deltaEta); histos.fill(HIST("JetKinematicsQA/h2dJetsPerEventvsCosThetaToLead"), selectedJets, cosTheta); + + // Collision PVz: + histos.fill(HIST("JetKinematicsQA/h2dJetEtaVsPVz"), jetMinusBkg.eta(), collisionPVz); + histos.fill(HIST("JetKinematicsQA/h2dJetPhiVsPVz"), jetMinusBkg.phi(), collisionPVz); } // Leading particle comparisons: - histos.fill(HIST("JetVsLeadingParticleQA/hLeadingParticlePt"), leadingParticle.pt()); - histos.fill(HIST("JetVsLeadingParticleQA/hLeadingParticleEta"), leadingParticle.eta()); - histos.fill(HIST("JetVsLeadingParticleQA/hLeadingParticlePhi"), leadingParticle.phi()); + histos.fill(HIST("LeadingParticleQA/hLeadingParticlePt"), leadingParticle.pt()); + histos.fill(HIST("LeadingParticleQA/hLeadingParticleEta"), leadingParticle.eta()); + histos.fill(HIST("LeadingParticleQA/hLeadingParticlePhi"), leadingParticle.phi()); float deltaPhiParticleToJet = RecoDecay::constrainAngle(leadingJetSub.phi() - leadingParticle.phi(), -o2::constants::math::PI); float deltaEtaParticleToJet = leadingJetSub.eta() - leadingParticle.eta(); float cosThetaParticleToJet = cosThetaJets(leadingJetSub, leadingParticle); // Takes advantage of the fact that this leading particle is a PseudoJet object - histos.fill(HIST("JetVsLeadingParticleQA/hCosThetaLeadParticleToJet"), cosThetaParticleToJet); - histos.fill(HIST("JetVsLeadingParticleQA/hDeltaPhiLeadParticleToJet"), deltaPhiParticleToJet); - histos.fill(HIST("JetVsLeadingParticleQA/hDeltaEtaToLeadParticleToJet"), deltaEtaParticleToJet); + histos.fill(HIST("LeadingParticleQA/hCosThetaLeadParticleToJet"), cosThetaParticleToJet); + histos.fill(HIST("LeadingParticleQA/hDeltaPhiLeadParticleToJet"), deltaPhiParticleToJet); + histos.fill(HIST("LeadingParticleQA/hDeltaEtaToLeadParticleToJet"), deltaEtaParticleToJet); + + histos.fill(HIST("LeadingParticleQA/h2dDeltaPhiParticleToLeadvsDeltaEtaParticleToLead"), deltaPhiParticleToJet, deltaEtaParticleToJet); - histos.fill(HIST("JetVsLeadingParticleQA/h2dDeltaPhiParticleToLeadvsDeltaEtaParticleToLead"), deltaPhiParticleToJet, deltaEtaParticleToJet); + histos.fill(HIST("LeadingParticleQA/h2dJetsPerEventvsDeltaPhiParticleToLead"), selectedJets, deltaPhiParticleToJet); + histos.fill(HIST("LeadingParticleQA/h2dJetsPerEventvsDeltaEtaParticleToLead"), selectedJets, deltaEtaParticleToJet); + histos.fill(HIST("LeadingParticleQA/h2dJetsPerEventvsCosThetaParticleToLead"), selectedJets, cosThetaParticleToJet); - histos.fill(HIST("JetVsLeadingParticleQA/h2dJetsPerEventvsDeltaPhiParticleToLead"), selectedJets, deltaPhiParticleToJet); - histos.fill(HIST("JetVsLeadingParticleQA/h2dJetsPerEventvsDeltaEtaParticleToLead"), selectedJets, deltaEtaParticleToJet); - histos.fill(HIST("JetVsLeadingParticleQA/h2dJetsPerEventvsCosThetaParticleToLead"), selectedJets, cosThetaParticleToJet); + histos.fill(HIST("LeadingParticleQA/h2dJetsPerEventvsLeadParticlePt"), selectedJets, leadingParticle.pt()); + histos.fill(HIST("LeadingParticleQA/h2dLeadJetPtvsLeadParticlePt"), leadingJetSub.pt(), leadingParticle.pt()); - histos.fill(HIST("JetVsLeadingParticleQA/h2dJetsPerEventvsLeadParticlePt"), selectedJets, leadingParticle.pt()); - histos.fill(HIST("JetVsLeadingParticleQA/h2dLeadJetPtvsLeadParticlePt"), leadingJetSub.pt(), leadingParticle.pt()); + histos.fill(HIST("LeadingParticleQA/h2dLeadJetPtvsCosThetaParticleToLead"), leadingJetSub.pt(), cosThetaParticleToJet); + histos.fill(HIST("LeadingParticleQA/h2dLeadParticlePtvsCosThetaParticleToLead"), leadingParticle.pt(), cosThetaParticleToJet); - histos.fill(HIST("JetVsLeadingParticleQA/h2dLeadJetPtvsCosThetaParticleToLead"), leadingJetSub.pt(), cosThetaParticleToJet); - histos.fill(HIST("JetVsLeadingParticleQA/h2dLeadParticlePtvsCosThetaParticleToLead"), leadingParticle.pt(), cosThetaParticleToJet); + histos.fill(HIST("LeadingParticleQA/h2dLeadJetPtvsDeltaPhiParticleToLead"), leadingJetSub.pt(), deltaPhiParticleToJet); // To see if there is any backgound in phi due to soft jets (or soft particles below) + histos.fill(HIST("LeadingParticleQA/h2dLeadParticlePtvsDeltaPhiParticleToLead"), leadingParticle.pt(), deltaPhiParticleToJet); - histos.fill(HIST("JetVsLeadingParticleQA/h2dLeadJetPtvsDeltaPhiParticleToLead"), leadingJetSub.pt(), deltaPhiParticleToJet); // To see if there is any backgound in phi due to soft jets (or soft particles below) - histos.fill(HIST("JetVsLeadingParticleQA/h2dLeadParticlePtvsDeltaPhiParticleToLead"), leadingParticle.pt(), deltaPhiParticleToJet); + // Collision PVz -- Leading Particle: + histos.fill(HIST("LeadingParticleQA/h2dLeadPEtaVsPVz"), leadingParticle.eta(), collisionPVz); + histos.fill(HIST("LeadingParticleQA/h2dLeadPPhiVsPVz"), leadingParticle.phi(), collisionPVz); } } else { // Otherwise, simple jet clustering (TODO: this is the fall back for kConstituentBased while not implemented) fastjet::ClusterSequence clustSeq(fjParticles, jetDef); @@ -1696,8 +1714,6 @@ struct lambdajetpolarizationions { // Other variables can better reveal jet quenching and help identify good selection criteria for quenched jets proxies if (doJetKinematicsQA) { - histos.fill(HIST("JetKinematicsQA/h2dConstituentsPerJetVsCentrality"), jet.constituents().size(), centrality); - histos.fill(HIST("JetKinematicsQA/h2dConstituentsPerJetVsJetPt"), jet.constituents().size(), jet.pt()); histos.fill(HIST("JetKinematicsQA/hJetPt"), jet.pt()); histos.fill(HIST("JetKinematicsQA/hJetEta"), jet_eta); histos.fill(HIST("JetKinematicsQA/hJetPhi"), jet.phi()); @@ -1731,9 +1747,16 @@ struct lambdajetpolarizationions { histos.fill(HIST("JetKinematicsQA/h2dJetsPerEventvsDeltaPhiToLead"), jetsInEvent, deltaPhi); histos.fill(HIST("JetKinematicsQA/h2dJetsPerEventvsDeltaEtaToLead"), jetsInEvent, deltaEta); histos.fill(HIST("JetKinematicsQA/h2dJetsPerEventvsCosThetaToLead"), jetsInEvent, cosTheta); + + histos.fill(HIST("JetKinematicsQA/h2dConstituentsPerJetVsCentrality"), jet.constituents().size(), centrality); + histos.fill(HIST("JetKinematicsQA/h2dConstituentsPerJetVsJetPt"), jet.constituents().size(), jet.pt()); + + // Collision PVz: + histos.fill(HIST("JetKinematicsQA/h2dJetEtaVsPVz"), jet.eta(), collisionPVz); + histos.fill(HIST("JetKinematicsQA/h2dJetPhiVsPVz"), jet.phi(), collisionPVz); } } - if (doJetKinematicsQA) { // Fills even when the jet is outside of the (0.9f - jetConfigurations.radiusJet) eta window. Fills at least for the leading jet. + if (doJetKinematicsQA) { // Fills even when the leading jet is outside of the (0.9f - jetConfigurations.radiusJet) eta window. Fills at least for the leading jet. histos.fill(HIST("JetKinematicsQA/h2dConstituentsPerJetVsCentrality"), leadingJet.constituents().size(), centrality); histos.fill(HIST("JetKinematicsQA/h2dConstituentsPerJetVsJetPt"), leadingJet.constituents().size(), leadingJet.pt()); histos.fill(HIST("JetKinematicsQA/hLeadingJetPt"), leadingJet.pt()); @@ -1741,32 +1764,36 @@ struct lambdajetpolarizationions { histos.fill(HIST("JetKinematicsQA/hLeadingJetPhi"), leadingJet.phi()); // Leading particle comparisons: - histos.fill(HIST("JetVsLeadingParticleQA/hLeadingParticlePt"), leadingParticle.pt()); - histos.fill(HIST("JetVsLeadingParticleQA/hLeadingParticleEta"), leadingParticle.eta()); - histos.fill(HIST("JetVsLeadingParticleQA/hLeadingParticlePhi"), leadingParticle.phi()); + histos.fill(HIST("LeadingParticleQA/hLeadingParticlePt"), leadingParticle.pt()); + histos.fill(HIST("LeadingParticleQA/hLeadingParticleEta"), leadingParticle.eta()); + histos.fill(HIST("LeadingParticleQA/hLeadingParticlePhi"), leadingParticle.phi()); double deltaPhiParticleToJet = RecoDecay::constrainAngle(leadingJet.phi() - leadingParticle.phi(), -o2::constants::math::PI); double deltaEtaParticleToJet = leadingJet.eta() - leadingParticle.eta(); double cosThetaParticleToJet = cosThetaJets(leadingJet, leadingParticle); // Takes advantage of the fact that this leading particle is a PseudoJet object - histos.fill(HIST("JetVsLeadingParticleQA/hCosThetaLeadParticleToJet"), cosThetaParticleToJet); - histos.fill(HIST("JetVsLeadingParticleQA/hDeltaPhiLeadParticleToJet"), deltaPhiParticleToJet); - histos.fill(HIST("JetVsLeadingParticleQA/hDeltaEtaToLeadParticleToJet"), deltaEtaParticleToJet); + histos.fill(HIST("LeadingParticleQA/hCosThetaLeadParticleToJet"), cosThetaParticleToJet); + histos.fill(HIST("LeadingParticleQA/hDeltaPhiLeadParticleToJet"), deltaPhiParticleToJet); + histos.fill(HIST("LeadingParticleQA/hDeltaEtaToLeadParticleToJet"), deltaEtaParticleToJet); + + histos.fill(HIST("LeadingParticleQA/h2dDeltaPhiParticleToLeadvsDeltaEtaParticleToLead"), deltaPhiParticleToJet, deltaEtaParticleToJet); - histos.fill(HIST("JetVsLeadingParticleQA/h2dDeltaPhiParticleToLeadvsDeltaEtaParticleToLead"), deltaPhiParticleToJet, deltaEtaParticleToJet); + histos.fill(HIST("LeadingParticleQA/h2dJetsPerEventvsDeltaPhiParticleToLead"), jetsInEvent, deltaPhiParticleToJet); + histos.fill(HIST("LeadingParticleQA/h2dJetsPerEventvsDeltaEtaParticleToLead"), jetsInEvent, deltaEtaParticleToJet); + histos.fill(HIST("LeadingParticleQA/h2dJetsPerEventvsCosThetaParticleToLead"), jetsInEvent, cosThetaParticleToJet); - histos.fill(HIST("JetVsLeadingParticleQA/h2dJetsPerEventvsDeltaPhiParticleToLead"), jetsInEvent, deltaPhiParticleToJet); - histos.fill(HIST("JetVsLeadingParticleQA/h2dJetsPerEventvsDeltaEtaParticleToLead"), jetsInEvent, deltaEtaParticleToJet); - histos.fill(HIST("JetVsLeadingParticleQA/h2dJetsPerEventvsCosThetaParticleToLead"), jetsInEvent, cosThetaParticleToJet); + histos.fill(HIST("LeadingParticleQA/h2dJetsPerEventvsLeadParticlePt"), jetsInEvent, leadingParticle.pt()); + histos.fill(HIST("LeadingParticleQA/h2dLeadJetPtvsLeadParticlePt"), leadingJet.pt(), leadingParticle.pt()); - histos.fill(HIST("JetVsLeadingParticleQA/h2dJetsPerEventvsLeadParticlePt"), jetsInEvent, leadingParticle.pt()); - histos.fill(HIST("JetVsLeadingParticleQA/h2dLeadJetPtvsLeadParticlePt"), leadingJet.pt(), leadingParticle.pt()); + histos.fill(HIST("LeadingParticleQA/h2dLeadJetPtvsCosThetaParticleToLead"), leadingJet.pt(), cosThetaParticleToJet); + histos.fill(HIST("LeadingParticleQA/h2dLeadParticlePtvsCosThetaParticleToLead"), leadingParticle.pt(), cosThetaParticleToJet); - histos.fill(HIST("JetVsLeadingParticleQA/h2dLeadJetPtvsCosThetaParticleToLead"), leadingJet.pt(), cosThetaParticleToJet); - histos.fill(HIST("JetVsLeadingParticleQA/h2dLeadParticlePtvsCosThetaParticleToLead"), leadingParticle.pt(), cosThetaParticleToJet); + histos.fill(HIST("LeadingParticleQA/h2dLeadJetPtvsDeltaPhiParticleToLead"), leadingJet.pt(), deltaPhiParticleToJet); // To see if there is any backgound in phi due to soft jets (or soft particles below) + histos.fill(HIST("LeadingParticleQA/h2dLeadParticlePtvsDeltaPhiParticleToLead"), leadingParticle.pt(), deltaPhiParticleToJet); - histos.fill(HIST("JetVsLeadingParticleQA/h2dLeadJetPtvsDeltaPhiParticleToLead"), leadingJet.pt(), deltaPhiParticleToJet); // To see if there is any backgound in phi due to soft jets (or soft particles below) - histos.fill(HIST("JetVsLeadingParticleQA/h2dLeadParticlePtvsDeltaPhiParticleToLead"), leadingParticle.pt(), deltaPhiParticleToJet); + // Collision PVz -- Leading Particle: + histos.fill(HIST("LeadingParticleQA/h2dLeadPEtaVsPVz"), leadingParticle.eta(), collisionPVz); + histos.fill(HIST("LeadingParticleQA/h2dLeadPPhiVsPVz"), leadingParticle.phi(), collisionPVz); } } } @@ -1803,18 +1830,20 @@ struct lambdajetpolarizationions { if (interactionRate < 0) interactionRate = rateFetcher.fetch(ccdb.service, bc.timestamp(), bc.runNumber(), irSource) * 1.e-3; + const float collisionPVz = collision.posZ(); + // Fill event table: tableCollisions(collision.centFT0M(), collision.centFT0C(), collision.centFV0A(), - collision.posZ(), + collisionPVz, interactionRate); // Get the derived collision row index for this event: const int ringCollIdx = tableCollisions.lastIndex(); // Call to jets process: - jetsProcess(V0DauTracks, ringCollIdx, centrality); // V0DauTracks takes the place of jetTracks now + jetsProcess(V0DauTracks, ringCollIdx, centrality, collisionPVz); // V0DauTracks takes the place of jetTracks now // Counting particles per event: uint NLambdas = 0; diff --git a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx index 1fe0ad36088..39af9617585 100644 --- a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx @@ -35,9 +35,12 @@ #include #include #include +#include #include #include +#include #include +#include #include #include #include @@ -57,6 +60,7 @@ #include #include #include +#include using namespace o2; using namespace o2::framework; @@ -103,6 +107,7 @@ enum CentEstimator { X(FOLDER "/EtaDependence/pRingObservableEtaJet", leadingJetEta, ringObservable) \ X(FOLDER "/pRingObservableIntegrated", 0., ringObservable) \ X(FOLDER "/pRingObservableLambdaPt", v0pt, ringObservable) \ + X(FOLDER "/pRingObservableLeadJetPVz", collisionPVz, ringObservable) \ X(FOLDER "/ProxyPtDependence/pRingVsPtJet", leadingJetPt, ringObservable) \ X(FOLDER "/ProxyPtDependence/pRingVsPtJetVsEtaJet", leadingJetPt, leadingJetEta, ringObservable) \ X(FOLDER "/ProxyPtDependence/pRingVsPtJetVsEtaV0", leadingJetPt, v0eta, ringObservable) \ @@ -165,10 +170,11 @@ enum CentEstimator { X(FOLDER "/pRingObservableLeadPDeltaPhi", deltaPhiLeadP, ringObservableLeadP) \ X(FOLDER "/pRingObservableLeadPDeltaTheta", deltaThetaLeadP, ringObservableLeadP) \ X(FOLDER "/EtaDependence/pRingObservableEtaLambdaLeadP", v0eta, ringObservableLeadP) \ - X(FOLDER "/EtaDependence/pRingObservableEtaLeadP", leadPEta, ringObservableLeadP) \ + X(FOLDER "/EtaDependence/pRingObservableEtaLeadP", leadPEta, ringObservableLeadP) \ X(FOLDER "/pRingObservableLeadPIntegrated", 0., ringObservableLeadP) \ X(FOLDER "/pRingObservableLeadPLambdaPt", v0pt, ringObservableLeadP) \ - X(FOLDER "/ProxyPtDependence/pRingVsPtLeadP", leadPPt, ringObservableLeadP) \ + X(FOLDER "/pRingObservableLeadPPVz", collisionPVz, ringObservableLeadP) \ + X(FOLDER "/ProxyPtDependence/pRingVsPtLeadP", leadPPt, ringObservableLeadP) \ X(FOLDER "/ProxyPtDependence/pRingVsPtLeadPVsEtaLeadP", leadPPt, leadPEta, ringObservableLeadP) \ X(FOLDER "/ProxyPtDependence/pRingVsPtLeadPVsEtaV0", leadPPt, v0eta, ringObservableLeadP) \ X(FOLDER "/EtaDependence/p2dRingObservableEtaLambdaVsEtaLeadP", v0eta, leadPEta, ringObservableLeadP) \ @@ -221,11 +227,12 @@ enum CentEstimator { X(FOLDER "/hRingObservable2ndJetCounter", ringObservable2ndJet) \ X(FOLDER "/pRingObservable2ndJetDeltaPhi", deltaPhi2ndJet, ringObservable2ndJet) \ X(FOLDER "/pRingObservable2ndJetDeltaTheta", deltaTheta2ndJet, ringObservable2ndJet) \ - X(FOLDER "/EtaDependence/pRingObservableEtaLambda2ndJet", v0eta, ringObservable2ndJet) \ - X(FOLDER "/EtaDependence/pRingObservableEta2ndJet", subleadingJetEta, ringObservable2ndJet) \ + X(FOLDER "/EtaDependence/pRingObservableEtaLambda2ndJet", v0eta, ringObservable2ndJet) \ + X(FOLDER "/EtaDependence/pRingObservableEta2ndJet", subleadingJetEta, ringObservable2ndJet) \ X(FOLDER "/pRingObservable2ndJetIntegrated", 0., ringObservable2ndJet) \ X(FOLDER "/pRingObservable2ndJetLambdaPt", v0pt, ringObservable2ndJet) \ - X(FOLDER "/ProxyPtDependence/pRingVsPt2ndJet", subleadingJetPt, ringObservable2ndJet) \ + X(FOLDER "/pRingObservableSubLeadPVz", collisionPVz, ringObservable2ndJet) \ + X(FOLDER "/ProxyPtDependence/pRingVsPt2ndJet", subleadingJetPt, ringObservable2ndJet) \ X(FOLDER "/ProxyPtDependence/pRingVsPt2ndJetVsEta2ndJet", subleadingJetPt, subleadingJetEta, ringObservable2ndJet) \ X(FOLDER "/ProxyPtDependence/pRingVsPt2ndJetVsEtaV0", subleadingJetPt, v0eta, ringObservable2ndJet) \ X(FOLDER "/EtaDependence/p2dRingObservableEtaLambdaVsEta2ndJet", v0eta, subleadingJetEta, ringObservable2ndJet) \ @@ -310,6 +317,12 @@ struct lambdajetpolarizationionsderived { Configurable analyseAntiLambda{"analyseAntiLambda", false, "process AntiLambda-like candidates"}; Configurable analyseMagField{"analyseMagField", true, "analyse efficiency effects wrt magnetic field"}; // Older DerivedData lacks magField. "if constexpr (requires { collision.magField(); })" would only see the current header definition, so need a flag for retro-comp. // Configurable doPPAnalysis{"doPPAnalysis", false, "if in pp, set to true. Default is HI"}; + Configurable calculateEventMixingHists{"calculateEventMixingHists", false, "generates expensive THn histograms for a-posteriori event-mixing-like computations."}; + + // A very inexpensive "signal extraction" imitation based on v0InMassPeak bool: + // (Uses a mass interval to remove or include V0s from the final AnalysisResults to take advantage of existing post-processing codes) + Configurable excludeOutOfPeakQA{"excludeOutOfPeakQA", false, "removes all V0s outside an approximate +/- 5*sigma window from the mass peak"}; // A naive estimator of signal + Configurable excludeInPeakQA{"excludeInPeakQA", false, "uses only the V0s outside an approximate +/- 5*sigma window from the mass peak"}; // A naive estimator of background // Per-family histogram switches: // (Each family books >100 histograms, so it is necessary to keep some of these switches off to avoid the HistogramRegistry limit) @@ -334,7 +347,9 @@ struct lambdajetpolarizationionsderived { Configurable forceJetDirectionSmudge{"forceJetDirectionSmudge", false, "fluctuate jet direction by 10% of R around original axis. For QA (tests sensibility)"}; Configurable forceRandJet{"forceRandJet", false, "makes jet direction random. A QA for AEE fake signal and its removal"}; Configurable forcePreviousJet{"forcePreviousJet", false, "uses previous event's jet direction instead of a random sample. A baseline for fake signal removal"}; - Configurable forceDatalikeJet{"forceDatalikeJet", false, "A compromise between forceRandJet and forcePreviousJet. Parameterized distribution from data"}; + Configurable forceDatalikeJet{"forceDatalikeJet", false, "a compromise between forceRandJet and forcePreviousJet. Parameterized distribution from data"}; + Configurable doMixedEventLeadP{"doMixedEventLeadP", false, "mix leading particle direction between events using (LeadP pt, Zvtx, centrality) bins"}; + Configurable mixedEventLeadPWindowSize{"mixedEventLeadPWindowSize", 10, "number of neighbours for doMixedEventLeadP: how many similar collisions stay eligible as mixing partners at once."}; Configurable nProxyResamples{"nProxyResamples", 1, "The amount of resamplings of jet direction per event. Use ONLY for forceRandJet and forceDatalikeJet"}; } fakePolSwitches; @@ -358,6 +373,9 @@ struct lambdajetpolarizationionsderived { ConfigurableAxis axisLambdaPy{"axisLambdaPy", {40, -3.0f, 3.0f}, "#Lambda p_{y} (GeV/c)"}; ConfigurableAxis axisLambdaPz{"axisLambdaPz", {40, -4.0f, 4.0f}, "#Lambda p_{z} (GeV/c)"}; + // Event properties: + ConfigurableAxis axisPVz{"axisPVz", {60, -15.0f, +15.0f}, "Primary Vertex Z [cm]"}; + // Jet axes: // ConfigurableAxis axisLeadingParticlePt{"axisLeadingParticlePt", {100, 0.f, 200.f}, "Leading particle p_{T} (GeV/c)"}; // Simpler version! // ConfigurableAxis axisJetPt{"axisJetPt", {50, 0.f, 200.f}, "Jet p_{t} (GeV)"}; @@ -585,6 +603,11 @@ struct lambdajetpolarizationionsderived { histos.add((folder + "/pRingObservable2ndJetDeltaTheta").c_str(), "<#it{R}> vs #Delta#theta_{SubJet};#Delta#theta_{SubJet};<#it{R}>", kTProfile, {axisConfigurations.axisDeltaTheta}); histos.add((folder + "/pRingObservable2ndJetIntegrated").c_str(), "Integrated <#it{R}> (SubJet); ;<#it{R}>", kTProfile, {{1, -0.5, 0.5}}); histos.add((folder + "/pRingObservable2ndJetLambdaPt").c_str(), "<#it{R}> vs #it{p}_{T}^{#Lambda} (SubJet);#it{p}_{T}^{#Lambda} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisPt}); + + // For the Zvtx dependence: + histos.add((folder + "/pRingObservableLeadJetPVz").c_str(), "<#it{R}>_{LeadJet} vs PVz;PVz (cm);<#it{R}>", kTProfile, {axisConfigurations.axisPVz}); + histos.add((folder + "/pRingObservableSubLeadPVz").c_str(), "<#it{R}>_{SubLead} vs PVz;PVz (cm);<#it{R}>", kTProfile, {axisConfigurations.axisPVz}); + histos.add((folder + "/pRingObservableLeadPPVz").c_str(), "<#it{R}>_{LeadP} vs PVz;PVz (cm);<#it{R}>", kTProfile, {axisConfigurations.axisPVz}); // =============================== // 2D TProfiles (Lambda correlations) // =============================== @@ -785,8 +808,8 @@ struct lambdajetpolarizationionsderived { histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"))->GetXaxis()->SetBinLabel(7, "#eta_{LeadP} #geq 0, #eta_{#Lambda} < 0"); histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"))->GetXaxis()->SetBinLabel(8, "#eta_{LeadP} < 0, #eta_{#Lambda} #geq 0"); histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"))->GetXaxis()->SetBinLabel(9, "#eta_{LeadP} < 0, #eta_{#Lambda} < 0"); - histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"))->GetYaxis()->SetBinLabel(1, "#Lambda in mass peak"); - histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"))->GetYaxis()->SetBinLabel(2, "#Lambda out of mass peak"); + histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"))->GetYaxis()->SetBinLabel(1, "#Lambda out of mass peak"); + histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"))->GetYaxis()->SetBinLabel(2, "#Lambda in mass peak"); // Fake polarization signal QA // --> The "negative helicity problem", where topologies with a proton decaying opposite to the Lambda momentum are enhanced by @@ -983,6 +1006,30 @@ struct lambdajetpolarizationionsderived { histos.add("JetKinematicsQA/hJetCounterPtLeadP", "hJetCounterPtLeadP; p_{T}^{LeadP} (GeV/c)", kTH1D, {axisConfigurations.axisJetPt}); histos.add("JetKinematicsQA/hJetCounterPt2ndJet", "hJetCounterPt2ndJet; p_{T}^{SubJet} (GeV/c)", kTH1D, {axisConfigurations.axisJetPt}); + // Proxy Eta vs PVz: + histos.add("JetKinematicsQA/h2dLeadJetEtaVsPVz", "Lead Jet #eta Vs PVz;#eta;Primary Vertex Z [cm];Counts", kTH2D, {axisConfigurations.axisEta, axisConfigurations.axisPVz}); + histos.add("JetKinematicsQA/h2dSubLeadJetEtaVsPVz", "SubLead Jet #eta Vs PVz;#eta;Primary Vertex Z [cm];Counts", kTH2D, {axisConfigurations.axisEta, axisConfigurations.axisPVz}); + histos.add("JetKinematicsQA/h2dLeadPEtaVsPVz", "Lead Ptc #eta Vs PVz;#eta;Primary Vertex Z [cm];Counts", kTH2D, {axisConfigurations.axisEta, axisConfigurations.axisPVz}); + + // For building and event-mixing-like procedure similar to forceDatalikeJet: + if (calculateEventMixingHists) { + histos.add("JetKinematicsQA/h5dLeadJetEtaPhiPtPVzCent", "h5dLeadJetEtaPhiPtPVzCent;#eta;#phi;p_{t};Primary Vertex Z [cm];Centrality (%);Counts", kTHnSparseF, + {axisConfigurations.axisEtaCoarse, axisConfigurations.axisPhi, axisConfigurations.axisJetPt, axisConfigurations.axisPVz, axisConfigurations.axisCentrality}); + histos.add("JetKinematicsQA/h5dSubLeadJetEtaPhiPtPVzCent", "h5dSubLeadJetEtaPhiPtPVzCent;#eta;#phi;p_{t};Primary Vertex Z [cm];Centrality (%);Counts", kTHnSparseF, + {axisConfigurations.axisEtaCoarse, axisConfigurations.axisPhi, axisConfigurations.axisJetPt, axisConfigurations.axisPVz, axisConfigurations.axisCentrality}); + histos.add("JetKinematicsQA/h5dLeadPEtaPhiPtPVzCent", "h5dLeadPEtaPhiPtPVzCent;#eta;#phi;p_{t};Primary Vertex Z [cm];Centrality (%);Counts", kTHnSparseF, + {axisConfigurations.axisEtaCoarse, axisConfigurations.axisPhi, axisConfigurations.axisJetPt, axisConfigurations.axisPVz, axisConfigurations.axisCentrality}); + } + + // doMixedEventLeadP QA: gauge the size of the "too few collisions per bin" problem (see resonanceMergeDF.cxx) + if (fakePolSwitches.doMixedEventLeadP) { + histos.add("JetKinematicsQA/hMixedEventLeadPOutcome", "hMixedEventLeadPOutcome;Outcome (0=skipped, 1=found);Counts", kTH1D, {{2, -0.5f, 1.5f}}); + histos.add("JetKinematicsQA/hMixedEventLeadPWindowNeighbours", "hMixedEventLeadPWindowNeighbours;Neighbours found in bin window;Counts", kTH1D, {{22, -0.5f, 21.5f}}); + + histos.add("JetKinematicsQA/h2dMixedLeadPEtaVsLeadPEta", "MixedLeadP #eta vs LeadP #eta;MixedLeadP #eta; LeadP #eta;Counts", kTH2D, {axisConfigurations.axisEtaCoarse, axisConfigurations.axisEtaCoarse}); + histos.add("JetKinematicsQA/h2dMixedLeadPPhiVsLeadPPhi", "MixedLeadP #phi vs LeadP #phi;MixedLeadP #phi; LeadP #phi;Counts", kTH2D, {axisConfigurations.axisPhi, axisConfigurations.axisPhi}); + } + // Fetch the X-axes from one of the families (since they all share the same ConfigurableAxis binning) mAxisPt = histos.get(HIST("Ring/DeltaMethod/h2dLambdaPtVsDeltaComp"))->GetXaxis(); mAxisMass = histos.get(HIST("Ring/DeltaMethod/h2dMassVsDeltaComp"))->GetXaxis(); @@ -1019,13 +1066,16 @@ struct lambdajetpolarizationionsderived { /// \param hasValidProxy in/out (edited in-place): whether the proxy being distorted is usable. Re-evaluated against minPtThreshold after distortion. /// \param pt,eta,phi in/out (edited in-place): the proxy's kinematics, overwritten by whichever distortion is active. /// \param unitVec in/out (edited in-place): the proxy direction as a unit vector. - /// \param cacheHadProxy,cacheEta,cachePhi the previous collision's proxy, used (and updated) by forcePreviousJet. + /// \param cacheHadProxy,cacheEta,cachePhi caches for either forcePreviousJet or doMixedEventLeadP. + // forcePreviousJet updates these here for the next collision, in a simplistic event event mixing approach. + // doMixedEventLeadP instead overwrites prevJetCache's fields with this collision's mixed proxy right before the call. + /// Fallback skips this event using hasValidProxy. /// \param etaDist,phiDist,rng sampling distributions/generator for forceRandJet and forceDatalikeJet. // Helper to modify the jet direction for QA and for spurious signal baseline removal tests: inline void applyProxyDistortion(bool& hasValidProxy, float& pt, float& eta, float& phi, XYZVector& unitVec, float minPtThreshold, bool& cacheHadProxy, float& cacheEta, float& cachePhi, std::discrete_distribution& etaDist, std::discrete_distribution& phiDist, std::mt19937& rng){ - if (!fakePolSwitches.forcePerpToJet && !fakePolSwitches.forceJetDirectionSmudge && !fakePolSwitches.forceRandJet && !fakePolSwitches.forcePreviousJet && !fakePolSwitches.forceDatalikeJet)[[likely]] { + if (!fakePolSwitches.forcePerpToJet && !fakePolSwitches.forceJetDirectionSmudge && !fakePolSwitches.forceRandJet && !fakePolSwitches.forcePreviousJet && !fakePolSwitches.forceDatalikeJet && !fakePolSwitches.doMixedEventLeadP)[[likely]] { return; // Skip this function if none of the modifications are actually being executed! } @@ -1122,6 +1172,19 @@ struct lambdajetpolarizationionsderived { const double sinPhi = std::sin(phi); const double cosPhi = std::cos(phi); unitVec = XYZVector(cosPhi * inverseCoshEta, sinPhi * inverseCoshEta, std::tanh(eta)); + } else if (fakePolSwitches.doMixedEventLeadP) { + // Mixes the leadP direction from a real, similar other collision, rather than sampling a fitted distribution as datalikeJet. + // Only unitVec is rebuilt, eta/phi are left as this collision's own real values -- same convention forcePreviousJet uses above. + if (cacheHadProxy) { + const double inverseCoshEta = 1.0 / std::cosh(cacheEta); + const double sinPhi = std::sin(cachePhi); + const double cosPhi = std::cos(cachePhi); + unitVec = XYZVector(cosPhi * inverseCoshEta, sinPhi * inverseCoshEta, std::tanh(cacheEta)); + } else { + // No mixing partner found for this collision (sparse bins!). + // Same skip that forcePreviousJet does above: an event without a valid mixed proxy cannot be used. + hasValidProxy = false; + } } // Recalculating pT, phi and eta after distortions: @@ -1164,16 +1227,70 @@ struct lambdajetpolarizationionsderived { Preslice perColJets = o2::aod::lambdajetpol::ringCollisionId; Preslice perColV0s = o2::aod::lambdajetpol::ringCollisionId; Preslice perColLeadPs = o2::aod::lambdajetpol::ringCollisionId; + // For doMixedEventLeadP: + SliceCache mixCache; /// \brief Main analysis loop: for each collision, rebuilds the leading jet/particle proxies (with optional fakePolSwitches distortions), then loops over V0s computing the ring observable and polarization-vector profiles for every enabled kinematic-cut family. void processPolarizationData(o2::aod::RingCollisions const& collisions, o2::aod::RingJets const& jets, o2::aod::RingLaV0s const& v0s, o2::aod::RingLeadPs const& leadPs) { + + // Event mixing, built once per dataframe + // Simplistic event mixing using previous collision: PrevJetCache prevJetCache{}; // Initializing struct before the collisions loop. This zero-initializes, so bools start as false // This should not be used along nProxyResamples > 1, as it does not apply to that case. + // doMixedEventLeadP: Binning on (Zvtx, leadP pt, centrality) lets eta/phi vary freely. + // Leading/subleading jet mixing are a separate, independent feature NOT implemented yet. Rough sketch of the difference, for later: + // auto getMixJetPt = [&jets, &mixCache](aod::RingCollision const& col) { + // auto rows = jets.sliceByCached(o2::aod::lambdajetpol::ringCollisionId, col.globalIndex(), mixCache); + // float leadPt = -999.f; + // for (auto const& jet : rows) leadPt = std::max(leadPt, jet.jetPt()); // needs the max-pt scan; RingLeadPs needs no such scan + // return leadPt; + // }; + struct MixedLeadPInfo { float eta; float phi; }; + std::unordered_map mixedLeadPByCollision; + // First we build a lookup table based on current dataframe's collisions (connects pairs of jet proxies from similar collisions): + // (these leading particles may come from collisions with no valid Lambdas, by construction) + if (fakePolSwitches.doMixedEventLeadP) { + // Pattern follows MixedEventsLambdaBinning tutorial (captures leadPs table and cache define in task's struct): + auto getMixLeadPPt = [&leadPs, this](o2::aod::RingCollision const& col) { + auto rows = leadPs.sliceByCached(o2::aod::lambdajetpol::ringCollisionId, col.globalIndex(), this->mixCache); // As it is cached, grouping happens only once + return rows.size() > 0 ? rows.begin().leadParticlePt() : -999.f; + }; + auto getMixCentrality = [this](o2::aod::RingCollision const& col) { return this->getCentrality(col); }; + + using MixBinningType = FlexibleBinningPolicy, + o2::aod::lambdajetpol::Zvtx, decltype(getMixLeadPPt), decltype(getMixCentrality)>; + MixBinningType mixBinning{{getMixLeadPPt, getMixCentrality}, + {axisConfigurations.axisPVz, axisConfigurations.axisJetPt, axisConfigurations.axisCentrality}, + true}; // Ignore overflows true + + // SameKindPair defaults to CombinationsBlockStrictlyUpperSameIndexPolicy, so no same-event mixing should happen: + SameKindPair mixPair{ + mixBinning, fakePolSwitches.mixedEventLeadPWindowSize, -1, collisions, std::make_tuple(leadPs), &mixCache}; + + // "Last neighbour wins" if a collision appears in more than one pair within its window -- simple and + // deterministic, not a uniform-random pick among the window's members: + for (auto it = mixPair.begin(); it != mixPair.end(); ++it) { + auto& [c1, leadP1, c2, leadP2] = *it; // Iterates over collision pairs and leading particle pairs (structured binding) + if (leadP1.size() > 0 && leadP2.size() > 0) { // There should always be at least one leadP, given the overflow exclusion above + float eta1 = 0.f, phi1 = 0.f, eta2 = 0.f, phi2 = 0.f; + for (auto const& lp : leadP1) { eta1 = lp.leadParticleEta(); phi1 = lp.leadParticlePhi(); break; } // Retrieves the first entry + for (auto const& lp : leadP2) { eta2 = lp.leadParticleEta(); phi2 = lp.leadParticlePhi(); break; } + // Saving the mixed leading particles with a key referring to the two collisions being mixed: + mixedLeadPByCollision[c1.globalIndex()] = {eta2, phi2}; + mixedLeadPByCollision[c2.globalIndex()] = {eta1, phi1}; + } + if (it.isNewWindow()) { // count each bin-window once, not once per pair inside it + histos.fill(HIST("JetKinematicsQA/hMixedEventLeadPWindowNeighbours"), it.currentWindowNeighbours()); + } + } + } + for (int idxResampling = 0; idxResampling < fakePolSwitches.nProxyResamples; idxResampling++){ // resampling loop for forceRandJet and forceDatalikeJet for (auto const& collision : collisions) { - if (std::abs(collision.zvtx()) > maxZVtxPosition) // Additional Zvtx filtering + const float collisionPVz = collision.zvtx(); + if (std::abs(collisionPVz) > maxZVtxPosition) // Additional Zvtx filtering continue; const auto collId = collision.globalIndex(); // The self-index accessor @@ -1240,6 +1357,21 @@ struct lambdajetpolarizationionsderived { // Apply distortion logic: const bool hadPreviousProxy = prevJetCache.hadLeadP; + + // doMixedEventLeadP: get this collision's mixing partner (if any) from the LUT built above: + // (this check is performed only if hasValidLeadingP is true for performance, as the LeadPPt matching for the event mixing would also demand a minimum jet pT) + if (fakePolSwitches.doMixedEventLeadP) { + auto itMix = mixedLeadPByCollision.find(collId); + prevJetCache.hadLeadP = (itMix != mixedLeadPByCollision.end()); // Check if this event had a valid mixing target. Reusing prevJetCache + if (prevJetCache.hadLeadP) { + prevJetCache.leadPEta = itMix->second.eta; + prevJetCache.leadPPhi = itMix->second.phi; + } + histos.fill(HIST("JetKinematicsQA/hMixedEventLeadPOutcome"), prevJetCache.hadLeadP ? 1 : 0); + histos.fill(HIST("JetKinematicsQA/h2dMixedLeadPEtaVsLeadPEta"), prevJetCache.leadPEta, leadPEta); + histos.fill(HIST("JetKinematicsQA/h2dMixedLeadPPhiVsLeadPPhi"), prevJetCache.leadPPhi, leadPPhi); + } + applyProxyDistortion(hasValidLeadingP, leadPPt, leadPEta, leadPPhi, leadPUnitVec, minLeadParticlePt, prevJetCache.hadLeadP, prevJetCache.leadPEta, prevJetCache.leadPPhi, etaLeadPDist, phiLeadPDist, rng); @@ -1250,6 +1382,10 @@ struct lambdajetpolarizationionsderived { histos.fill(HIST("JetKinematicsQA/hLeadPEta"), leadPEta); histos.fill(HIST("JetKinematicsQA/hLeadPPhi"), leadPPhi); histos.fill(HIST("JetKinematicsQA/hJetCounterPtLeadP"), leadPPt); + + histos.fill(HIST("JetKinematicsQA/h2dLeadPEtaVsPVz"), leadPEta, collisionPVz); + if (calculateEventMixingHists) + histos.fill(HIST("JetKinematicsQA/h5dLeadPEtaPhiPtPVzCent"), leadPEta, leadPPhi, leadPPt, collisionPVz, centrality); } } @@ -1294,6 +1430,11 @@ struct lambdajetpolarizationionsderived { // Apply distortion logic: const bool hadPreviousProxy = prevJetCache.hadLeadJet; + // TODO: implement event mixing for other proxies. Leading-jet mixing is a separate, independent feature from + // doMixedEventLeadP (RingJets has several rows per collision, unlike RingLeadPs, so it can't just reuse + // the same SameKindPair setup) -- would need its own switch (or we can just add everything to the same switch and rename it), + // its own leading-jet-pt/Zvtx/centrality FlexibleBinningPolicy, and its own SameKindPair + LUT, mirroring the + // leadP one below but sourcing the pt dimension from a per-collision max-pt scan instead of a plain read. applyProxyDistortion(hasValidLeadingJet, leadingJetPt, leadingJetEta, leadingJetPhi, leadingJetUnitVec, minLeadJetPt, prevJetCache.hadLeadJet, prevJetCache.leadJetEta, prevJetCache.leadJetPhi, etaLeadPDist, phiLeadPDist, rng); @@ -1304,6 +1445,10 @@ struct lambdajetpolarizationionsderived { histos.fill(HIST("JetKinematicsQA/hLeadJetEta"), leadingJetEta); histos.fill(HIST("JetKinematicsQA/hLeadJetPhi"), leadingJetPhi); histos.fill(HIST("JetKinematicsQA/hJetCounterPtJet"), leadingJetPt); + + histos.fill(HIST("JetKinematicsQA/h2dLeadJetEtaVsPVz"), leadingJetEta, collisionPVz); + if (calculateEventMixingHists) + histos.fill(HIST("JetKinematicsQA/h5dLeadJetEtaPhiPtPVzCent"), leadingJetEta, leadingJetPhi, leadingJetPt, collisionPVz, centrality); } } @@ -1317,6 +1462,7 @@ struct lambdajetpolarizationionsderived { // Apply distortion logic: const bool hadPreviousProxy = prevJetCache.hadSubJet; + // TODO: implement event mixing for other proxies applyProxyDistortion(hasValidSubJet, subleadingJetPt, subleadingJetEta, subleadingJetPhi, subJetUnitVec, minSubLeadJetPt, prevJetCache.hadSubJet, prevJetCache.subJetEta, prevJetCache.subJetPhi, etaLeadPDist, phiLeadPDist, rng); @@ -1328,6 +1474,10 @@ struct lambdajetpolarizationionsderived { histos.fill(HIST("JetKinematicsQA/hSubLeadJetEta"), subleadingJetEta); histos.fill(HIST("JetKinematicsQA/hSubLeadJetPhi"), subleadingJetPhi); histos.fill(HIST("JetKinematicsQA/hJetCounterPt2ndJet"), subleadingJetPt); + + histos.fill(HIST("JetKinematicsQA/h2dSubLeadJetEtaVsPVz"), subleadingJetEta, collisionPVz); + if (calculateEventMixingHists) + histos.fill(HIST("JetKinematicsQA/h5dSubLeadJetEtaPhiPtPVzCent"), subleadingJetEta, subleadingJetPhi, subleadingJetPt, collisionPVz, centrality); } } @@ -1947,4 +2097,4 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) } // Avoid macro leakage! -#undef APPLY_HISTO_FILL +#undef APPLY_HISTO_FILL \ No newline at end of file From 88d07f0bf2b1648ac7acd1aaef58bb3e18701e12 Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Mon, 17 Aug 2026 13:45:40 +0000 Subject: [PATCH 20/31] Implementing quick QA for event mixing, and saving into a commit the "last neighbour wins" before commiting the uniform randomization via reservoir sampling for leading particles. --- .../lambdaJetPolarizationIonsDerived.cxx | 56 ++++++++++++++----- 1 file changed, 43 insertions(+), 13 deletions(-) diff --git a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx index 39af9617585..ceb48d29ad7 100644 --- a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx @@ -178,7 +178,9 @@ enum CentEstimator { X(FOLDER "/ProxyPtDependence/pRingVsPtLeadPVsEtaLeadP", leadPPt, leadPEta, ringObservableLeadP) \ X(FOLDER "/ProxyPtDependence/pRingVsPtLeadPVsEtaV0", leadPPt, v0eta, ringObservableLeadP) \ X(FOLDER "/EtaDependence/p2dRingObservableEtaLambdaVsEtaLeadP", v0eta, leadPEta, ringObservableLeadP) \ - X(FOLDER "/EtaDependence/h2dCounterEtaLambdaVsEtaLeadP", v0eta, leadPEta) + X(FOLDER "/EtaDependence/h2dCounterEtaLambdaVsEtaLeadP", v0eta, leadPEta) \ + X(FOLDER "/p2dRingObservableLeadPVsPxPy", v0px, v0py, ringObservableLeadP) \ + X(FOLDER "/p2dRingObservableLeadPVsPzPx", v0pz, v0px, ringObservableLeadP) // A macro that encapsulates all eta checks for leading particle and V0s, along with the fills // Parameters: @@ -308,7 +310,6 @@ enum CentEstimator { } struct lambdajetpolarizationionsderived { - // Define histogram registries: HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; @@ -317,7 +318,7 @@ struct lambdajetpolarizationionsderived { Configurable analyseAntiLambda{"analyseAntiLambda", false, "process AntiLambda-like candidates"}; Configurable analyseMagField{"analyseMagField", true, "analyse efficiency effects wrt magnetic field"}; // Older DerivedData lacks magField. "if constexpr (requires { collision.magField(); })" would only see the current header definition, so need a flag for retro-comp. // Configurable doPPAnalysis{"doPPAnalysis", false, "if in pp, set to true. Default is HI"}; - Configurable calculateEventMixingHists{"calculateEventMixingHists", false, "generates expensive THn histograms for a-posteriori event-mixing-like computations."}; + Configurable doJetProxy5dQA{"doJetProxy5dQA", false, "generates expensive THnSparse histograms for joint distribution QA of the jet proxies and collisions"}; // A very inexpensive "signal extraction" imitation based on v0InMassPeak bool: // (Uses a mass interval to remove or include V0s from the final AnalysisResults to take advantage of existing post-processing codes) @@ -530,6 +531,9 @@ struct lambdajetpolarizationionsderived { // =============================== histos.add((folder + "/p2dRingObservableVsPxPy").c_str(), "<#it{R}> vs (p_{x}^{#Lambda},p_{y}^{#Lambda});p_{x}^{#Lambda} (GeV/c);p_{y}^{#Lambda} (GeV/c);<#it{R}>", kTProfile2D, {axisConfigurations.axisLambdaPx, axisConfigurations.axisLambdaPy}); histos.add((folder + "/p2dRingObservableVsPzPx").c_str(), "<#it{R}> vs (p_{z}^{#Lambda},p_{x}^{#Lambda});p_{z}^{#Lambda} (GeV/c);p_{x}^{#Lambda} (GeV/c);<#it{R}>", kTProfile2D, {axisConfigurations.axisLambdaPz, axisConfigurations.axisLambdaPx}); + // For LeadP estimators: + histos.add((folder + "/p2dRingObservableLeadPVsPxPy").c_str(), "<#it{R}>_{LeadP} vs (p_{x}^{#Lambda},p_{y}^{#Lambda});p_{x}^{#Lambda} (GeV/c);p_{y}^{#Lambda} (GeV/c);<#it{R}>", kTProfile2D, {axisConfigurations.axisLambdaPx, axisConfigurations.axisLambdaPy}); + histos.add((folder + "/p2dRingObservableLeadPVsPzPx").c_str(), "<#it{R}>_{LeadP} vs (p_{z}^{#Lambda},p_{x}^{#Lambda});p_{z}^{#Lambda} (GeV/c);p_{x}^{#Lambda} (GeV/c);<#it{R}>", kTProfile2D, {axisConfigurations.axisLambdaPz, axisConfigurations.axisLambdaPx}); // TProfiles with correct error bars:: // -- TProfiles will handle the error estimate of the Ring Observable via the variance, even though @@ -1012,7 +1016,7 @@ struct lambdajetpolarizationionsderived { histos.add("JetKinematicsQA/h2dLeadPEtaVsPVz", "Lead Ptc #eta Vs PVz;#eta;Primary Vertex Z [cm];Counts", kTH2D, {axisConfigurations.axisEta, axisConfigurations.axisPVz}); // For building and event-mixing-like procedure similar to forceDatalikeJet: - if (calculateEventMixingHists) { + if (doJetProxy5dQA) { histos.add("JetKinematicsQA/h5dLeadJetEtaPhiPtPVzCent", "h5dLeadJetEtaPhiPtPVzCent;#eta;#phi;p_{t};Primary Vertex Z [cm];Centrality (%);Counts", kTHnSparseF, {axisConfigurations.axisEtaCoarse, axisConfigurations.axisPhi, axisConfigurations.axisJetPt, axisConfigurations.axisPVz, axisConfigurations.axisCentrality}); histos.add("JetKinematicsQA/h5dSubLeadJetEtaPhiPtPVzCent", "h5dSubLeadJetEtaPhiPtPVzCent;#eta;#phi;p_{t};Primary Vertex Z [cm];Centrality (%);Counts", kTHnSparseF, @@ -1028,6 +1032,14 @@ struct lambdajetpolarizationionsderived { histos.add("JetKinematicsQA/h2dMixedLeadPEtaVsLeadPEta", "MixedLeadP #eta vs LeadP #eta;MixedLeadP #eta; LeadP #eta;Counts", kTH2D, {axisConfigurations.axisEtaCoarse, axisConfigurations.axisEtaCoarse}); histos.add("JetKinematicsQA/h2dMixedLeadPPhiVsLeadPPhi", "MixedLeadP #phi vs LeadP #phi;MixedLeadP #phi; LeadP #phi;Counts", kTH2D, {axisConfigurations.axisPhi, axisConfigurations.axisPhi}); + + // Event mixing source QA -- How repeated is each collision in the mix: + // For each source collision for mixing, counts how many times it was used. Flat distribution is ideal. + histos.add("JetKinematicsQA/hMixedEventLeadPSourceUsageCount", "hMixedEventLeadPSourceUsageCount;Times collision was used;Counts", kTH1D, {{50, -0.5f, 49.5f}}); + // Useful TProfiles -- the mean number of times a given source was used, as a function of eta or phi + // (more convenient than a single TH1, as it gives an average, not the raw counter) + histos.add("JetKinematicsQA/pMixedEventLeadPSourceUsageVsEta", "pMixedEventLeadPSourceUsageVsEta;Source #eta;", kTProfile, {axisConfigurations.axisEtaCoarse}); + histos.add("JetKinematicsQA/pMixedEventLeadPSourceUsageVsPhi", "pMixedEventLeadPSourceUsageVsPhi;Source #varphi;", kTProfile, {axisConfigurations.axisPhi}); } // Fetch the X-axes from one of the families (since they all share the same ConfigurableAxis binning) @@ -1247,7 +1259,7 @@ struct lambdajetpolarizationionsderived { // for (auto const& jet : rows) leadPt = std::max(leadPt, jet.jetPt()); // needs the max-pt scan; RingLeadPs needs no such scan // return leadPt; // }; - struct MixedLeadPInfo { float eta; float phi; }; + struct MixedLeadPInfo { float eta; float phi; int64_t sourceCollisionId; }; std::unordered_map mixedLeadPByCollision; // First we build a lookup table based on current dataframe's collisions (connects pairs of jet proxies from similar collisions): // (these leading particles may come from collisions with no valid Lambdas, by construction) @@ -1278,13 +1290,29 @@ struct lambdajetpolarizationionsderived { for (auto const& lp : leadP1) { eta1 = lp.leadParticleEta(); phi1 = lp.leadParticlePhi(); break; } // Retrieves the first entry for (auto const& lp : leadP2) { eta2 = lp.leadParticleEta(); phi2 = lp.leadParticlePhi(); break; } // Saving the mixed leading particles with a key referring to the two collisions being mixed: - mixedLeadPByCollision[c1.globalIndex()] = {eta2, phi2}; - mixedLeadPByCollision[c2.globalIndex()] = {eta1, phi1}; + mixedLeadPByCollision[c1.globalIndex()] = {eta2, phi2, c2.globalIndex()}; + mixedLeadPByCollision[c2.globalIndex()] = {eta1, phi1, c1.globalIndex()}; } - if (it.isNewWindow()) { // count each bin-window once, not once per pair inside it + if (it.isNewWindow()) { // Count each bin-window once, not once per pair inside it histos.fill(HIST("JetKinematicsQA/hMixedEventLeadPWindowNeighbours"), it.currentWindowNeighbours()); } } + + // Source-usage QA: + std::unordered_map sourceUsageCount; + for (auto const& kv : mixedLeadPByCollision) + sourceUsageCount[kv.second.sourceCollisionId]++; + for (auto const& kv : sourceUsageCount) + histos.fill(HIST("JetKinematicsQA/hMixedEventLeadPSourceUsageCount"), kv.second); + // Eta/phi of a mixed LeadP is stored under the collision that receives the mixing -- reuse the first hit found: + for (auto const& kv : mixedLeadPByCollision) { + auto usageIt = sourceUsageCount.find(kv.second.sourceCollisionId); + if (usageIt == sourceUsageCount.end()) // already consumed below + continue; + histos.fill(HIST("JetKinematicsQA/pMixedEventLeadPSourceUsageVsEta"), kv.second.eta, usageIt->second); + histos.fill(HIST("JetKinematicsQA/pMixedEventLeadPSourceUsageVsPhi"), kv.second.phi, usageIt->second); + sourceUsageCount.erase(usageIt); // Fill this source exactly once, not once per target it supplied + } } for (int idxResampling = 0; idxResampling < fakePolSwitches.nProxyResamples; idxResampling++){ // resampling loop for forceRandJet and forceDatalikeJet @@ -1368,8 +1396,10 @@ struct lambdajetpolarizationionsderived { prevJetCache.leadPPhi = itMix->second.phi; } histos.fill(HIST("JetKinematicsQA/hMixedEventLeadPOutcome"), prevJetCache.hadLeadP ? 1 : 0); - histos.fill(HIST("JetKinematicsQA/h2dMixedLeadPEtaVsLeadPEta"), prevJetCache.leadPEta, leadPEta); - histos.fill(HIST("JetKinematicsQA/h2dMixedLeadPPhiVsLeadPPhi"), prevJetCache.leadPPhi, leadPPhi); + if (prevJetCache.hadLeadP) { // Only fill the comparison histograms on an actual hit: + histos.fill(HIST("JetKinematicsQA/h2dMixedLeadPEtaVsLeadPEta"), prevJetCache.leadPEta, leadPEta); + histos.fill(HIST("JetKinematicsQA/h2dMixedLeadPPhiVsLeadPPhi"), prevJetCache.leadPPhi, leadPPhi); + } } applyProxyDistortion(hasValidLeadingP, leadPPt, leadPEta, leadPPhi, leadPUnitVec, @@ -1384,7 +1414,7 @@ struct lambdajetpolarizationionsderived { histos.fill(HIST("JetKinematicsQA/hJetCounterPtLeadP"), leadPPt); histos.fill(HIST("JetKinematicsQA/h2dLeadPEtaVsPVz"), leadPEta, collisionPVz); - if (calculateEventMixingHists) + if (doJetProxy5dQA) histos.fill(HIST("JetKinematicsQA/h5dLeadPEtaPhiPtPVzCent"), leadPEta, leadPPhi, leadPPt, collisionPVz, centrality); } } @@ -1447,7 +1477,7 @@ struct lambdajetpolarizationionsderived { histos.fill(HIST("JetKinematicsQA/hJetCounterPtJet"), leadingJetPt); histos.fill(HIST("JetKinematicsQA/h2dLeadJetEtaVsPVz"), leadingJetEta, collisionPVz); - if (calculateEventMixingHists) + if (doJetProxy5dQA) histos.fill(HIST("JetKinematicsQA/h5dLeadJetEtaPhiPtPVzCent"), leadingJetEta, leadingJetPhi, leadingJetPt, collisionPVz, centrality); } } @@ -1476,7 +1506,7 @@ struct lambdajetpolarizationionsderived { histos.fill(HIST("JetKinematicsQA/hJetCounterPt2ndJet"), subleadingJetPt); histos.fill(HIST("JetKinematicsQA/h2dSubLeadJetEtaVsPVz"), subleadingJetEta, collisionPVz); - if (calculateEventMixingHists) + if (doJetProxy5dQA) histos.fill(HIST("JetKinematicsQA/h5dSubLeadJetEtaPhiPtPVzCent"), subleadingJetEta, subleadingJetPhi, subleadingJetPt, collisionPVz, centrality); } } From 759302e6824c83b28653e152a3b6c5a999472d8e Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Mon, 17 Aug 2026 13:46:26 +0000 Subject: [PATCH 21/31] Reservoir sampling for event mixing of leading particles (commit preceding major changes for leading jet and subleading jet event mixing, which will change some things in the structure of the code) --- .../Strangeness/lambdaJetPolarizationIons.cxx | 4 +-- .../lambdaJetPolarizationIonsDerived.cxx | 26 +++++++++++++------ 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx b/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx index 7c7312a5ef9..a24a6ae31de 100644 --- a/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx +++ b/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx @@ -774,7 +774,7 @@ struct lambdajetpolarizationions { histos.add("AntiLambda/hAntiLambdasPerEvent", "hAntiLambdasPerEvent;N_{#bar{#Lambda}} per event;Counts", kTH1D, {{15, 0, 15}}); } if (analyseLambda && analyseAntiLambda) { - histos.add("hAmbiguousLambdaCandidates", "hAmbiguousLambdaCandidates;Integrated counts;Counts", kTH1D, {{1, 0, 1}}); + // histos.add("hAmbiguousLambdaCandidates", "hAmbiguousLambdaCandidates;Integrated counts;Counts", kTH1D, {{1, 0, 1}}); // No longer required: v0 selection flow already considers this histos.add("hAmbiguousPerEvent", "hAmbiguousPerEvent;N_{ambiguous} per event;Counts", kTH1D, {{15, 0, 15}}); histos.add("hNonAmbiguousPerEvent", "hNonAmbiguousPerEvent;N_{non-ambiguous} per event;Counts", kTH1D, {{25, 0, 25}}); // To understand the population of correlated Lambda-likes per event histos.add("hLambdasAndAntiLambdasPerEvent", "hLambdasAndAntiLambdasPerEvent;N_{#Lambda}+N_{#bar{#Lambda}} per event;Counts", kTH1D, {{25, 0, 25}}); // Alternative check that shows how bad is the possibly correlated full population (Ambig+NonAmbig) @@ -1896,7 +1896,7 @@ struct lambdajetpolarizationions { // int lambdaIdx = -1; // No need to pass armenteros if (isLambda && isAntiLambda) { NAmbiguous++; - histos.fill(HIST("hAmbiguousLambdaCandidates"), 0); + // histos.fill(HIST("hAmbiguousLambdaCandidates"), 0); if (doArmenterosQA) histos.fill(HIST("GeneralQA/h2dArmenterosFullSelectedAmbiguous"), v0.alpha(), v0.qtarm()); // To know the discerning power of Armenteros in an Ambiguous Lambda vs AntiLambda case diff --git a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx index ceb48d29ad7..547e66bde4f 100644 --- a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx @@ -1281,17 +1281,28 @@ struct lambdajetpolarizationionsderived { SameKindPair mixPair{ mixBinning, fakePolSwitches.mixedEventLeadPWindowSize, -1, collisions, std::make_tuple(leadPs), &mixCache}; - // "Last neighbour wins" if a collision appears in more than one pair within its window -- simple and - // deterministic, not a uniform-random pick among the window's members: + // Uniform random pick among each target's candidate window, via reservoir sampling + // (cannibalization of leading particles by neighbouring collisions in Continuous Readout is not a worry as ITS hits are being demanded) + std::unordered_map mixedLeadPCandidateCount; // how many candidates seen so far, per target + auto reservoirInsert = [this, &mixedLeadPCandidateCount, &mixedLeadPByCollision](int64_t targetId, const MixedLeadPInfo& candidate) { + int& nSeen = mixedLeadPCandidateCount[targetId]; + ++nSeen; + // The n-th candidate seen for a given target replaces the current pick with probability 1/n, so we never need more than one cached candidate: + std::uniform_int_distribution pick(1, nSeen); + if (pick(rng) == 1) // Reuses the same rng member as forceRandJet/forceDatalikeJet, rather than a second generator. + mixedLeadPByCollision[targetId] = candidate; + }; + for (auto it = mixPair.begin(); it != mixPair.end(); ++it) { auto& [c1, leadP1, c2, leadP2] = *it; // Iterates over collision pairs and leading particle pairs (structured binding) if (leadP1.size() > 0 && leadP2.size() > 0) { // There should always be at least one leadP, given the overflow exclusion above float eta1 = 0.f, phi1 = 0.f, eta2 = 0.f, phi2 = 0.f; for (auto const& lp : leadP1) { eta1 = lp.leadParticleEta(); phi1 = lp.leadParticlePhi(); break; } // Retrieves the first entry for (auto const& lp : leadP2) { eta2 = lp.leadParticleEta(); phi2 = lp.leadParticlePhi(); break; } - // Saving the mixed leading particles with a key referring to the two collisions being mixed: - mixedLeadPByCollision[c1.globalIndex()] = {eta2, phi2, c2.globalIndex()}; - mixedLeadPByCollision[c2.globalIndex()] = {eta1, phi1, c1.globalIndex()}; + // Each side of the pair is one more candidate for the other collision's reservoir: + // (after that, saves the mixed leading particles with a key referring to the two collisions being mixed) + reservoirInsert(c1.globalIndex(), {eta2, phi2, c2.globalIndex()}); + reservoirInsert(c2.globalIndex(), {eta1, phi1, c1.globalIndex()}); } if (it.isNewWindow()) { // Count each bin-window once, not once per pair inside it histos.fill(HIST("JetKinematicsQA/hMixedEventLeadPWindowNeighbours"), it.currentWindowNeighbours()); @@ -1359,9 +1370,8 @@ struct lambdajetpolarizationionsderived { float leadPEta = 0.; float leadPPhi = 0.; float leadPPx = 0., leadPPy = 0., leadPPz = 0.; - for (auto const& lp : leadPsInColl) { - // Table should contain exactly one entry per collision, - // but we break immediately to be safe: + for (auto const& lp : leadPsInColl) { // + // Table should contain exactly one entry per collision, but we break immediately to be safe: leadPPt = lp.leadParticlePt(); leadPEta = lp.leadParticleEta(); leadPPhi = lp.leadParticlePhi(); From f768c44bce471f39563c6b88067409b5015dc067 Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Mon, 17 Aug 2026 21:43:15 +0000 Subject: [PATCH 22/31] Implementing event mixing for FastJet leading jets and Subleading jets. Some refactoring of leading particle code in order to avoid repetition and to preserve performance. --- .../lambdaJetPolarizationIonsDerived.cxx | 370 +++++++++++++----- 1 file changed, 271 insertions(+), 99 deletions(-) diff --git a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx index 547e66bde4f..d860bcf93ec 100644 --- a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx @@ -349,8 +349,8 @@ struct lambdajetpolarizationionsderived { Configurable forceRandJet{"forceRandJet", false, "makes jet direction random. A QA for AEE fake signal and its removal"}; Configurable forcePreviousJet{"forcePreviousJet", false, "uses previous event's jet direction instead of a random sample. A baseline for fake signal removal"}; Configurable forceDatalikeJet{"forceDatalikeJet", false, "a compromise between forceRandJet and forcePreviousJet. Parameterized distribution from data"}; - Configurable doMixedEventLeadP{"doMixedEventLeadP", false, "mix leading particle direction between events using (LeadP pt, Zvtx, centrality) bins"}; - Configurable mixedEventLeadPWindowSize{"mixedEventLeadPWindowSize", 10, "number of neighbours for doMixedEventLeadP: how many similar collisions stay eligible as mixing partners at once."}; + Configurable doMixedEventProxies{"doMixedEventProxies", false, "mix leadP/leadJet/subJet directions between events using (proxy pt, Zvtx, centrality) bins -- three independent mixings, one per proxy"}; + Configurable mixedEventWindowSize{"mixedEventWindowSize", 10, "number of neighbours for doMixedEventProxies: how many similar collisions stay eligible as mixing partners at once (shared by all 3 proxies)."}; Configurable nProxyResamples{"nProxyResamples", 1, "The amount of resamplings of jet direction per event. Use ONLY for forceRandJet and forceDatalikeJet"}; } fakePolSwitches; @@ -1025,21 +1025,37 @@ struct lambdajetpolarizationionsderived { {axisConfigurations.axisEtaCoarse, axisConfigurations.axisPhi, axisConfigurations.axisJetPt, axisConfigurations.axisPVz, axisConfigurations.axisCentrality}); } - // doMixedEventLeadP QA: gauge the size of the "too few collisions per bin" problem (see resonanceMergeDF.cxx) - if (fakePolSwitches.doMixedEventLeadP) { + // doMixedEventProxies QA: gauge the size of the "too few collisions per bin" problem (see resonanceMergeDF.cxx) + // Booked per proxy, since the three mixings are independent and can succeed/fail at different rates. + if (fakePolSwitches.doMixedEventProxies) { histos.add("JetKinematicsQA/hMixedEventLeadPOutcome", "hMixedEventLeadPOutcome;Outcome (0=skipped, 1=found);Counts", kTH1D, {{2, -0.5f, 1.5f}}); + histos.add("JetKinematicsQA/hMixedEventLeadJetOutcome", "hMixedEventLeadJetOutcome;Outcome (0=skipped, 1=found);Counts", kTH1D, {{2, -0.5f, 1.5f}}); + histos.add("JetKinematicsQA/hMixedEventSubJetOutcome", "hMixedEventSubJetOutcome;Outcome (0=skipped, 1=found);Counts", kTH1D, {{2, -0.5f, 1.5f}}); + histos.add("JetKinematicsQA/hMixedEventLeadPWindowNeighbours", "hMixedEventLeadPWindowNeighbours;Neighbours found in bin window;Counts", kTH1D, {{22, -0.5f, 21.5f}}); + histos.add("JetKinematicsQA/hMixedEventLeadJetWindowNeighbours", "hMixedEventLeadJetWindowNeighbours;Neighbours found in bin window;Counts", kTH1D, {{22, -0.5f, 21.5f}}); + histos.add("JetKinematicsQA/hMixedEventSubJetWindowNeighbours", "hMixedEventSubJetWindowNeighbours;Neighbours found in bin window;Counts", kTH1D, {{22, -0.5f, 21.5f}}); histos.add("JetKinematicsQA/h2dMixedLeadPEtaVsLeadPEta", "MixedLeadP #eta vs LeadP #eta;MixedLeadP #eta; LeadP #eta;Counts", kTH2D, {axisConfigurations.axisEtaCoarse, axisConfigurations.axisEtaCoarse}); histos.add("JetKinematicsQA/h2dMixedLeadPPhiVsLeadPPhi", "MixedLeadP #phi vs LeadP #phi;MixedLeadP #phi; LeadP #phi;Counts", kTH2D, {axisConfigurations.axisPhi, axisConfigurations.axisPhi}); + histos.add("JetKinematicsQA/h2dMixedLeadJetEtaVsLeadJetEta", "MixedLeadJet #eta vs LeadJet #eta;MixedLeadJet #eta; LeadJet #eta;Counts", kTH2D, {axisConfigurations.axisEtaCoarse, axisConfigurations.axisEtaCoarse}); + histos.add("JetKinematicsQA/h2dMixedLeadJetPhiVsLeadJetPhi", "MixedLeadJet #phi vs LeadJet #phi;MixedLeadJet #phi; LeadJet #phi;Counts", kTH2D, {axisConfigurations.axisPhi, axisConfigurations.axisPhi}); + histos.add("JetKinematicsQA/h2dMixedSubJetEtaVsSubJetEta", "MixedSubJet #eta vs SubJet #eta;MixedSubJet #eta; SubJet #eta;Counts", kTH2D, {axisConfigurations.axisEtaCoarse, axisConfigurations.axisEtaCoarse}); + histos.add("JetKinematicsQA/h2dMixedSubJetPhiVsSubJetPhi", "MixedSubJet #phi vs SubJet #phi;MixedSubJet #phi; SubJet #phi;Counts", kTH2D, {axisConfigurations.axisPhi, axisConfigurations.axisPhi}); // Event mixing source QA -- How repeated is each collision in the mix: // For each source collision for mixing, counts how many times it was used. Flat distribution is ideal. histos.add("JetKinematicsQA/hMixedEventLeadPSourceUsageCount", "hMixedEventLeadPSourceUsageCount;Times collision was used;Counts", kTH1D, {{50, -0.5f, 49.5f}}); + histos.add("JetKinematicsQA/hMixedEventLeadJetSourceUsageCount", "hMixedEventLeadJetSourceUsageCount;Times collision was used;Counts", kTH1D, {{50, -0.5f, 49.5f}}); + histos.add("JetKinematicsQA/hMixedEventSubJetSourceUsageCount", "hMixedEventSubJetSourceUsageCount;Times collision was used;Counts", kTH1D, {{50, -0.5f, 49.5f}}); // Useful TProfiles -- the mean number of times a given source was used, as a function of eta or phi // (more convenient than a single TH1, as it gives an average, not the raw counter) histos.add("JetKinematicsQA/pMixedEventLeadPSourceUsageVsEta", "pMixedEventLeadPSourceUsageVsEta;Source #eta;", kTProfile, {axisConfigurations.axisEtaCoarse}); histos.add("JetKinematicsQA/pMixedEventLeadPSourceUsageVsPhi", "pMixedEventLeadPSourceUsageVsPhi;Source #varphi;", kTProfile, {axisConfigurations.axisPhi}); + histos.add("JetKinematicsQA/pMixedEventLeadJetSourceUsageVsEta", "pMixedEventLeadJetSourceUsageVsEta;Source #eta;", kTProfile, {axisConfigurations.axisEtaCoarse}); + histos.add("JetKinematicsQA/pMixedEventLeadJetSourceUsageVsPhi", "pMixedEventLeadJetSourceUsageVsPhi;Source #varphi;", kTProfile, {axisConfigurations.axisPhi}); + histos.add("JetKinematicsQA/pMixedEventSubJetSourceUsageVsEta", "pMixedEventSubJetSourceUsageVsEta;Source #eta;", kTProfile, {axisConfigurations.axisEtaCoarse}); + histos.add("JetKinematicsQA/pMixedEventSubJetSourceUsageVsPhi", "pMixedEventSubJetSourceUsageVsPhi;Source #varphi;", kTProfile, {axisConfigurations.axisPhi}); } // Fetch the X-axes from one of the families (since they all share the same ConfigurableAxis binning) @@ -1078,16 +1094,17 @@ struct lambdajetpolarizationionsderived { /// \param hasValidProxy in/out (edited in-place): whether the proxy being distorted is usable. Re-evaluated against minPtThreshold after distortion. /// \param pt,eta,phi in/out (edited in-place): the proxy's kinematics, overwritten by whichever distortion is active. /// \param unitVec in/out (edited in-place): the proxy direction as a unit vector. - /// \param cacheHadProxy,cacheEta,cachePhi caches for either forcePreviousJet or doMixedEventLeadP. + /// \param cacheHadProxy,cacheEta,cachePhi caches for either forcePreviousJet or doMixedEventProxies. // forcePreviousJet updates these here for the next collision, in a simplistic event event mixing approach. - // doMixedEventLeadP instead overwrites prevJetCache's fields with this collision's mixed proxy right before the call. + // doMixedEventProxies instead overwrites the caller's cache fields with this collision's mixed proxy right before the call. /// Fallback skips this event using hasValidProxy. /// \param etaDist,phiDist,rng sampling distributions/generator for forceRandJet and forceDatalikeJet. + /// \note Shared across leadP/leadJet/subJet: the caller resolves which proxy-specific procedure (e.g., LUT for evtMixing) applies. // Helper to modify the jet direction for QA and for spurious signal baseline removal tests: inline void applyProxyDistortion(bool& hasValidProxy, float& pt, float& eta, float& phi, XYZVector& unitVec, float minPtThreshold, bool& cacheHadProxy, float& cacheEta, float& cachePhi, std::discrete_distribution& etaDist, std::discrete_distribution& phiDist, std::mt19937& rng){ - if (!fakePolSwitches.forcePerpToJet && !fakePolSwitches.forceJetDirectionSmudge && !fakePolSwitches.forceRandJet && !fakePolSwitches.forcePreviousJet && !fakePolSwitches.forceDatalikeJet && !fakePolSwitches.doMixedEventLeadP)[[likely]] { + if (!fakePolSwitches.forcePerpToJet && !fakePolSwitches.forceJetDirectionSmudge && !fakePolSwitches.forceRandJet && !fakePolSwitches.forcePreviousJet && !fakePolSwitches.forceDatalikeJet && !fakePolSwitches.doMixedEventProxies)[[likely]] { return; // Skip this function if none of the modifications are actually being executed! } @@ -1184,8 +1201,8 @@ struct lambdajetpolarizationionsderived { const double sinPhi = std::sin(phi); const double cosPhi = std::cos(phi); unitVec = XYZVector(cosPhi * inverseCoshEta, sinPhi * inverseCoshEta, std::tanh(eta)); - } else if (fakePolSwitches.doMixedEventLeadP) { - // Mixes the leadP direction from a real, similar other collision, rather than sampling a fitted distribution as datalikeJet. + } else if (fakePolSwitches.doMixedEventProxies) { + // Mixes this proxy's direction from a real, similar other collision, rather than sampling a fitted distribution as datalikeJet. // Only unitVec is rebuilt, eta/phi are left as this collision's own real values -- same convention forcePreviousJet uses above. if (cacheHadProxy) { const double inverseCoshEta = 1.0 / std::cosh(cacheEta); @@ -1233,96 +1250,246 @@ struct lambdajetpolarizationionsderived { bool hadSubJet; float subJetEta; float subJetPhi; // Subleading jet bool hadLeadP; float leadPEta; float leadPPhi; // Leading particle }; + + // Per-collision leading/subleading jet, computed once per dataframe and shared by both the main loop below + // (instead of re-scanning RingJets once per resampling pass) and the leadJet/subJet event mixing functions: + struct JetProxyCache { + bool hasValidLeadingJet = false; float leadingJetPt = -1.f; float leadingJetEta = 0.f; float leadingJetPhi = 0.f; + bool hasValidSubJet = false; float subleadingJetPt = -1.f; float subleadingJetEta = 0.f; float subleadingJetPhi = 0.f; + }; + + // A simple struct for doMixedEventProxies. Each proxy (leadP/leadJet/subJet) gets its own independent cache. + struct MixedProxyInfo { float eta; float phi; int64_t sourceCollisionId; }; + + /// \brief Uniform random pick among each target's candidate window, via reservoir sampling + /// (cannibalization of proxies by neighbouring collisions in Continuous Readout is not a worry as ITS hits are being demanded) + /// \note Reuses the task's own rng member rather than a separate generator per proxy. + void reservoirInsert(std::unordered_map& candidateCount, std::unordered_map& lut, + int64_t targetId, const MixedProxyInfo& candidate) + { + int& nSeen = candidateCount[targetId]; + ++nSeen; + std::uniform_int_distribution pick(1, nSeen); + if (pick(rng) == 1) { + lut[targetId] = candidate; + } + } + + /// \brief How many different target collisions each source collision ended up supplying, from a finished LUT. + static std::unordered_map tallySourceUsage(std::unordered_map const& lut) + { + std::unordered_map usage; + for (auto const& kv : lut) + usage[kv.second.sourceCollisionId]++; + return usage; + } // Preslices for correct collisions association: // (TODO: test using custom grouping) Preslice perColJets = o2::aod::lambdajetpol::ringCollisionId; Preslice perColV0s = o2::aod::lambdajetpol::ringCollisionId; Preslice perColLeadPs = o2::aod::lambdajetpol::ringCollisionId; - // For doMixedEventLeadP: + // For doMixedEventProxies: SliceCache mixCache; /// \brief Main analysis loop: for each collision, rebuilds the leading jet/particle proxies (with optional fakePolSwitches distortions), then loops over V0s computing the ring observable and polarization-vector profiles for every enabled kinematic-cut family. void processPolarizationData(o2::aod::RingCollisions const& collisions, o2::aod::RingJets const& jets, o2::aod::RingLaV0s const& v0s, o2::aod::RingLeadPs const& leadPs) { - // Event mixing, built once per dataframe // Simplistic event mixing using previous collision: PrevJetCache prevJetCache{}; // Initializing struct before the collisions loop. This zero-initializes, so bools start as false // This should not be used along nProxyResamples > 1, as it does not apply to that case. - // doMixedEventLeadP: Binning on (Zvtx, leadP pt, centrality) lets eta/phi vary freely. - // Leading/subleading jet mixing are a separate, independent feature NOT implemented yet. Rough sketch of the difference, for later: - // auto getMixJetPt = [&jets, &mixCache](aod::RingCollision const& col) { - // auto rows = jets.sliceByCached(o2::aod::lambdajetpol::ringCollisionId, col.globalIndex(), mixCache); - // float leadPt = -999.f; - // for (auto const& jet : rows) leadPt = std::max(leadPt, jet.jetPt()); // needs the max-pt scan; RingLeadPs needs no such scan - // return leadPt; - // }; - struct MixedLeadPInfo { float eta; float phi; int64_t sourceCollisionId; }; - std::unordered_map mixedLeadPByCollision; - // First we build a lookup table based on current dataframe's collisions (connects pairs of jet proxies from similar collisions): - // (these leading particles may come from collisions with no valid Lambdas, by construction) - if (fakePolSwitches.doMixedEventLeadP) { + // Leading/subleading jet per collision, resolved once here instead of inside the (possibly nProxyResamples times resampled) main loop. + // Both the main loop and the leadJet/subJet mixing functions read from this, so RingJets is scanned exactly once + std::unordered_map jetProxyByCollision; + for (auto const& collision : collisions) { + const float collisionPVz = collision.zvtx(); + if (std::abs(collisionPVz) > maxZVtxPosition) // Additional Zvtx filtering + continue; + + const auto collId = collision.globalIndex(); + JetProxyCache cache; + // std::optional avoids undefined behaviour from a default-constructed iterator: + std::optional leadingJet; + std::optional subleadingJet; + for (auto const& jet : jets.sliceBy(perColJets, collId)) { + const auto jetpt = jet.jetPt(); + if (jetpt > cache.leadingJetPt) { + // Current leading becomes subleading: + cache.subleadingJetPt = cache.leadingJetPt; + subleadingJet = leadingJet; // may still be std::nullopt on first pass -- that is fine! + // Now update the leading jet: + cache.leadingJetPt = jetpt; + leadingJet = jet; + } else if (jetpt > cache.subleadingJetPt) { // Update subleading only: + cache.subleadingJetPt = jetpt; + subleadingJet = jet; + } + } + // Finer control on jet momentum, further than TableProducer pre-selection: + cache.hasValidLeadingJet = cache.leadingJetPt > minLeadJetPt; + cache.hasValidSubJet = cache.subleadingJetPt > minSubLeadJetPt; + if (cache.hasValidLeadingJet) { + cache.leadingJetEta = leadingJet->jetEta(); + cache.leadingJetPhi = leadingJet->jetPhi(); + } + if (cache.hasValidSubJet) { + cache.subleadingJetEta = subleadingJet->jetEta(); + cache.subleadingJetPhi = subleadingJet->jetPhi(); + } + jetProxyByCollision[collId] = cache; + } + + // doMixedEventProxies caches: three independent mixings, one per proxy (a collision may have a valid LeadP, but no SubLeadJet). + // Mixing is binned on (Zvtx, proxy pt, centrality), varying only eta/phi. + std::unordered_map mixedLeadPByCollision; + std::unordered_map mixedLeadJetByCollision; + std::unordered_map mixedSubJetByCollision; + // First we build lookup tables based on current dataframe's collisions (connects pairs of jet proxies from similar collisions): + // (these proxies may come from collisions with no valid Lambdas, by construction, enabling more mixes) + if (fakePolSwitches.doMixedEventProxies) { + auto getMixCentrality = [this](o2::aod::RingCollision const& col) { return this->getCentrality(col); }; + + // For the leading particle: // Pattern follows MixedEventsLambdaBinning tutorial (captures leadPs table and cache define in task's struct): auto getMixLeadPPt = [&leadPs, this](o2::aod::RingCollision const& col) { auto rows = leadPs.sliceByCached(o2::aod::lambdajetpol::ringCollisionId, col.globalIndex(), this->mixCache); // As it is cached, grouping happens only once return rows.size() > 0 ? rows.begin().leadParticlePt() : -999.f; }; - auto getMixCentrality = [this](o2::aod::RingCollision const& col) { return this->getCentrality(col); }; - - using MixBinningType = FlexibleBinningPolicy, - o2::aod::lambdajetpol::Zvtx, decltype(getMixLeadPPt), decltype(getMixCentrality)>; - MixBinningType mixBinning{{getMixLeadPPt, getMixCentrality}, - {axisConfigurations.axisPVz, axisConfigurations.axisJetPt, axisConfigurations.axisCentrality}, - true}; // Ignore overflows true + 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 // SameKindPair defaults to CombinationsBlockStrictlyUpperSameIndexPolicy, so no same-event mixing should happen: - SameKindPair mixPair{ - mixBinning, fakePolSwitches.mixedEventLeadPWindowSize, -1, collisions, std::make_tuple(leadPs), &mixCache}; - - // Uniform random pick among each target's candidate window, via reservoir sampling - // (cannibalization of leading particles by neighbouring collisions in Continuous Readout is not a worry as ITS hits are being demanded) - std::unordered_map mixedLeadPCandidateCount; // how many candidates seen so far, per target - auto reservoirInsert = [this, &mixedLeadPCandidateCount, &mixedLeadPByCollision](int64_t targetId, const MixedLeadPInfo& candidate) { - int& nSeen = mixedLeadPCandidateCount[targetId]; - ++nSeen; - // The n-th candidate seen for a given target replaces the current pick with probability 1/n, so we never need more than one cached candidate: - std::uniform_int_distribution pick(1, nSeen); - if (pick(rng) == 1) // Reuses the same rng member as forceRandJet/forceDatalikeJet, rather than a second generator. - mixedLeadPByCollision[targetId] = candidate; - }; + SameKindPair leadPPair{ + leadPBinning, fakePolSwitches.mixedEventWindowSize, -1, collisions, std::make_tuple(leadPs), &mixCache}; - for (auto it = mixPair.begin(); it != mixPair.end(); ++it) { + std::unordered_map leadPCandidateCount; + for (auto it = leadPPair.begin(); it != leadPPair.end(); ++it) { auto& [c1, leadP1, c2, leadP2] = *it; // Iterates over collision pairs and leading particle pairs (structured binding) if (leadP1.size() > 0 && leadP2.size() > 0) { // There should always be at least one leadP, given the overflow exclusion above float eta1 = 0.f, phi1 = 0.f, eta2 = 0.f, phi2 = 0.f; for (auto const& lp : leadP1) { eta1 = lp.leadParticleEta(); phi1 = lp.leadParticlePhi(); break; } // Retrieves the first entry for (auto const& lp : leadP2) { eta2 = lp.leadParticleEta(); phi2 = lp.leadParticlePhi(); break; } // Each side of the pair is one more candidate for the other collision's reservoir: - // (after that, saves the mixed leading particles with a key referring to the two collisions being mixed) - reservoirInsert(c1.globalIndex(), {eta2, phi2, c2.globalIndex()}); - reservoirInsert(c2.globalIndex(), {eta1, phi1, c1.globalIndex()}); + reservoirInsert(leadPCandidateCount, mixedLeadPByCollision, c1.globalIndex(), {eta2, phi2, c2.globalIndex()}); + reservoirInsert(leadPCandidateCount, mixedLeadPByCollision, c2.globalIndex(), {eta1, phi1, c1.globalIndex()}); } if (it.isNewWindow()) { // Count each bin-window once, not once per pair inside it histos.fill(HIST("JetKinematicsQA/hMixedEventLeadPWindowNeighbours"), it.currentWindowNeighbours()); } } + // Leading jet: + // pt comes from the jetProxyByCollision pre-pass above. + // (Collisions whose leading jet failed minLeadJetPt get the -999 sentinel, so they never enter this mixing) + auto getMixLeadJetPt = [this, &jetProxyByCollision](o2::aod::RingCollision const& col) { + auto cacheIt = jetProxyByCollision.find(col.globalIndex()); + if (cacheIt == jetProxyByCollision.end() || !cacheIt->second.hasValidLeadingJet) + return -999.f; + return cacheIt->second.leadingJetPt; + }; + using LeadJetBinningType = FlexibleBinningPolicy, + o2::aod::lambdajetpol::Zvtx, decltype(getMixLeadJetPt), decltype(getMixCentrality)>; + LeadJetBinningType leadJetBinning{{getMixLeadJetPt, getMixCentrality}, + {axisConfigurations.axisPVz, axisConfigurations.axisJetPt, axisConfigurations.axisCentrality}, + true}; + + // 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. + SameKindPair leadJetPair{ + leadJetBinning, fakePolSwitches.mixedEventWindowSize, -1, collisions, std::make_tuple(jets), &mixCache}; + + std::unordered_map leadJetCandidateCount; + for (auto it = leadJetPair.begin(); it != leadJetPair.end(); ++it) { + auto& [c1, jets1, c2, jets2] = *it; // jets1/jets2 intentionally unused: the leading/subleading jet is already resolved in jetProxyByCollision + auto cachedLeadJet1 = jetProxyByCollision.find(c1.globalIndex()); + auto cachedLeadJet2 = jetProxyByCollision.find(c2.globalIndex()); + if (cachedLeadJet1 != jetProxyByCollision.end() && cachedLeadJet2 != jetProxyByCollision.end() && + cachedLeadJet1->second.hasValidLeadingJet && cachedLeadJet2->second.hasValidLeadingJet) { + reservoirInsert(leadJetCandidateCount, mixedLeadJetByCollision, c1.globalIndex(), + {cachedLeadJet2->second.leadingJetEta, cachedLeadJet2->second.leadingJetPhi, c2.globalIndex()}); + reservoirInsert(leadJetCandidateCount, mixedLeadJetByCollision, c2.globalIndex(), + {cachedLeadJet1->second.leadingJetEta, cachedLeadJet1->second.leadingJetPhi, c1.globalIndex()}); + } + if (it.isNewWindow()) { + histos.fill(HIST("JetKinematicsQA/hMixedEventLeadJetWindowNeighbours"), it.currentWindowNeighbours()); + } + } + + // Subleading jet: + auto getMixSubJetPt = [this, &jetProxyByCollision](o2::aod::RingCollision const& col) { + auto cacheIt = jetProxyByCollision.find(col.globalIndex()); + if (cacheIt == jetProxyByCollision.end() || !cacheIt->second.hasValidSubJet) + return -999.f; + return cacheIt->second.subleadingJetPt; + }; + using SubJetBinningType = FlexibleBinningPolicy, + o2::aod::lambdajetpol::Zvtx, decltype(getMixSubJetPt), decltype(getMixCentrality)>; + SubJetBinningType subJetBinning{{getMixSubJetPt, getMixCentrality}, + {axisConfigurations.axisPVz, axisConfigurations.axisJetPt, axisConfigurations.axisCentrality}, + true}; + + SameKindPair subJetPair{ + subJetBinning, fakePolSwitches.mixedEventWindowSize, -1, collisions, std::make_tuple(jets), &mixCache}; + + std::unordered_map subJetCandidateCount; + for (auto it = subJetPair.begin(); it != subJetPair.end(); ++it) { + auto& [c1, jets1, c2, jets2] = *it; // jets1/jets2 intentionally unused: the leading/subleading jet is already resolved in jetProxyByCollision + auto cachedSubJet1 = jetProxyByCollision.find(c1.globalIndex()); + auto cachedSubJet2 = jetProxyByCollision.find(c2.globalIndex()); + if (cachedSubJet1 != jetProxyByCollision.end() && cachedSubJet2 != jetProxyByCollision.end() && + cachedSubJet1->second.hasValidSubJet && cachedSubJet2->second.hasValidSubJet) { + reservoirInsert(subJetCandidateCount, mixedSubJetByCollision, c1.globalIndex(), + {cachedSubJet2->second.subleadingJetEta, cachedSubJet2->second.subleadingJetPhi, c2.globalIndex()}); + reservoirInsert(subJetCandidateCount, mixedSubJetByCollision, c2.globalIndex(), + {cachedSubJet1->second.subleadingJetEta, cachedSubJet1->second.subleadingJetPhi, c1.globalIndex()}); + } + if (it.isNewWindow()) { + histos.fill(HIST("JetKinematicsQA/hMixedEventSubJetWindowNeighbours"), it.currentWindowNeighbours()); + } + } + // Source-usage QA: - std::unordered_map sourceUsageCount; - for (auto const& kv : mixedLeadPByCollision) - sourceUsageCount[kv.second.sourceCollisionId]++; - for (auto const& kv : sourceUsageCount) + // (Per-proxy (HIST() needs literal names, so the fills stay unrolled here) + auto leadPUsage = tallySourceUsage(mixedLeadPByCollision); + for (auto const& kv : leadPUsage) histos.fill(HIST("JetKinematicsQA/hMixedEventLeadPSourceUsageCount"), kv.second); - // Eta/phi of a mixed LeadP is stored under the collision that receives the mixing -- reuse the first hit found: + // Eta/phi of a mixed proxy is stored under the collision that receives the mixing -- reuse the first hit found: for (auto const& kv : mixedLeadPByCollision) { - auto usageIt = sourceUsageCount.find(kv.second.sourceCollisionId); - if (usageIt == sourceUsageCount.end()) // already consumed below + auto usageIt = leadPUsage.find(kv.second.sourceCollisionId); + if (usageIt == leadPUsage.end()) // already consumed below continue; histos.fill(HIST("JetKinematicsQA/pMixedEventLeadPSourceUsageVsEta"), kv.second.eta, usageIt->second); histos.fill(HIST("JetKinematicsQA/pMixedEventLeadPSourceUsageVsPhi"), kv.second.phi, usageIt->second); - sourceUsageCount.erase(usageIt); // Fill this source exactly once, not once per target it supplied + leadPUsage.erase(usageIt); // Fill this source exactly once, not once per target it supplied + } + + auto leadJetUsage = tallySourceUsage(mixedLeadJetByCollision); + for (auto const& kv : leadJetUsage) + histos.fill(HIST("JetKinematicsQA/hMixedEventLeadJetSourceUsageCount"), kv.second); + for (auto const& kv : mixedLeadJetByCollision) { + auto usageIt = leadJetUsage.find(kv.second.sourceCollisionId); + if (usageIt == leadJetUsage.end()) + continue; + histos.fill(HIST("JetKinematicsQA/pMixedEventLeadJetSourceUsageVsEta"), kv.second.eta, usageIt->second); + histos.fill(HIST("JetKinematicsQA/pMixedEventLeadJetSourceUsageVsPhi"), kv.second.phi, usageIt->second); + leadJetUsage.erase(usageIt); + } + + auto subJetUsage = tallySourceUsage(mixedSubJetByCollision); + for (auto const& kv : subJetUsage) + histos.fill(HIST("JetKinematicsQA/hMixedEventSubJetSourceUsageCount"), kv.second); + for (auto const& kv : mixedSubJetByCollision) { + auto usageIt = subJetUsage.find(kv.second.sourceCollisionId); + if (usageIt == subJetUsage.end()) + continue; + histos.fill(HIST("JetKinematicsQA/pMixedEventSubJetSourceUsageVsEta"), kv.second.eta, usageIt->second); + histos.fill(HIST("JetKinematicsQA/pMixedEventSubJetSourceUsageVsPhi"), kv.second.phi, usageIt->second); + subJetUsage.erase(usageIt); } } @@ -1370,7 +1537,7 @@ struct lambdajetpolarizationionsderived { float leadPEta = 0.; float leadPPhi = 0.; float leadPPx = 0., leadPPy = 0., leadPPz = 0.; - for (auto const& lp : leadPsInColl) { // + for (auto const& lp : leadPsInColl) { // Table should contain exactly one entry per collision, but we break immediately to be safe: leadPPt = lp.leadParticlePt(); leadPEta = lp.leadParticleEta(); @@ -1396,9 +1563,9 @@ struct lambdajetpolarizationionsderived { // Apply distortion logic: const bool hadPreviousProxy = prevJetCache.hadLeadP; - // doMixedEventLeadP: get this collision's mixing partner (if any) from the LUT built above: + // doMixedEventProxies: get this collision's mixing partner (if any) from the LUT built above: // (this check is performed only if hasValidLeadingP is true for performance, as the LeadPPt matching for the event mixing would also demand a minimum jet pT) - if (fakePolSwitches.doMixedEventLeadP) { + if (fakePolSwitches.doMixedEventProxies) { auto itMix = mixedLeadPByCollision.find(collId); prevJetCache.hadLeadP = (itMix != mixedLeadPByCollision.end()); // Check if this event had a valid mixing target. Reusing prevJetCache if (prevJetCache.hadLeadP) { @@ -1429,33 +1596,14 @@ struct lambdajetpolarizationionsderived { } } - // 3) Checking if the event has a leading jet: - auto jetsInColl = jets.sliceBy(perColJets, collId); - float leadingJetPt = -1.; - float subleadingJetPt = -1.; - // std::optional avoids undefined behaviour from a default-constructed iterator: - // (essentially, just protection for when we fetch jetEta() and the such) - std::optional leadingJet; - std::optional subleadingJet; - for (auto const& jet : jetsInColl) { - const auto jetpt = jet.jetPt(); - if (jetpt > leadingJetPt) { - // Current leading becomes subleading: - subleadingJetPt = leadingJetPt; - subleadingJet = leadingJet; // may still be std::nullopt on first pass -- that is fine! - // Now update the leading jet: - leadingJetPt = jetpt; - leadingJet = jet; - } else if (jetpt > subleadingJetPt) { // Update subleading only: - subleadingJetPt = jetpt; - subleadingJet = jet; - } - } + // 3) Fetching leading jet and subleading jet -- Resolved once per collision in the jetProxyByCollision pre-pass: + const JetProxyCache& jetProxies = jetProxyByCollision[collId]; + float leadingJetPt = jetProxies.leadingJetPt; + float subleadingJetPt = jetProxies.subleadingJetPt; - // Some useful bools to check if we have a leading jet and a subleading jet: - // const bool hasValidLeadingJet = leadingJetPt > 0.; - bool hasValidLeadingJet = leadingJetPt > minLeadJetPt; // Finer control on jet momentum - bool hasValidSubJet = subleadingJetPt > minSubLeadJetPt; + // Defining local bools that may be changed by applyProxyDistortion: + bool hasValidLeadingJet = jetProxies.hasValidLeadingJet; + bool hasValidSubJet = jetProxies.hasValidSubJet; // Build jet vectors (only when the corresponding jet exists): // Dummy initialisations are safe: all jet-dependent fills are gated on hasValidLeadingJet / hasValidSubJet. @@ -1463,18 +1611,28 @@ struct lambdajetpolarizationionsderived { float leadingJetPhi = 0.; XYZVector leadingJetUnitVec(1., 0., 0.); // dummy (overwritten below) if (hasValidLeadingJet) { - leadingJetEta = leadingJet->jetEta(); - leadingJetPhi = leadingJet->jetPhi(); - // Using internal getters to make code cleaner: - leadingJetUnitVec = XYZVector(leadingJet->jetPx(), leadingJet->jetPy(), leadingJet->jetPz()).Unit(); + leadingJetEta = jetProxies.leadingJetEta; + leadingJetPhi = jetProxies.leadingJetPhi; + // Rebuild the direction from the cached eta/phi (cheaper than calling jetPx() internal getters and then normalizing with .Unit()): + const double inverseCoshEta = 1.0 / std::cosh(leadingJetEta); + leadingJetUnitVec = XYZVector(std::cos(leadingJetPhi) * inverseCoshEta, std::sin(leadingJetPhi) * inverseCoshEta, std::tanh(leadingJetEta)); // Apply distortion logic: const bool hadPreviousProxy = prevJetCache.hadLeadJet; - // TODO: implement event mixing for other proxies. Leading-jet mixing is a separate, independent feature from - // doMixedEventLeadP (RingJets has several rows per collision, unlike RingLeadPs, so it can't just reuse - // the same SameKindPair setup) -- would need its own switch (or we can just add everything to the same switch and rename it), - // its own leading-jet-pt/Zvtx/centrality FlexibleBinningPolicy, and its own SameKindPair + LUT, mirroring the - // leadP one below but sourcing the pt dimension from a per-collision max-pt scan instead of a plain read. + if (fakePolSwitches.doMixedEventProxies) { + // Get this collision's leading-jet mixing partner (if any) from the LUT built above: + auto itMix = mixedLeadJetByCollision.find(collId); + prevJetCache.hadLeadJet = (itMix != mixedLeadJetByCollision.end()); + if (prevJetCache.hadLeadJet) { + prevJetCache.leadJetEta = itMix->second.eta; + prevJetCache.leadJetPhi = itMix->second.phi; + } + histos.fill(HIST("JetKinematicsQA/hMixedEventLeadJetOutcome"), prevJetCache.hadLeadJet ? 1 : 0); + if (prevJetCache.hadLeadJet) { + histos.fill(HIST("JetKinematicsQA/h2dMixedLeadJetEtaVsLeadJetEta"), prevJetCache.leadJetEta, leadingJetEta); + histos.fill(HIST("JetKinematicsQA/h2dMixedLeadJetPhiVsLeadJetPhi"), prevJetCache.leadJetPhi, leadingJetPhi); + } + } applyProxyDistortion(hasValidLeadingJet, leadingJetPt, leadingJetEta, leadingJetPhi, leadingJetUnitVec, minLeadJetPt, prevJetCache.hadLeadJet, prevJetCache.leadJetEta, prevJetCache.leadJetPhi, etaLeadPDist, phiLeadPDist, rng); @@ -1496,13 +1654,27 @@ struct lambdajetpolarizationionsderived { float subleadingJetPhi = 0.; XYZVector subJetUnitVec(1., 0., 0.); if (hasValidSubJet) { - subleadingJetEta = subleadingJet->jetEta(); - subleadingJetPhi = subleadingJet->jetPhi(); - subJetUnitVec = XYZVector(subleadingJet->jetPx(), subleadingJet->jetPy(), subleadingJet->jetPz()).Unit(); + subleadingJetEta = jetProxies.subleadingJetEta; + subleadingJetPhi = jetProxies.subleadingJetPhi; + const double inverseCoshEtaSub = 1.0 / std::cosh(subleadingJetEta); + subJetUnitVec = XYZVector(std::cos(subleadingJetPhi) * inverseCoshEtaSub, std::sin(subleadingJetPhi) * inverseCoshEtaSub, std::tanh(subleadingJetEta)); // Apply distortion logic: const bool hadPreviousProxy = prevJetCache.hadSubJet; - // TODO: implement event mixing for other proxies + if (fakePolSwitches.doMixedEventProxies) { + // Get this collision's subleading-jet mixing partner (if any) from the LUT built above: + auto itMix = mixedSubJetByCollision.find(collId); + prevJetCache.hadSubJet = (itMix != mixedSubJetByCollision.end()); + if (prevJetCache.hadSubJet) { + prevJetCache.subJetEta = itMix->second.eta; + prevJetCache.subJetPhi = itMix->second.phi; + } + histos.fill(HIST("JetKinematicsQA/hMixedEventSubJetOutcome"), prevJetCache.hadSubJet ? 1 : 0); + if (prevJetCache.hadSubJet) { + histos.fill(HIST("JetKinematicsQA/h2dMixedSubJetEtaVsSubJetEta"), prevJetCache.subJetEta, subleadingJetEta); + histos.fill(HIST("JetKinematicsQA/h2dMixedSubJetPhiVsSubJetPhi"), prevJetCache.subJetPhi, subleadingJetPhi); + } + } applyProxyDistortion(hasValidSubJet, subleadingJetPt, subleadingJetEta, subleadingJetPhi, subJetUnitVec, minSubLeadJetPt, prevJetCache.hadSubJet, prevJetCache.subJetEta, prevJetCache.subJetPhi, etaLeadPDist, phiLeadPDist, rng); From 04f29803d4de213a045173bc282059c186a78d9a Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Wed, 19 Aug 2026 16:35:36 +0000 Subject: [PATCH 23/31] Major cleaning of variable names (now using camelToe convention more often), expressions that were difficult to read and "to-dos". Some additions listed below. - TableProducer: Added an in-out of acceptance comparison for jet QAing; cached pos/negTrackExtra usages; fixed some improper histogram filling guards. - TableConsumer: refactored the DeltaError estimators into a more performant format using flat vectors instead of maps. Added FATAL errors for configurable bool selection validation to avoid errors when other people are running this. Properly gated some expensive QA histogram fills. Added more event mixing QAs related to collision cannibalization. Refactored applyProxyDistortion to accept some small structs to better organize the callsite. Corrected a small bug between my commits where I seem to have removed excludeOutOfPeakQA and excludeInPeakQA options. --- .../Strangeness/lambdaJetPolarizationIons.cxx | 344 ++--- .../lambdaJetPolarizationIonsDerived.cxx | 1200 +++++++++-------- 2 files changed, 812 insertions(+), 732 deletions(-) diff --git a/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx b/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx index a24a6ae31de..57d938f6a49 100644 --- a/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx +++ b/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx @@ -196,8 +196,8 @@ struct lambdajetpolarizationions { Configurable requireNoCollInROFStd{"requireNoCollInROFStd", false, "reject collisions corrupted by the cannibalism, with other collisions within the same ITS ROF with mult. above a certain threshold (Run 3 only)"}; Configurable requireNoCollInROFStrict{"requireNoCollInROFStrict", false, "reject collisions corrupted by the cannibalism, with other collisions within the same ITS ROF (Run 3 only)"}; // Physics selections: - Configurable requireINEL0{"requireINEL0", true, "require INEL>0 event selection"}; // Only truly useful in pp - Configurable requireINEL1{"requireINEL1", false, "require INEL>1 event selection"}; + Configurable requireINEL0{"requireINEL0", true, "require INEL>0 event selection. Only applied in pp"}; // Only truly useful in pp. Also only ever applied in pp + Configurable requireINEL1{"requireINEL1", false, "require INEL>1 event selection. Only applied in pp"}; // MC: 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", true, "Apply Z-vtx cut on the PV of the generated collision?"}; // I see no reason as to not do this by default @@ -300,6 +300,9 @@ struct lambdajetpolarizationions { ctpRateFetcher rateFetcher; int mRunNumber; float magField; + // For some binning variables: + int mBinHasRingJet; + int mBinHasRingV0; std::map metadata; o2::parameters::GRPMagField* grpmag = nullptr; @@ -310,7 +313,21 @@ struct lambdajetpolarizationions { ConfigurableAxis axisPtXi{"axisPtXi", {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 feeddown from Xi"}; ConfigurableAxis axisPtCoarse{"axisPtCoarse", {VARIABLE_WIDTH, 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 7.0f, 10.0f, 15.0f}, "pt axis for QA"}; ConfigurableAxis axisLambdaMass{"axisLambdaMass", {450, 1.08f, 1.15f}, ""}; // Default is {200, 1.101f, 1.131f} - ConfigurableAxis axisLambdaMassCoarse{"axisLambdaMassCoarse", {200, 1.101f, 1.131f}, ""}; + // ConfigurableAxis axisLambdaMassCoarse{"axisLambdaMassCoarse", {200, 1.101f, 1.131f}, ""}; + // Shared axis with the derived consumer, to focus on the peak-vs-sideband trends: + ConfigurableAxis axisLambdaMassSigExtract{ + "axisLambdaMassSigExtract", + {VARIABLE_WIDTH, + // Left sideband (7 bins, 0.004 width) + 1.0800, 1.0840, 1.0880, 1.0920, + 1.0960, 1.1000, 1.1040, 1.1080, + // Fine peak region (8 bins, 0.0016 width) + 1.1096, 1.1112, 1.1128, 1.1144, + 1.1160, 1.1176, 1.1192, 1.1208, + // Right sideband (7 bins, 0.004 width) + 1.1248, 1.1288, 1.1328, 1.1368, + 1.1408, 1.1448, 1.1488}, + "Lambda mass in GeV/c"}; ConfigurableAxis axisPVz{"axisPVz", {100, -20.0f, +20.0f}, "Primary Vertex Z [cm]"}; ConfigurableAxis axisPVzCoarse{"axisPVzCoarse", {20, -20.0f, +20.0f}, "Primary Vertex Z [cm]"}; // Centrality/IR/Occupancy: @@ -372,7 +389,7 @@ struct lambdajetpolarizationions { struct : ConfigurableGroup { std::string prefix = "jetConfigurations"; // JSON group name Configurable minJetPt{"minJetPt", 20.0f, "Minimum reconstructed pt of the jet (GeV/c)"}; // Something in between pp and PbPb minima. Change for bkgSubtraction true or false! - Configurable radiusJet{"radiusJet", 0.4f, "Jet resolution parameter (R)"}; // (TODO: check if the JE people don't define this as a rescaled int to not lose precision for stricter selections) + Configurable radiusJet{"radiusJet", 0.4f, "Jet resolution parameter (R)"}; // Notice that the maximum Eta of the jet will then be 0.9 - R to keep the jet contained within the ITS+TPC barrel. Configurable jetAlgorithm{"jetAlgorithm", kAntiKt, "jet clustering algorithm. 0 = kT, 1 = C/A, 2 = Anti-kT"}; @@ -473,7 +490,7 @@ struct lambdajetpolarizationions { /// \brief Books every histogram used by the producer and sets up the CCDB/RCT services. void init(InitContext const&) { - // setting CCDB service + // Setting CCDB service ccdb->setURL(ccdbConfigurations.ccdbUrl); ccdb->setCaching(true); ccdb->setFatalWhenNull(false); @@ -514,6 +531,10 @@ struct lambdajetpolarizationions { histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(24, "hasRingV0"); // (notice we lack a hasRingJet AND hasRingV0 bin because the tasks run separately on all events!) // (this QA number can be obtained at derived data level with ease) + // Cache the x-values of the last two stages for convenience: + const int nEvSelBins = histos.get(HIST("hEventSelection"))->GetXaxis()->GetNbins(); + mBinHasRingJet = nEvSelBins - 2; + mBinHasRingV0 = nEvSelBins - 1; histos.add("Centrality/hEventCentrality", "hEventCentrality;Centrality (%);Counts", kTH1D, {{101, 0.0f, 101.0f}}); histos.add("Centrality/hCentralityVsNch", "hCentralityVsNch;Centrality (%);N_{ch}", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisNch}); @@ -650,7 +671,7 @@ struct lambdajetpolarizationions { // TH2D: "selection flow" vs "Lambda invariant mass" (to investigate whether we are removing background or just signal) auto h2dSelectionLambdaMass = histos.add("GeneralQA/h2dSelectionLambdaMass", "V0 selection flow vs M_{#Lambda}; ; M_{#Lambda} (GeV/#it{c}^{2})", kTH2D, {{static_cast(v0LambdaSelectionLabels.size()), -0.5, static_cast(v0LambdaSelectionLabels.size()) - 0.5}, - axisConfigurations.axisLambdaMassCoarse}); + axisConfigurations.axisLambdaMassSigExtract}); for (size_t i = 0; i < v0LambdaSelectionLabels.size(); ++i) { auto lbl = v0LambdaSelectionLabels[i].label; if (!v0LambdaSelectionLabels[i].enabled) @@ -661,7 +682,7 @@ struct lambdajetpolarizationions { // Same for AntiLambda mass hypothesis: auto h2dSelectionAntiLambdaMass = histos.add("GeneralQA/h2dSelectionAntiLambdaMass", "V0 selection flow vs M_{#bar{#Lambda}}; ; M_{#bar{#Lambda}} (GeV/#it{c}^{2})", kTH2D, {{static_cast(v0LambdaSelectionLabels.size()), -0.5, static_cast(v0LambdaSelectionLabels.size()) - 0.5}, - axisConfigurations.axisLambdaMassCoarse}); + axisConfigurations.axisLambdaMassSigExtract}); for (size_t i = 0; i < v0LambdaSelectionLabels.size(); ++i) { auto lbl = v0LambdaSelectionLabels[i].label; if (!v0LambdaSelectionLabels[i].enabled) @@ -867,6 +888,12 @@ struct lambdajetpolarizationions { histos.add("JetKinematicsQA/hCosThetaToLeadingJet", "hCosThetaToLeadingJet;cos(#Delta#theta_{jet});Counts", kTH1D, {axisConfigurations.axisCosTheta}); histos.add("JetKinematicsQA/hDeltaPhiToLeadingJet", "hDeltaPhiToLeadingJet;#Delta#varphi;Counts", kTH1D, {axisConfigurations.axisDeltaPhi}); histos.add("JetKinematicsQA/hDeltaEtaToLeadingJet", "hDeltaEtaToLeadingJet;#Delta#eta;Counts", kTH1D, {axisConfigurations.axisDeltaEta}); + // Acceptance-edge QA: same three observables, but filled only for the jets rejected by the |eta| < 0.9 - R cut: + // (Those jets lose constituents on one side, so this measures the pT biasing) + histos.add("JetKinematicsQA/hJetEtaOutOfAcceptance", "hJetEtaOutOfAcceptance;#eta;Counts", kTH1D, {axisConfigurations.axisEta}); + histos.add("JetKinematicsQA/hDeltaEtaToLeadingJetOutOfAcceptance", "hDeltaEtaToLeadingJetOutOfAcceptance;#Delta#eta;Counts", kTH1D, {axisConfigurations.axisDeltaEta}); + histos.add("JetKinematicsQA/hDeltaRToLeadingJetOutOfAcceptance", "hDeltaRToLeadingJetOutOfAcceptance;#Delta R;Counts", kTH1D, {axisConfigurations.axisDeltaR}); + histos.add("JetKinematicsQA/hDeltaRToLeadingJet", "hDeltaRToLeadingJet;#Delta R;Counts", kTH1D, {axisConfigurations.axisDeltaR}); histos.add("JetKinematicsQA/hLeadingJetPt", "hLeadingJetPt;Jet p_{T} [GeV/c];Counts", kTH1D, {axisConfigurations.axisJetPt}); @@ -995,7 +1022,7 @@ struct lambdajetpolarizationions { // fillUpTo advances through disabled bins, filling all three histograms uniformly. void fillUpTo(int targetBinX) { while (binValue < targetBinX) fill(); } }; - V0SelectionFlowCounter V0SelCounter{-1, &histos}; // Could initialize with any index (resetForNewV0 is always called for a new V0 anyways) + V0SelectionFlowCounter v0SelCounter{-1, &histos}; // Could initialize with any index (resetForNewV0 is always called for a new V0 anyways) // Calculating some bins, for convenience: const int nGenericCuts = 31; // x=0 to x=30 (bins 1 and 31, as the lower-edge is inclusive) const int nHypoCuts = 11; // per hypothesis (x=31..41 for Lambda) @@ -1010,7 +1037,7 @@ struct lambdajetpolarizationions { void resetForNewTrack() { binValue = -1; } void fill() { histos->fill(HIST("GeneralQA/hSelectionJetTracks"), ++binValue); } }; - JetTrackSelectionFlowCounter JetTrackSelCounter{-1, &histos}; + JetTrackSelectionFlowCounter jetTrackSelCounter{-1, &histos}; // Short inlined helper to simplify QA inline void fillEventSelectionQA(int bin, float centrality) @@ -1036,7 +1063,6 @@ struct lambdajetpolarizationions { histos.fill(HIST("Centrality/hEventCentVsMultFV0A"), collision.centFV0A(), collision.multFV0A()); histos.fill(HIST("Centrality/hEventMultFT0CvsMultFV0A"), collision.multFT0C(), collision.multFV0A()); } - return; } ///////////////////////////////////////////// @@ -1238,84 +1264,82 @@ struct lambdajetpolarizationions { if (detMap & o2::aod::track::ITS) return false; } - JetTrackSelCounter.fill(); // bin: forceNoITS + jetTrackSelCounter.fill(); // bin: forceNoITS if (pseudoJetCandidateTrackSelections.minITSnCls >= 0) { if (track.itsNCls() < pseudoJetCandidateTrackSelections.minITSnCls) return false; } - JetTrackSelCounter.fill(); // bin: ITS clusters (min) + jetTrackSelCounter.fill(); // bin: ITS clusters (min) if (track.tpcNClsCrossedRows() < pseudoJetCandidateTrackSelections.minNCrossedRowsTPC) return false; - JetTrackSelCounter.fill(); + jetTrackSelCounter.fill(); if (track.tpcChi2NCl() > pseudoJetCandidateTrackSelections.maxChi2TPC) return false; - JetTrackSelCounter.fill(); + jetTrackSelCounter.fill(); if (track.itsChi2NCl() > pseudoJetCandidateTrackSelections.maxChi2ITS) return false; - JetTrackSelCounter.fill(); + jetTrackSelCounter.fill(); // Kinematics: const float pt = track.pt(); if (pt < pseudoJetCandidateTrackSelections.minCandidatePt) return false; - JetTrackSelCounter.fill(); + jetTrackSelCounter.fill(); if (std::fabs(track.eta()) > pseudoJetCandidateTrackSelections.etaCut) return false; - JetTrackSelCounter.fill(); + jetTrackSelCounter.fill(); // DCA pseudojet candidate selections -- These select primary vertex particles for the jet: if (pseudoJetCandidateTrackSelections.doDCAcuts) { // if (std::fabs(track.dcaXY()) > pseudoJetCandidateTrackSelections.maxDCAxy) return false; if (std::fabs(track.dcaZ()) > pseudoJetCandidateTrackSelections.maxDCAz) return false; - JetTrackSelCounter.fill(); + jetTrackSelCounter.fill(); // Slightly more physics-motivated cut (parametrizes the DCA resolution as function of pt) if (std::fabs(track.dcaXY()) > (pseudoJetCandidateTrackSelections.dcaxyMaxTrackPar0 + pseudoJetCandidateTrackSelections.dcaxyMaxTrackPar1 / std::pow(pt, pseudoJetCandidateTrackSelections.dcaxyMaxTrackPar2))) return false; - JetTrackSelCounter.fill(); + jetTrackSelCounter.fill(); } else { // Should fill counters an equal number of times to advance indices (future-proofing, but could do it by just advancing indices by hand in JetTrackSelectionFlowCounter) - JetTrackSelCounter.fill(); - JetTrackSelCounter.fill(); + jetTrackSelCounter.fill(); + jetTrackSelCounter.fill(); } return true; } // Lambda selections: /// \brief Charge-independent V0 cuts (topology, ITS/TPC track quality) shared by both the Lambda and AntiLambda hypotheses. + /// \param posTrackExtra,negTrackExtra the V0's daughter tracks, cached once at the process function. /// \note See passesLambdaLambdaBarHypothesis() for the hypothesis-specific cuts. - template - bool passesGenericV0Cuts(TV0 const& v0) + template + bool passesGenericV0Cuts(TV0 const& v0, TTrack const& posTrackExtra, TTrack const& negTrackExtra) { // Base topological variables (high rejection, low cost checks) if (v0.v0radius() < v0Selections.v0radius) return false; - V0SelCounter.fill(); + v0SelCounter.fill(); if (v0.v0radius() > v0Selections.v0radiusMax) return false; - V0SelCounter.fill(); + v0SelCounter.fill(); if (v0.v0cosPA() < v0Selections.v0cospa) return false; - V0SelCounter.fill(); + v0SelCounter.fill(); if (v0.dcaV0daughters() > v0Selections.dcav0dau) return false; - V0SelCounter.fill(); + v0SelCounter.fill(); // rapidity cuts (this is actually a physics cut, moreso than being an acceptance cut): if (std::fabs(v0.yLambda()) > v0Selections.rapidityCut) return false; - V0SelCounter.fill(); + v0SelCounter.fill(); // competing mass rejection (if compMassRejection < 0, this cut does nothing) if (std::fabs(v0.mK0Short() - o2::constants::physics::MassK0Short) < v0Selections.compMassRejection) return false; - V0SelCounter.fill(); - - const auto posTrackExtra = v0.template posTrack_as(); // (TODO: is it worth it to cache these transformations outside of the function? They are reused in the Lambda hypothesis checks) - const auto negTrackExtra = v0.template negTrack_as(); + v0SelCounter.fill(); // ITS quality cuts bool posIsFromAfterburner = posTrackExtra.isITSAfterburner(); @@ -1324,124 +1348,122 @@ struct lambdajetpolarizationions { // check minimum number of ITS clusters + maximum ITS chi2 per clusters + reject or select ITS afterburner tracks if requested if (posTrackExtra.itsNCls() < v0Selections.minITSclusters) return false; // check minimum ITS clusters - V0SelCounter.fill(); + v0SelCounter.fill(); if (posTrackExtra.itsChi2NCl() >= v0Selections.maxITSchi2PerNcls) return false; // check maximum ITS chi2 per clusters - V0SelCounter.fill(); + v0SelCounter.fill(); if (v0Selections.rejectPosITSafterburner && posIsFromAfterburner) return false; // reject afterburner track or not - V0SelCounter.fill(); + v0SelCounter.fill(); if (v0Selections.requirePosITSafterburnerOnly && !posIsFromAfterburner) return false; // keep afterburner track or not - V0SelCounter.fill(); + v0SelCounter.fill(); if (negTrackExtra.itsNCls() < v0Selections.minITSclusters) return false; // check minimum ITS clusters - V0SelCounter.fill(); + v0SelCounter.fill(); if (negTrackExtra.itsChi2NCl() >= v0Selections.maxITSchi2PerNcls) return false; // check maximum ITS chi2 per clusters - V0SelCounter.fill(); + v0SelCounter.fill(); if (v0Selections.rejectNegITSafterburner && negIsFromAfterburner) return false; // reject afterburner track or not - V0SelCounter.fill(); + v0SelCounter.fill(); if (v0Selections.requireNegITSafterburnerOnly && !negIsFromAfterburner) return false; // keep afterburner track or not - V0SelCounter.fill(); + v0SelCounter.fill(); // TPC quality cuts if (posTrackExtra.tpcNClsCrossedRows() < v0Selections.minTPCrows) return false; // check minimum TPC crossed rows - V0SelCounter.fill(); + v0SelCounter.fill(); if (posTrackExtra.tpcChi2NCl() >= v0Selections.maxTPCchi2PerNcls) return false; // check maximum TPC chi2 per clusters - V0SelCounter.fill(); + v0SelCounter.fill(); if (posTrackExtra.tpcCrossedRowsOverFindableCls() < v0Selections.minTPCrowsOverFindableClusters) return false; // check minimum fraction of TPC rows over findable - V0SelCounter.fill(); + v0SelCounter.fill(); if (posTrackExtra.tpcFoundOverFindableCls() < v0Selections.minTPCfoundOverFindableClusters) return false; // check minimum fraction of found over findable TPC clusters - V0SelCounter.fill(); + v0SelCounter.fill(); if (posTrackExtra.tpcFractionSharedCls() >= v0Selections.maxFractionTPCSharedClusters) return false; // check the maximum fraction of allowed shared TPC clusters - V0SelCounter.fill(); + v0SelCounter.fill(); if (v0Selections.rejectTPCsectorBoundary && !isTrackFarFromTPCBoundary(v0.positivept(), v0.positivephi(), +1)) return false; // reject track far from TPC sector boundary or not - V0SelCounter.fill(); + v0SelCounter.fill(); if (negTrackExtra.tpcNClsCrossedRows() < v0Selections.minTPCrows) return false; // check minimum TPC crossed rows - V0SelCounter.fill(); + v0SelCounter.fill(); if (negTrackExtra.tpcChi2NCl() >= v0Selections.maxTPCchi2PerNcls) return false; // check maximum TPC chi2 per clusters - V0SelCounter.fill(); + v0SelCounter.fill(); if (negTrackExtra.tpcCrossedRowsOverFindableCls() < v0Selections.minTPCrowsOverFindableClusters) return false; // check minimum fraction of TPC rows over findable - V0SelCounter.fill(); + v0SelCounter.fill(); if (negTrackExtra.tpcFoundOverFindableCls() < v0Selections.minTPCfoundOverFindableClusters) return false; // check minimum fraction of found over findable TPC clusters - V0SelCounter.fill(); + v0SelCounter.fill(); if (negTrackExtra.tpcFractionSharedCls() >= v0Selections.maxFractionTPCSharedClusters) return false; // check the maximum fraction of allowed shared TPC clusters - V0SelCounter.fill(); + v0SelCounter.fill(); if (v0Selections.rejectTPCsectorBoundary && !isTrackFarFromTPCBoundary(v0.negativept(), v0.negativephi(), -1)) return false; // reject track far from TPC sector boundary or not - V0SelCounter.fill(); + v0SelCounter.fill(); // ITS only tag if (v0Selections.requirePosITSonly && posTrackExtra.tpcNClsCrossedRows() > 1) return false; - V0SelCounter.fill(); + v0SelCounter.fill(); if (v0Selections.requireNegITSonly && negTrackExtra.tpcNClsCrossedRows() > 1) return false; - V0SelCounter.fill(); + v0SelCounter.fill(); // TPC only tag if (v0Selections.skipTPConly && posTrackExtra.detectorMap() == o2::aod::track::TPC) return false; - V0SelCounter.fill(); + v0SelCounter.fill(); if (v0Selections.skipTPConly && negTrackExtra.detectorMap() == o2::aod::track::TPC) return false; - V0SelCounter.fill(); + v0SelCounter.fill(); return true; } /// \brief Tests the hypothesis of the V0 being a Lambda or of it being an antiLambda. - /// \param Lambda_hypothesis "true" tests the Lambda (proton+/pion-) hypothesis. "false" tests AntiLambda. - template - bool passesLambdaLambdaBarHypothesis(TV0 const& v0, TCollision const& collision, bool Lambda_hypothesis) + /// \param posTrackExtra,negTrackExtra the V0's daughter tracks, cached once at the process function. + /// \param lambdaHypothesis "true" tests the Lambda (proton+/pion-) hypothesis. "false" tests AntiLambda. + template + bool passesLambdaLambdaBarHypothesis(TV0 const& v0, TCollision const& collision, TTrack const& posTrackExtra, TTrack const& negTrackExtra, bool lambdaHypothesis) { // Remaining topological cuts that were charge-dependent: // (there is no real gain in doing a looser version of these in the passesGenericV0Cuts function. // The DCA check will be done anyways and is very unexpensive) // (even though they are high rejection, they demand a Lambda vs AntiLambda hypothesis, so they // only appear here...) - const float dcaProtonToPV = Lambda_hypothesis ? std::abs(v0.dcapostopv()) : std::abs(v0.dcanegtopv()); + const float dcaProtonToPV = lambdaHypothesis ? std::abs(v0.dcapostopv()) : std::abs(v0.dcanegtopv()); if (dcaProtonToPV < v0Selections.dcaProtonToPV) return false; - V0SelCounter.fill(); - const float dcaPionToPV = Lambda_hypothesis ? std::abs(v0.dcanegtopv()) : std::abs(v0.dcapostopv()); // Checks Lambda_hypothesis twice, but compiler can handle it cleanly. + v0SelCounter.fill(); + const float dcaPionToPV = lambdaHypothesis ? std::abs(v0.dcanegtopv()) : std::abs(v0.dcapostopv()); // Checks lambdaHypothesis twice, but compiler can handle it cleanly. if (dcaPionToPV < v0Selections.dcaPionToPV) return false; - V0SelCounter.fill(); - - const auto posTrackExtra = v0.template posTrack_as(); - const auto negTrackExtra = v0.template negTrack_as(); + v0SelCounter.fill(); // For the PID cuts to be properly applied while also keeping this function // general enough for Lambdas and AntiLambdas, we identify the roles of // proton-like and pion-like for the pos and neg tracks accordingly: - auto const& protonTrack = Lambda_hypothesis ? posTrackExtra : negTrackExtra; - auto const& pionTrack = Lambda_hypothesis ? negTrackExtra : posTrackExtra; + auto const& protonTrack = lambdaHypothesis ? posTrackExtra : negTrackExtra; + auto const& pionTrack = lambdaHypothesis ? negTrackExtra : posTrackExtra; ///// Expensive PID checks come last: // TPC PID if (std::fabs(protonTrack.tpcNSigmaPr()) > v0Selections.tpcPidNsigmaCut) return false; - V0SelCounter.fill(); + v0SelCounter.fill(); if (std::fabs(pionTrack.tpcNSigmaPi()) > v0Selections.tpcPidNsigmaCut) return false; - V0SelCounter.fill(); + v0SelCounter.fill(); // Only do TOF checks when actually using TOF subscriptions: // if (doprocessDataWithTOF) { @@ -1452,23 +1474,23 @@ struct lambdajetpolarizationions { const bool pionHasTOF = pionTrack.hasTOF(); // Proton-like track - if (protonHasTOF && std::abs(Lambda_hypothesis ? v0.posTOFDeltaTLaPr() : v0.negTOFDeltaTLaPr()) > v0Selections.maxDeltaTimeProton) + if (protonHasTOF && std::abs(lambdaHypothesis ? v0.posTOFDeltaTLaPr() : v0.negTOFDeltaTLaPr()) > v0Selections.maxDeltaTimeProton) return false; - V0SelCounter.fill(); + v0SelCounter.fill(); // Pion-like track - if (pionHasTOF && std::abs(Lambda_hypothesis ? v0.negTOFDeltaTLaPi() : v0.posTOFDeltaTLaPi()) > v0Selections.maxDeltaTimePion) + if (pionHasTOF && std::abs(lambdaHypothesis ? v0.negTOFDeltaTLaPi() : v0.posTOFDeltaTLaPi()) > v0Selections.maxDeltaTimePion) return false; - V0SelCounter.fill(); + v0SelCounter.fill(); // TOF PID in NSigma (TODO: add asymmetric NSigma windows for purity tuning?) // Proton-like track (notice usage of tofNSigmaLaPr vs tofNSigmaALaPr) - if (protonHasTOF && std::fabs(Lambda_hypothesis ? v0.tofNSigmaLaPr() : v0.tofNSigmaALaPr()) > v0Selections.tofPidNsigmaCutLaPr) - return false; // (No need to select which candidate is which with the Lambda_hypothesis. Automatically done already!) - V0SelCounter.fill(); + if (protonHasTOF && std::fabs(lambdaHypothesis ? v0.tofNSigmaLaPr() : v0.tofNSigmaALaPr()) > v0Selections.tofPidNsigmaCutLaPr) + return false; // (No need to select which candidate is which with the lambdaHypothesis. Automatically done already!) + v0SelCounter.fill(); // Pion-like track - if (pionHasTOF && std::fabs(Lambda_hypothesis ? v0.tofNSigmaLaPi() : v0.tofNSigmaALaPi()) > v0Selections.tofPidNsigmaCutLaPi) + if (pionHasTOF && std::fabs(lambdaHypothesis ? v0.tofNSigmaLaPi() : v0.tofNSigmaALaPi()) > v0Selections.tofPidNsigmaCutLaPi) return false; - V0SelCounter.fill(); + v0SelCounter.fill(); // (CAUTION!) You cannot use the getter for raw data's PIDResponseTOF.h instead of LFStrangenessPIDTables.h (as below) // If you do use, TOF will just try to identify that track as a proton from the PV, instead of using the correct path @@ -1477,19 +1499,19 @@ struct lambdajetpolarizationions { // if (protonHasTOF && std::fabs(protonTrack.tofNSigmaPr()) > v0Selections.tofPidNsigmaCutLaPr) return false; // To properly use the LFStrangenessPIDTables version, you need to call o2-analysis-lf-strangenesstofpid too. } else { // Should fill counters an equal number of times to advance indices - V0SelCounter.fillUpTo(V0SelCounter.binValue + 4); // Fills the 4 times "V0SelCounter.fill()" would be called + v0SelCounter.fillUpTo(v0SelCounter.binValue + 4); // Fills the 4 times "v0SelCounter.fill()" would be called } // proper lifetime if (v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * o2::constants::physics::MassLambda0 > v0Selections.lambdaLifetimeCut) return false; - V0SelCounter.fill(); + v0SelCounter.fill(); return true; } // Function to help distinguish ambiguous candidates (via Armenteros) that pass both - // the Lambda_hypothesis true (i.e., a Lambda) or false (i.e., an AntiLambda) checks + // the lambdaHypothesis true (i.e., a Lambda) or false (i.e., an AntiLambda) checks // (This function is only called in about 1-3% of the Lambda-Like V0s which remain ambiguous after all other cuts) // int isCandidateArmenterosLambda(const float alpha, const float qt){ // // Remove K0s band @@ -1521,8 +1543,8 @@ struct lambdajetpolarizationions { int leadingParticleIdx = -1; // Initialized as -1, but could leave it unitialized as well. We reject any invalid events where this could pose a problem (e.g., pT<=0) float leadingParticlePt = 0; for (auto const& track : tracks) { - JetTrackSelCounter.resetForNewTrack(); // reset bin counter for this candidate - JetTrackSelCounter.fill(); // bin: "All track candidates" + jetTrackSelCounter.resetForNewTrack(); // reset bin counter for this candidate + jetTrackSelCounter.fill(); // bin: "All track candidates" // Require that tracks pass selection criteria if (!isCandidateForChargedPseudojetAccepted(track)) @@ -1541,19 +1563,15 @@ struct lambdajetpolarizationions { leadingParticleIdx = fjParticles.size() - 1; } } - // Reject empty events if (fjParticles.size() < 1) return; - int lastBinEvSel = histos.get(HIST("hEventSelection"))->GetXaxis()->GetNbins(); - auto const& leadingParticle = fjParticles[leadingParticleIdx]; if (leadingParticle.pt() > jetConfigurations.minLeadParticlePt) { // If not, leading particle is probably a bad proxy tableLeadParticles(ringCollIdx, leadingParticle.pt(), leadingParticle.eta(), leadingParticle.phi()); } - // Start jet clusterization: - // Cluster particles using the anti-kt algorithm + // Cluster particles with the configured algorithm: fastjet::JetDefinition jetDef(mapFJAlgorithm(jetConfigurations.jetAlgorithm), jetConfigurations.radiusJet, mapFJRecombScheme(jetConfigurations.jetRecombScheme)); if (jetConfigurations.bkgSubtraction == kAreaBased) { fastjet::AreaDefinition areaDef(fastjet::active_area, fastjet::GhostedAreaSpec(jetConfigurations.GhostedAreaSpecRapidity)); @@ -1564,16 +1582,23 @@ struct lambdajetpolarizationions { // Perpendicular cone area subtraction, not the traditional subtraction (TODO: include an option for traditional area subtraction) auto [rhoPerp, rhoMPerp] = jetutilities::estimateRhoPerpCone(fjParticles, jets[0], jetConfigurations.radiusJet); // This uses a geometric, pi*R^2 area, not exactly a ghost-based area! - // Loop over clustered jets: int selectedJets = 0; fastjet::PseudoJet leadingJetSub; float leadingJetPt = -1.f; + // Storing the accepted subtracted jets in memory: + // (the QA loop below uses them, and this is inexpensive enough to store) + std::vector subtractedJets; + if (doJetKinematicsQA) + subtractedJets.reserve(jets.size()); for (const auto& jet : jets) { // Jet must be fully contained in the acceptance (0.9 for ITS+TPC barrel) - const float jet_eta = jet.eta(); - if (std::fabs(jet_eta) > (0.9f - jetConfigurations.radiusJet)) + const float jetEta = jet.eta(); + if (std::fabs(jetEta) > (0.9f - jetConfigurations.radiusJet)) { + if (doJetKinematicsQA) + histos.fill(HIST("JetKinematicsQA/hJetEtaOutOfAcceptance"), jetEta); continue; + } auto jetForSub = jet; // Subtracts same background estimated for highest pt jet, but every jet might have a slightly different area @@ -1584,8 +1609,9 @@ struct lambdajetpolarizationions { if (jetMinusBkg.pt() < jetConfigurations.minJetPt) continue; selectedJets++; + if (doJetKinematicsQA) + subtractedJets.emplace_back(jetMinusBkg); - // Store jet: tableJets(ringCollIdx, jetMinusBkg.pt(), jetMinusBkg.eta(), // Using eta instead of rapidity @@ -1604,7 +1630,7 @@ struct lambdajetpolarizationions { histos.fill(HIST("hEventsWithJet"), 0.5); // Another version of this counter, which is already integrated in the Event Selection flow: if (doEventQA) - fillEventSelectionQA(lastBinEvSel - 2, centrality); // hasRingJet passes + fillEventSelectionQA(mBinHasRingJet, centrality); // hasRingJet passes if (doJetKinematicsQA) { histos.fill(HIST("JetKinematicsQA/h2dConstituentsPerJetVsCentrality"), leadingJetSub.constituents().size(), centrality); @@ -1613,15 +1639,8 @@ struct lambdajetpolarizationions { histos.fill(HIST("JetKinematicsQA/hLeadingJetEta"), leadingJetSub.eta()); histos.fill(HIST("JetKinematicsQA/hLeadingJetPhi"), leadingJetSub.phi()); - // Now looping through jets again to calculate the correlations: - for (const auto& jet : jets) { - // Will recalculated background subtraction during QA to avoid storing jets in memory when running in non-QA cases: - auto jetForSub = jet; - fastjet::PseudoJet jetMinusBkg = backgroundSub.doRhoAreaSub(jetForSub, rhoPerp, rhoMPerp); - - if (jetMinusBkg.pt() < jetConfigurations.minJetPt) - continue; - + // Now looping through the accepted subtracted jets to calculate the correlations: + for (const auto& jetMinusBkg : subtractedJets) { float cosTheta = cosThetaJets(leadingJetSub, jetMinusBkg); float deltaPhi = RecoDecay::constrainAngle(leadingJetSub.phi() - jetMinusBkg.phi(), -o2::constants::math::PI); float deltaEta = leadingJetSub.eta() - jetMinusBkg.eta(); @@ -1697,34 +1716,43 @@ struct lambdajetpolarizationions { histos.fill(HIST("hEventsWithJet"), 0.5); // Another version of this counter, which is already integrated in the Event Selection flow: if (doEventQA) - fillEventSelectionQA(lastBinEvSel - 2, centrality); // hasRingJet passes + fillEventSelectionQA(mBinHasRingJet, centrality); // hasRingJet passes - const auto& leadingJet = jets[0]; // A leading jet before eta cuts. Different type of QA from what we will be able to do down the consumer pipeline + const auto& leadingJet = jets[0]; for (const auto& jet : jets) { // Jet must be fully contained in the acceptance (0.9 for ITS+TPC barrel) - const float jet_eta = jet.eta(); - if (std::fabs(jet_eta) > (0.9f - jetConfigurations.radiusJet)) + const float jetEta = jet.eta(); + if (std::fabs(jetEta) > (0.9f - jetConfigurations.radiusJet)) { + if (doJetKinematicsQA) { + // QA before the acceptance cut: + const float deltaPhiOut = RecoDecay::constrainAngle(leadingJet.phi() - jet.phi(), -o2::constants::math::PI); + const float deltaEtaOut = leadingJet.eta() - jetEta; + histos.fill(HIST("JetKinematicsQA/hJetEtaOutOfAcceptance"), jetEta); + histos.fill(HIST("JetKinematicsQA/hDeltaEtaToLeadingJetOutOfAcceptance"), deltaEtaOut); + histos.fill(HIST("JetKinematicsQA/hDeltaRToLeadingJetOutOfAcceptance"), std::sqrt(deltaPhiOut * deltaPhiOut + deltaEtaOut * deltaEtaOut)); + } continue; + } tableJets(ringCollIdx, jet.pt(), - jet_eta, // Using eta instead of rapidity + jetEta, // Using eta instead of rapidity jet.phi()); // jet.constituents().size()); // Currently removed from datamodel. // Other variables can better reveal jet quenching and help identify good selection criteria for quenched jets proxies if (doJetKinematicsQA) { histos.fill(HIST("JetKinematicsQA/hJetPt"), jet.pt()); - histos.fill(HIST("JetKinematicsQA/hJetEta"), jet_eta); + histos.fill(HIST("JetKinematicsQA/hJetEta"), jetEta); histos.fill(HIST("JetKinematicsQA/hJetPhi"), jet.phi()); - histos.fill(HIST("JetKinematicsQA/h2dJetEtaVsJetPhi"), jet_eta, jet.phi()); + histos.fill(HIST("JetKinematicsQA/h2dJetEtaVsJetPhi"), jetEta, jet.phi()); // Calculate angle to leading jet: float cosTheta = cosThetaJets(leadingJet, jet); // Calculate angular separation in projected angles: float deltaPhi = RecoDecay::constrainAngle(leadingJet.phi() - jet.phi(), -o2::constants::math::PI); - float deltaEta = leadingJet.eta() - jet_eta; + float deltaEta = leadingJet.eta() - jetEta; float deltaR = std::sqrt(deltaPhi * deltaPhi + deltaEta * deltaEta); // 2D angular distance in the eta-phi plane histos.fill(HIST("JetKinematicsQA/hCosThetaToLeadingJet"), cosTheta); // Measuring the cosine, not angle, because it is faster! @@ -1805,13 +1833,11 @@ struct lambdajetpolarizationions { { float centrality = getCentrality(collision); // Strictly for QA. We save other types of centrality estimators in the derived data! - // For event QA the last two indices never change for NEv_withJets and NEv_withV0s - // (Not the best way to initialize this: runs once per collision! TODO: think of a better way to do it) - int lastBinEvSel = histos.get(HIST("hEventSelection"))->GetXaxis()->GetNbins(); bool validV0AlreadyFound = false; histos.fill(HIST("hEventSelection"), 0. /* all collisions */); - histos.fill(HIST("hEventSelectionVsCentrality"), 0. /* all collisions */, centrality); + if (doEventQA) + histos.fill(HIST("hEventSelectionVsCentrality"), 0. /* all collisions */, centrality); auto bc = bcs.iteratorAt(collision.bcId()); @@ -1832,7 +1858,6 @@ struct lambdajetpolarizationions { const float collisionPVz = collision.posZ(); - // Fill event table: tableCollisions(collision.centFT0M(), collision.centFT0C(), collision.centFV0A(), @@ -1842,20 +1867,24 @@ struct lambdajetpolarizationions { // Get the derived collision row index for this event: const int ringCollIdx = tableCollisions.lastIndex(); - // Call to jets process: jetsProcess(V0DauTracks, ringCollIdx, centrality, collisionPVz); // V0DauTracks takes the place of jetTracks now // Counting particles per event: - uint NLambdas = 0; - uint NAntiLambdas = 0; - uint NNonAmbiguous = 0; - uint NAmbiguous = 0; + uint16_t nLambdas = 0; + uint16_t nAntiLambdas = 0; + uint16_t nNonAmbiguous = 0; + uint16_t nAmbiguous = 0; for (auto const& v0 : V0s) { - V0SelCounter.resetForNewV0(v0.mLambda(), v0.mAntiLambda()); - V0SelCounter.fill(); // Fill for all v0 candidates + v0SelCounter.resetForNewV0(v0.mLambda(), v0.mAntiLambda()); + v0SelCounter.fill(); // Fill for all v0 candidates if (doArmenterosQA) histos.fill(HIST("GeneralQA/h2dArmenterosAll"), v0.alpha(), v0.qtarm()); // fill AP plot for all V0s - if (!passesGenericV0Cuts(v0)) + + // Daughter tracks cached once per candidate (needed by both hypothesis tests, by the QA and by the table fill) + const auto posTrackExtra = v0.template posTrack_as(); + const auto negTrackExtra = v0.template negTrack_as(); + + if (!passesGenericV0Cuts(v0, posTrackExtra, negTrackExtra)) continue; if (doArmenterosQA) @@ -1865,20 +1894,20 @@ struct lambdajetpolarizationions { bool isLambda = false; bool isAntiLambda = false; if (analyseLambda) - isLambda = passesLambdaLambdaBarHypothesis(v0, collision, true); + isLambda = passesLambdaLambdaBarHypothesis(v0, collision, posTrackExtra, negTrackExtra, true); if (analyseAntiLambda) { if (analyseLambda) // We only need to advance when the Lambda hypothesis had an early exit on the counters - V0SelCounter.advanceTo(lambdaHypoEnd + 1); // sync to bin 43 (x=42 means bin 43, the first #bar{#Lambda} bin) - isAntiLambda = passesLambdaLambdaBarHypothesis(v0, collision, false); + v0SelCounter.advanceTo(lambdaHypoEnd + 1); // sync to bin 43 (x=42 means bin 43, the first #bar{#Lambda} bin) + isAntiLambda = passesLambdaLambdaBarHypothesis(v0, collision, posTrackExtra, negTrackExtra, false); } if (!isLambda && !isAntiLambda) continue; // Candidate is not considered to be Lambda-like if (isLambda) - NLambdas++; + nLambdas++; if (isAntiLambda) - NAntiLambdas++; + nAntiLambdas++; if (doArmenterosQA) histos.fill(HIST("GeneralQA/h2dArmenterosFullSelected"), v0.alpha(), v0.qtarm()); // cross-check @@ -1890,12 +1919,12 @@ struct lambdajetpolarizationions { // XOR check: if (isLambda ^ isAntiLambda){ histos.fill(HIST("GeneralQA/h2dArmenterosFullSelectedNonAmbiguous"), v0.alpha(), v0.qtarm()); - NNonAmbiguous++; + nNonAmbiguous++; } // int lambdaIdx = -1; // No need to pass armenteros if (isLambda && isAntiLambda) { - NAmbiguous++; + nAmbiguous++; // histos.fill(HIST("hAmbiguousLambdaCandidates"), 0); if (doArmenterosQA) histos.fill(HIST("GeneralQA/h2dArmenterosFullSelectedAmbiguous"), v0.alpha(), v0.qtarm()); // To know the discerning power of Armenteros in an Ambiguous Lambda vs AntiLambda case @@ -1913,12 +1942,12 @@ struct lambdajetpolarizationions { // Bookkeeping for the candidates that passed eveything until the ambiguity check: if (isLambda) { // Go back to Lambda bin to fill it as well: - V0SelCounter.advanceTo(lambdaHypoEnd-1); // Bin 41: Ambiguous Lambda rejection - V0SelCounter.fill(); + v0SelCounter.advanceTo(lambdaHypoEnd-1); // Bin 41: Ambiguous Lambda rejection + v0SelCounter.fill(); } if (isAntiLambda) { - V0SelCounter.advanceTo(antiLambdaHypoEnd-1); // Bin 52: Ambiguous antiLambda rejection - V0SelCounter.fill(); + v0SelCounter.advanceTo(antiLambdaHypoEnd-1); // Bin 52: Ambiguous antiLambda rejection + v0SelCounter.fill(); } // Removing ambiguous Lambda candidates for final storage, AFTER some QAing: @@ -1927,17 +1956,15 @@ struct lambdajetpolarizationions { // Updating selection counters after ambiguous candidate rejection: if (isLambda) { - V0SelCounter.advanceTo(lambdaHypoEnd); // Bin 42: Final accepted Lambda V0s - V0SelCounter.fill(); + v0SelCounter.advanceTo(lambdaHypoEnd); // Bin 42: Final accepted Lambda V0s + v0SelCounter.fill(); } else { // i.e., isAntiLambda - V0SelCounter.advanceTo(antiLambdaHypoEnd); // Bin 53: Final accepted anti-Lambda V0s - V0SelCounter.fill(); + v0SelCounter.advanceTo(antiLambdaHypoEnd); // Bin 53: Final accepted anti-Lambda V0s + v0SelCounter.fill(); } // Saving the Lambdas into a derived data column: auto const v0pt = v0.pt(); - const auto posTrackExtra = v0.template posTrack_as(); - const auto negTrackExtra = v0.template negTrack_as(); tableV0s(ringCollIdx, v0pt, v0.eta(), v0.phi(), // Using eta instead of rapidity isLambda, // 0: antiLambda, 1: Lambda. No ambiguous candidates (isLambda && isAntiLambda) are stored @@ -1950,7 +1977,7 @@ struct lambdajetpolarizationions { // Perform extensive QAs for non-ambiguous Lambda/antiLambda candidates only: if (doEventQA && !validV0AlreadyFound) - fillEventSelectionQA(lastBinEvSel - 1, centrality); // hasRingV0 passes (lastBinEvSel would be bin 24, but x=23 for a correct fill in our convention) + fillEventSelectionQA(mBinHasRingV0, centrality); // hasRingV0 passes validV0AlreadyFound = true; if (doV0KinematicQA) { @@ -1987,8 +2014,17 @@ struct lambdajetpolarizationions { } } + // Invariant mass spectra: + if (analyseLambda && isLambda) { + histos.fill(HIST("hMassLambda"), v0.mLambda()); + histos.fill(HIST("Lambda/h3dMassLambda"), centrality, v0pt, v0.mLambda()); + } + if (analyseAntiLambda && isAntiLambda) { + histos.fill(HIST("hMassAntiLambda"), v0.mAntiLambda()); + histos.fill(HIST("AntiLambda/h3dMassAntiLambda"), centrality, v0pt, v0.mAntiLambda()); + } + if (doTopoQA) { - // Remaking these variables outside of the passesLambdaLambdaBarHypothesis. Loses performance, but that should be OK for QA histos.fill(HIST("V0KinematicQA/hPosDCAToPV"), v0.dcapostopv()); histos.fill(HIST("V0KinematicQA/hNegDCAToPV"), v0.dcanegtopv()); histos.fill(HIST("V0KinematicQA/hDCADaughters"), v0.dcaV0daughters()); @@ -1998,9 +2034,7 @@ struct lambdajetpolarizationions { histos.fill(HIST("V0KinematicQA/h2dNegativeITSvsTPCpts"), negTrackExtra.tpcNClsCrossedRows(), negTrackExtra.itsNCls()); histos.fill(HIST("V0KinematicQA/h2dPositivePtVsPhi"), v0.positivept(), computePhiMod(v0.positivephi(), 1)); histos.fill(HIST("V0KinematicQA/h2dNegativePtVsPhi"), v0.negativept(), computePhiMod(v0.negativephi(), -1)); - if (isLambda && analyseLambda) { - histos.fill(HIST("hMassLambda"), v0.mLambda()); - histos.fill(HIST("Lambda/h3dMassLambda"), centrality, v0pt, v0.mLambda()); + if (analyseLambda && isLambda) { histos.fill(HIST("Lambda/hPosDCAToPV"), v0.dcapostopv()); histos.fill(HIST("Lambda/hNegDCAToPV"), v0.dcanegtopv()); histos.fill(HIST("Lambda/hDCADaughters"), v0.dcaV0daughters()); @@ -2041,9 +2075,7 @@ struct lambdajetpolarizationions { } } } - if (isAntiLambda && analyseAntiLambda) { - histos.fill(HIST("hMassAntiLambda"), v0.mAntiLambda()); - histos.fill(HIST("AntiLambda/h3dMassAntiLambda"), centrality, v0pt, v0.mAntiLambda()); + if (analyseAntiLambda && isAntiLambda) { histos.fill(HIST("AntiLambda/hPosDCAToPV"), v0.dcapostopv()); histos.fill(HIST("AntiLambda/hNegDCAToPV"), v0.dcanegtopv()); histos.fill(HIST("AntiLambda/hDCADaughters"), v0.dcaV0daughters()); @@ -2089,17 +2121,17 @@ struct lambdajetpolarizationions { // Fill histograms on a per-event level: if (analyseLambda) { - histos.fill(HIST("Lambda/hLambdasPerEvent"), NLambdas); - histos.fill(HIST("Lambda/h2dNbrOfLambdaVsCentrality"), centrality, NLambdas); + histos.fill(HIST("Lambda/hLambdasPerEvent"), nLambdas); + histos.fill(HIST("Lambda/h2dNbrOfLambdaVsCentrality"), centrality, nLambdas); } if (analyseAntiLambda) { - histos.fill(HIST("AntiLambda/hAntiLambdasPerEvent"), NAntiLambdas); - histos.fill(HIST("AntiLambda/h2dNbrOfAntiLambdaVsCentrality"), centrality, NAntiLambdas); + histos.fill(HIST("AntiLambda/hAntiLambdasPerEvent"), nAntiLambdas); + histos.fill(HIST("AntiLambda/h2dNbrOfAntiLambdaVsCentrality"), centrality, nAntiLambdas); } if (analyseLambda && analyseAntiLambda) { - histos.fill(HIST("hAmbiguousPerEvent"), NAmbiguous); - histos.fill(HIST("hNonAmbiguousPerEvent"), NNonAmbiguous); // The actual - histos.fill(HIST("hLambdasAndAntiLambdasPerEvent"), NLambdas + NAntiLambdas); + histos.fill(HIST("hAmbiguousPerEvent"), nAmbiguous); + histos.fill(HIST("hNonAmbiguousPerEvent"), nNonAmbiguous); + histos.fill(HIST("hLambdasAndAntiLambdasPerEvent"), nLambdas + nAntiLambdas); } } diff --git a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx index d860bcf93ec..44379219658 100644 --- a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx @@ -44,6 +44,7 @@ #include #include #include +#include #include #include @@ -54,6 +55,7 @@ #include #include // For perpendicular jet direction QAs +#include // std::fill, for resetting the Delta Method accumulators #include #include #include @@ -244,28 +246,28 @@ enum CentEstimator { /* =============================== */ \ /* 1D TProfiles vs v0phi */ \ /* =============================== */ \ - X(FOLDER "/QA/pPxStarPhi", v0phiToFillHists, PolStarX) \ - X(FOLDER "/QA/pPyStarPhi", v0phiToFillHists, PolStarY) \ - X(FOLDER "/QA/pPzStarPhi", v0phiToFillHists, PolStarZ) \ + X(FOLDER "/QA/pPxStarPhi", v0phiToFillHists, polStarX) \ + X(FOLDER "/QA/pPyStarPhi", v0phiToFillHists, polStarY) \ + X(FOLDER "/QA/pPzStarPhi", v0phiToFillHists, polStarZ) \ /* =============================== */ \ /* 1D TProfiles vs DeltaPhi_jet */ \ /* =============================== */ \ - X(FOLDER "/QA/pPxStarDeltaPhi", deltaPhiJet, PolStarX) \ - X(FOLDER "/QA/pPyStarDeltaPhi", deltaPhiJet, PolStarY) \ - X(FOLDER "/QA/pPzStarDeltaPhi", deltaPhiJet, PolStarZ) \ + X(FOLDER "/QA/pPxStarDeltaPhi", deltaPhiJet, polStarX) \ + X(FOLDER "/QA/pPyStarDeltaPhi", deltaPhiJet, polStarY) \ + X(FOLDER "/QA/pPzStarDeltaPhi", deltaPhiJet, polStarZ) \ /* =============================== */ \ /* 2D TProfiles vs DeltaPhi_jet and Lambda pT */ \ /* =============================== */ \ - X(FOLDER "/QA/p2dPxStarDeltaPhiVsLambdaPt", deltaPhiJet, v0pt, PolStarX) \ - X(FOLDER "/QA/p2dPyStarDeltaPhiVsLambdaPt", deltaPhiJet, v0pt, PolStarY) \ - X(FOLDER "/QA/p2dPzStarDeltaPhiVsLambdaPt", deltaPhiJet, v0pt, PolStarZ) \ + X(FOLDER "/QA/p2dPxStarDeltaPhiVsLambdaPt", deltaPhiJet, v0pt, polStarX) \ + X(FOLDER "/QA/p2dPyStarDeltaPhiVsLambdaPt", deltaPhiJet, v0pt, polStarY) \ + X(FOLDER "/QA/p2dPzStarDeltaPhiVsLambdaPt", deltaPhiJet, v0pt, polStarZ) \ /* 2D vector-field profiles vs Lambda momentum plane */ \ - X(FOLDER "/QA/p2dPxStar_vsPxPy", v0px, v0py, PolStarX) \ - X(FOLDER "/QA/p2dPyStar_vsPxPy", v0px, v0py, PolStarY) \ - X(FOLDER "/QA/p2dPzStar_vsPxPy", v0px, v0py, PolStarZ) \ - X(FOLDER "/QA/p2dPxStar_vsPzPx", v0pz, v0px, PolStarX) \ - X(FOLDER "/QA/p2dPyStar_vsPzPx", v0pz, v0px, PolStarY) \ - X(FOLDER "/QA/p2dPzStar_vsPzPx", v0pz, v0px, PolStarZ) + X(FOLDER "/QA/p2dPxStar_vsPxPy", v0px, v0py, polStarX) \ + X(FOLDER "/QA/p2dPyStar_vsPxPy", v0px, v0py, polStarY) \ + X(FOLDER "/QA/p2dPzStar_vsPxPy", v0px, v0py, polStarZ) \ + X(FOLDER "/QA/p2dPxStar_vsPzPx", v0pz, v0px, polStarX) \ + X(FOLDER "/QA/p2dPyStar_vsPzPx", v0pz, v0px, polStarY) \ + X(FOLDER "/QA/p2dPzStar_vsPzPx", v0pz, v0px, polStarZ) // Apply the macros (notice I had to include the semicolon (";") after the function, so you don't need to // write that when calling this APPLY_HISTO_FILL. The code will look weird, but without this the compiler @@ -293,18 +295,24 @@ enum CentEstimator { if ((TRACKER).n_int > 0) { \ DELTA_INTEGRATED_FILL_LIST(APPLY_HISTO_FILL, FOLDER, (TRACKER).r_int, (TRACKER).n_int) \ } \ - for (const auto& [bin, r_val] : (TRACKER).r_pt) { \ - int n_val = (TRACKER).n_pt.at(bin); \ + for (size_t bin = 0; bin < (TRACKER).r_pt.size(); ++bin) { \ + int n_val = (TRACKER).n_pt[bin]; \ + if (n_val == 0) continue; \ + double r_val = (TRACKER).r_pt[bin]; \ double center = (AXIS_PT)->GetBinCenter(bin); \ DELTA_2D_FILL_LIST(APPLY_HISTO_FILL, FOLDER, "h2dLambdaPtVsDeltaComp", center, r_val, n_val) \ } \ - for (const auto& [bin, r_val] : (TRACKER).r_mass) { \ - int n_val = (TRACKER).n_mass.at(bin); \ + for (size_t bin = 0; bin < (TRACKER).r_mass.size(); ++bin) { \ + int n_val = (TRACKER).n_mass[bin]; \ + if (n_val == 0) continue; \ + double r_val = (TRACKER).r_mass[bin]; \ double center = (AXIS_MASS)->GetBinCenter(bin); \ DELTA_2D_FILL_LIST(APPLY_HISTO_FILL, FOLDER, "h2dMassVsDeltaComp", center, r_val, n_val) \ } \ - for (const auto& [bin, r_val] : (TRACKER).r_dtheta) { \ - int n_val = (TRACKER).n_dtheta.at(bin); \ + for (size_t bin = 0; bin < (TRACKER).r_dtheta.size(); ++bin) { \ + int n_val = (TRACKER).n_dtheta[bin]; \ + if (n_val == 0) continue; \ + double r_val = (TRACKER).r_dtheta[bin]; \ double center = (AXIS_DTHETA)->GetBinCenter(bin); \ DELTA_2D_FILL_LIST(APPLY_HISTO_FILL, FOLDER, "h2dDeltaThetaVsDeltaComp", center, r_val, n_val) \ } @@ -331,10 +339,16 @@ struct lambdajetpolarizationionsderived { std::string prefix = "familySwitches"; // JSON group name Configurable doFamilyRing{"doFamilyRing", true, "Book and fill the 'Ring' family (no additional cuts). Keep this on for most passes."}; Configurable doFamilyRingKinematicCuts{"doFamilyRingKinematicCuts", false, "Book and fill the 'RingKinematicCuts' family (Lambda kinematic cuts applied)."}; - Configurable doFamilyJetKinematicCuts{"doFamilyJetKinematicCuts", false, "Book and fill the 'JetKinematicCuts' family (jet kinematic cuts applied)."}; - Configurable doFamilyJetAndLambdaKinematicCuts{"doFamilyJetAndLambdaKinematicCuts", true, "Book and fill the 'JetAndLambdaKinematicCuts' family (both cuts applied)."}; + Configurable doFamilyJetKinematicCuts{"doFamilyJetKinematicCuts", true, "Book and fill the 'JetKinematicCuts' family (jet kinematic cuts applied)."}; + Configurable doFamilyJetAndLambdaKinematicCuts{"doFamilyJetAndLambdaKinematicCuts", false, "Book and fill the 'JetAndLambdaKinematicCuts' family (both cuts applied)."}; } familySwitches; + // QA switches: + struct : ConfigurableGroup { + std::string prefix = "qaSwitches"; // JSON group name + Configurable doFakePolDiagnosticsQA{"doFakePolDiagnosticsQA", true, "Book and fill the EtaStudy/ and HelicityEfficiencyQA/ folders."}; // The largest per-V0 fill cost in this task + } qaSwitches; + // Centrality: Configurable centralityEstimator{"centralityEstimator", kCentFT0M, "Run 3 centrality estimator (0:CentFT0C, 1:CentFT0M, 2:CentFV0A)"}; // Default is FT0M Configurable maxZVtxPosition{"maxZVtxPosition", 10., "max Z vtx position [cm]"}; // An additional post-processing cut after derived data was written. Same default as lambdaJetPolarizationIons.cxx producer @@ -400,6 +414,7 @@ struct lambdajetpolarizationionsderived { ConfigurableAxis axisPhi{"axisPhi", {40, 0., constants::math::TwoPI}, "#varphi"}; ConfigurableAxis axisDeltaPhi{"axisDeltaPhi", {40, -constants::math::PI, constants::math::PI}, "#Delta #phi_{jet}"}; ConfigurableAxis axisRingCounts{"axisRingCounts", {90, -4.5, 4.5}, "<#it{R}>"}; + ConfigurableAxis axisDeltaCollisionIndex{"axisDeltaCollisionIndex", {200, -0.5f, 199.5f}, "#Delta collision index"}; // Always positive: SameKindPair pairs strictly upper ConfigurableAxis axisDCAdau{"axisDCAdau", {10, 0., 2.0}, "DCA V0 daughters (cm)"}; ConfigurableAxis axisDCAdauPV{"axisDCAdauPV", {10, 0., 1.2}, "DCA dauPV (cm)"}; // v0Selections.dcav0dau's default maximum is 1.2f in the TableProducer @@ -448,8 +463,21 @@ struct lambdajetpolarizationionsderived { // A small tracker struct for convenience -- Accumulates values for the Delta Method error estimator: struct EventDeltaTracker { double r_int = 0.0; int n_int = 0; - std::map r_pt, r_mass, r_dtheta; - std::map n_pt, n_mass, n_dtheta; + std::vector r_pt, r_mass, r_dtheta; + std::vector n_pt, n_mass, n_dtheta; + + /// \brief Resizes every accumulator. Size includes ROOT's under/overflow bins, so the indices + /// returned by TAxis::FindBin() (0 .. nBins+1) can be used directly for dereferencing here. + void resize(int nBinsPt, int nBinsMass, int nBinsDTheta) { + r_pt.assign(nBinsPt + 2, 0.0); r_mass.assign(nBinsMass + 2, 0.0); r_dtheta.assign(nBinsDTheta + 2, 0.0); + n_pt.assign(nBinsPt + 2, 0); n_mass.assign(nBinsMass + 2, 0); n_dtheta.assign(nBinsDTheta + 2, 0); + } + + void reset() { + r_int = 0.0; n_int = 0; + std::fill(r_pt.begin(), r_pt.end(), 0.0); std::fill(r_mass.begin(), r_mass.end(), 0.0); std::fill(r_dtheta.begin(), r_dtheta.end(), 0.0); + std::fill(n_pt.begin(), n_pt.end(), 0); std::fill(n_mass.begin(), n_mass.end(), 0); std::fill(n_dtheta.begin(), n_dtheta.end(), 0); + } void addV0(double ringObs, int binPt, int binMass, int binDTheta) { r_int += ringObs; n_int += 1; @@ -459,6 +487,9 @@ struct lambdajetpolarizationionsderived { } }; + // Allocating one tracker per family so the accumulators are allocated only once: + EventDeltaTracker trackRing, trackRingKinCuts, trackJetKinCuts, trackJetLambdaKinCuts; + // Axis pointers for Delta Method binning (fetched once in init, declared once here) TAxis* mAxisPt = nullptr; TAxis* mAxisMass = nullptr; @@ -466,6 +497,20 @@ struct lambdajetpolarizationionsderived { void init(InitContext const&) { + // Configuration validation: + // These combinations would not crash otherwise, so they are rejected at init() time. + const int nDistortionsOn = static_cast(fakePolSwitches.forcePerpToJet) + static_cast(fakePolSwitches.forceJetDirectionSmudge) + + static_cast(fakePolSwitches.forceRandJet) + static_cast(fakePolSwitches.forcePreviousJet) + + static_cast(fakePolSwitches.forceDatalikeJet) + static_cast(fakePolSwitches.doMixedEventProxies); + if (nDistortionsOn > 1) // applyProxyDistortion() is an if/else chain, so extra switches are silently ignored + LOG(fatal) << "fakePolSwitches: " << nDistortionsOn << " proxy distortions enabled at once. They are mutually exclusive -- " + << "applyProxyDistortion() would apply only the first and silently drop the rest."; + if (fakePolSwitches.nProxyResamples > 1 && (fakePolSwitches.forcePreviousJet || fakePolSwitches.doMixedEventProxies)) + LOG(fatal) << "fakePolSwitches: nProxyResamples > 1 is only meaningful for forceRandJet/forceDatalikeJet. " + << "Previous-jet/Mixed-Event proxies do not change between resamplings, so every extra pass would double-count the same proxy."; + if (excludeOutOfPeakQA && excludeInPeakQA) // Complementary selections + LOG(fatal) << "excludeOutOfPeakQA and excludeInPeakQA are complementary: enabling both rejects every V0."; + // Ring observable histograms: // Helper to register one full histogram family (kinematic cut variation of ring observable) auto addRingObservableFamily = [&](const std::string& folder) { @@ -755,239 +800,242 @@ struct lambdajetpolarizationionsderived { histos.get(HIST("IntegratedCuts/hCountCutsLeadingP"))->GetXaxis()->SetBinLabel(4, "#Lambda + LeadP cuts"); - // Integrated observable dependent on jet proxy #eta to unfold possible asymmetries in detector: - histos.add("EtaStudy/pRingEtaCuts", "pRingEtaCuts; ;<#it{R}>", kTProfile, {{15, 0, 15}}); - histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(1, "All #Lambda"); - histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(2, "#eta_{Jet} #geq 0"); - histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(3, "#eta_{Jet} < 0"); - histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); - histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); - histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(6, "#eta_{Jet} #geq 0, #eta_{#Lambda} #geq 0"); - histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(7, "#eta_{Jet} #geq 0, #eta_{#Lambda} < 0"); - histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(8, "#eta_{Jet} < 0, #eta_{#Lambda} #geq 0"); - histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(9, "#eta_{Jet} < 0, #eta_{#Lambda} < 0"); - histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(10, "#eta_{Jet} > R"); - histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(11, "#eta_{Jet} < -R"); - histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(12, "#eta_{Jet} > R, #eta_{#Lambda} #geq 0"); - histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(13, "#eta_{Jet} > R, #eta_{#Lambda} < 0"); - histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(14, "#eta_{Jet} < -R, #eta_{#Lambda} #geq 0"); - histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(15, "#eta_{Jet} < -R, #eta_{#Lambda} < 0"); - - histos.add("EtaStudy/pRingEtaCutsSubLeadingJet", "pRingEtaCutsSubLeadingJet; ;<#it{R}>", kTProfile, {{15, 0, 15}}); - histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(1, "All #Lambda"); - histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(2, "#eta_{SubJet} #geq 0"); - histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(3, "#eta_{SubJet} < 0"); - histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); - histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); - histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(6, "#eta_{SubJet} #geq 0, #eta_{#Lambda} #geq 0"); - histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(7, "#eta_{SubJet} #geq 0, #eta_{#Lambda} < 0"); - histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(8, "#eta_{SubJet} < 0, #eta_{#Lambda} #geq 0"); - histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(9, "#eta_{SubJet} < 0, #eta_{#Lambda} < 0"); - histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(10, "#eta_{SubJet} > R"); - histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(11, "#eta_{SubJet} < -R"); - histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(12, "#eta_{SubJet} > R, #eta_{#Lambda} #geq 0"); - histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(13, "#eta_{SubJet} > R, #eta_{#Lambda} < 0"); - histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(14, "#eta_{SubJet} < -R, #eta_{#Lambda} #geq 0"); - histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(15, "#eta_{SubJet} < -R, #eta_{#Lambda} < 0"); - - histos.add("EtaStudy/pRingEtaCutsLeadingP", "pRingEtaCutsLeadingP; ;<#it{R}>", kTProfile, {{9, 0, 9}}); - histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP"))->GetXaxis()->SetBinLabel(1, "All #Lambda"); - histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP"))->GetXaxis()->SetBinLabel(2, "#eta_{LeadP} #geq 0"); - histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP"))->GetXaxis()->SetBinLabel(3, "#eta_{LeadP} < 0"); - histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP"))->GetXaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); - histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP"))->GetXaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); - histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP"))->GetXaxis()->SetBinLabel(6, "#eta_{LeadP} #geq 0, #eta_{#Lambda} #geq 0"); - histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP"))->GetXaxis()->SetBinLabel(7, "#eta_{LeadP} #geq 0, #eta_{#Lambda} < 0"); - histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP"))->GetXaxis()->SetBinLabel(8, "#eta_{LeadP} < 0, #eta_{#Lambda} #geq 0"); - histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP"))->GetXaxis()->SetBinLabel(9, "#eta_{LeadP} < 0, #eta_{#Lambda} < 0"); - - // Studying the signal Vs background integral (a very naive estimative of the invariant mass peak) - histos.add("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground", "pRingEtaCutsLeadingP_MassSignalVsBackground; ; ;<#it{R}>", kTProfile2D, {{9, 0, 9}, {2, 0, 2}}); - histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"))->GetXaxis()->SetBinLabel(1, "All #Lambda"); - histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"))->GetXaxis()->SetBinLabel(2, "#eta_{LeadP} #geq 0"); - histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"))->GetXaxis()->SetBinLabel(3, "#eta_{LeadP} < 0"); - histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"))->GetXaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); - histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"))->GetXaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); - histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"))->GetXaxis()->SetBinLabel(6, "#eta_{LeadP} #geq 0, #eta_{#Lambda} #geq 0"); - histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"))->GetXaxis()->SetBinLabel(7, "#eta_{LeadP} #geq 0, #eta_{#Lambda} < 0"); - histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"))->GetXaxis()->SetBinLabel(8, "#eta_{LeadP} < 0, #eta_{#Lambda} #geq 0"); - histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"))->GetXaxis()->SetBinLabel(9, "#eta_{LeadP} < 0, #eta_{#Lambda} < 0"); - histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"))->GetYaxis()->SetBinLabel(1, "#Lambda out of mass peak"); - histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"))->GetYaxis()->SetBinLabel(2, "#Lambda in mass peak"); - - // Fake polarization signal QA - // --> The "negative helicity problem", where topologies with a proton decaying opposite to the Lambda momentum are enhanced by - // efficiency of reconstruction. The geometries where the proton moves in the same direction as the boost will have a very small - // momentum pion, which is not as easily detected as the opposite case! This may insert a fake signal of polarization in the measurement! - histos.add("EtaStudy/hFakePolCounts", "FakePolCounts; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda};", kTH2D, {axisConfigurations.axisCosTheta, {9, 0, 9}}); - histos.get(HIST("EtaStudy/hFakePolCounts"))->GetZaxis()->SetTitle("N_{V0s}"); - histos.get(HIST("EtaStudy/hFakePolCounts"))->GetYaxis()->SetBinLabel(1, "All #Lambda"); - histos.get(HIST("EtaStudy/hFakePolCounts"))->GetYaxis()->SetBinLabel(2, "#eta_{Jet} #geq 0"); - histos.get(HIST("EtaStudy/hFakePolCounts"))->GetYaxis()->SetBinLabel(3, "#eta_{Jet} < 0"); - histos.get(HIST("EtaStudy/hFakePolCounts"))->GetYaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); - histos.get(HIST("EtaStudy/hFakePolCounts"))->GetYaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); - histos.get(HIST("EtaStudy/hFakePolCounts"))->GetYaxis()->SetBinLabel(6, "#eta_{Jet} #geq 0, #eta_{#Lambda} #geq 0"); - histos.get(HIST("EtaStudy/hFakePolCounts"))->GetYaxis()->SetBinLabel(7, "#eta_{Jet} #geq 0, #eta_{#Lambda} < 0"); - histos.get(HIST("EtaStudy/hFakePolCounts"))->GetYaxis()->SetBinLabel(8, "#eta_{Jet} < 0, #eta_{#Lambda} #geq 0"); - histos.get(HIST("EtaStudy/hFakePolCounts"))->GetYaxis()->SetBinLabel(9, "#eta_{Jet} < 0, #eta_{#Lambda} < 0"); - - // The same, but for actual signal instead of counts: - histos.add("EtaStudy/pFakePolSignalVsCosTheta", "FakePolSignal; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda};", kTProfile2D, {axisConfigurations.axisCosTheta, {9, 0, 9}}); - histos.get(HIST("EtaStudy/pFakePolSignalVsCosTheta"))->GetZaxis()->SetTitle("<#it{R}>"); - histos.get(HIST("EtaStudy/pFakePolSignalVsCosTheta"))->GetYaxis()->SetBinLabel(1, "All #Lambda"); - histos.get(HIST("EtaStudy/pFakePolSignalVsCosTheta"))->GetYaxis()->SetBinLabel(2, "#eta_{Jet} #geq 0"); - histos.get(HIST("EtaStudy/pFakePolSignalVsCosTheta"))->GetYaxis()->SetBinLabel(3, "#eta_{Jet} < 0"); - histos.get(HIST("EtaStudy/pFakePolSignalVsCosTheta"))->GetYaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); - histos.get(HIST("EtaStudy/pFakePolSignalVsCosTheta"))->GetYaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); - histos.get(HIST("EtaStudy/pFakePolSignalVsCosTheta"))->GetYaxis()->SetBinLabel(6, "#eta_{Jet} #geq 0, #eta_{#Lambda} #geq 0"); - histos.get(HIST("EtaStudy/pFakePolSignalVsCosTheta"))->GetYaxis()->SetBinLabel(7, "#eta_{Jet} #geq 0, #eta_{#Lambda} < 0"); - histos.get(HIST("EtaStudy/pFakePolSignalVsCosTheta"))->GetYaxis()->SetBinLabel(8, "#eta_{Jet} < 0, #eta_{#Lambda} #geq 0"); - histos.get(HIST("EtaStudy/pFakePolSignalVsCosTheta"))->GetYaxis()->SetBinLabel(9, "#eta_{Jet} < 0, #eta_{#Lambda} < 0"); - - // Seeing the dependence between phi* = atan2(p_p_star \cdot (p_Lambda_hat \times (z_hat \cross p_Lambda)), p_p_star \cdot (z_hat \cross p_Lambda)) - // e_z = p_Lambda_hat; // e_x = normalize(z_hat cross p_Lambda); // e_y = e_z cross e_x; - // phi_star = atan2(p_p_star dot e_y, p_p_star dot e_x); - histos.add("HelicityEfficiencyQA/hFakePolCounts_CosThetaVsPhiStar", "FakePolCounts; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; #phi^{*}", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisDeltaPhi}); - histos.add("HelicityEfficiencyQA/pFakePolSignal_CosThetaVsPhiStar", "FakePolSignal; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; #phi^{*}", kTProfile2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisDeltaPhi}); - // Specific counter for when we have leading jets (relates directly to pFakePolSignal_CosThetaVsPhiStar): - histos.add("HelicityEfficiencyQA/hFakePolCountsJet_CosThetaVsPhiStar", "FakePolCounts - HasValidLeadJet OK; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; #phi^{*}", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisDeltaPhi}); - - // Similar split, but for AEE instead of HEE: - histos.add("EtaStudy/hCountsVsPhiStar", "FakePolCounts, AEE dependence; #phi^{*};", kTH2D, {axisConfigurations.axisDeltaPhi, {9, 0, 9}}); - histos.get(HIST("EtaStudy/hCountsVsPhiStar"))->GetZaxis()->SetTitle("Counts"); - histos.get(HIST("EtaStudy/hCountsVsPhiStar"))->GetYaxis()->SetBinLabel(1, "All #Lambda"); - histos.get(HIST("EtaStudy/hCountsVsPhiStar"))->GetYaxis()->SetBinLabel(2, "#eta_{Jet} #geq 0"); - histos.get(HIST("EtaStudy/hCountsVsPhiStar"))->GetYaxis()->SetBinLabel(3, "#eta_{Jet} < 0"); - histos.get(HIST("EtaStudy/hCountsVsPhiStar"))->GetYaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); - histos.get(HIST("EtaStudy/hCountsVsPhiStar"))->GetYaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); - histos.get(HIST("EtaStudy/hCountsVsPhiStar"))->GetYaxis()->SetBinLabel(6, "#eta_{Jet} #geq 0, #eta_{#Lambda} #geq 0"); - histos.get(HIST("EtaStudy/hCountsVsPhiStar"))->GetYaxis()->SetBinLabel(7, "#eta_{Jet} #geq 0, #eta_{#Lambda} < 0"); - histos.get(HIST("EtaStudy/hCountsVsPhiStar"))->GetYaxis()->SetBinLabel(8, "#eta_{Jet} < 0, #eta_{#Lambda} #geq 0"); - histos.get(HIST("EtaStudy/hCountsVsPhiStar"))->GetYaxis()->SetBinLabel(9, "#eta_{Jet} < 0, #eta_{#Lambda} < 0"); - // For the ring observable as well: - // Explicitly, checking the Phi* dependence on a series of #eta cuts. - // The fake, AEE-induced, signal should be zero when integrating on full solid angle, and then have some shape for each eta slice. - histos.add("EtaStudy/pFakePolSignalvsPhiStar", "FakePolSignal, AEE dependence; #phi^{*};", kTProfile2D, {axisConfigurations.axisDeltaPhi, {9, 0, 9}}); - histos.get(HIST("EtaStudy/pFakePolSignalvsPhiStar"))->GetZaxis()->SetTitle("<#it{R}>"); - histos.get(HIST("EtaStudy/pFakePolSignalvsPhiStar"))->GetYaxis()->SetBinLabel(1, "All #Lambda"); - histos.get(HIST("EtaStudy/pFakePolSignalvsPhiStar"))->GetYaxis()->SetBinLabel(2, "#eta_{Jet} #geq 0"); - histos.get(HIST("EtaStudy/pFakePolSignalvsPhiStar"))->GetYaxis()->SetBinLabel(3, "#eta_{Jet} < 0"); - histos.get(HIST("EtaStudy/pFakePolSignalvsPhiStar"))->GetYaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); - histos.get(HIST("EtaStudy/pFakePolSignalvsPhiStar"))->GetYaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); - histos.get(HIST("EtaStudy/pFakePolSignalvsPhiStar"))->GetYaxis()->SetBinLabel(6, "#eta_{Jet} #geq 0, #eta_{#Lambda} #geq 0"); - histos.get(HIST("EtaStudy/pFakePolSignalvsPhiStar"))->GetYaxis()->SetBinLabel(7, "#eta_{Jet} #geq 0, #eta_{#Lambda} < 0"); - histos.get(HIST("EtaStudy/pFakePolSignalvsPhiStar"))->GetYaxis()->SetBinLabel(8, "#eta_{Jet} < 0, #eta_{#Lambda} #geq 0"); - histos.get(HIST("EtaStudy/pFakePolSignalvsPhiStar"))->GetYaxis()->SetBinLabel(9, "#eta_{Jet} < 0, #eta_{#Lambda} < 0"); - - // For the phi_Lambda - phi_D^* dependency as well: - histos.add("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar", "FakePolSignal, AEE dependence; #phi_{#Lambda} - #phi_{p}^{*};", kTProfile2D, {axisConfigurations.axisDeltaPhi, {9, 0, 9}}); - histos.get(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"))->GetZaxis()->SetTitle("<#it{R}>"); - histos.get(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"))->GetYaxis()->SetBinLabel(1, "All #Lambda"); - histos.get(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"))->GetYaxis()->SetBinLabel(2, "#eta_{Jet} #geq 0"); - histos.get(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"))->GetYaxis()->SetBinLabel(3, "#eta_{Jet} < 0"); - histos.get(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"))->GetYaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); - histos.get(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"))->GetYaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); - histos.get(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"))->GetYaxis()->SetBinLabel(6, "#eta_{Jet} #geq 0, #eta_{#Lambda} #geq 0"); - histos.get(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"))->GetYaxis()->SetBinLabel(7, "#eta_{Jet} #geq 0, #eta_{#Lambda} < 0"); - histos.get(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"))->GetYaxis()->SetBinLabel(8, "#eta_{Jet} < 0, #eta_{#Lambda} #geq 0"); - histos.get(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"))->GetYaxis()->SetBinLabel(9, "#eta_{Jet} < 0, #eta_{#Lambda} < 0"); - - // More about possible AEE dependencies (should see an invariance with JetEta and /Jz): - // TODO: think about these error bars: do they still make sense via regular TProfile's SEM error? These are just a quick check, so wouldn't bother much about it. - histos.add("HelicityEfficiencyQA/pRingVsJetZcomponent", "<#it{R}> vs #hat{t}_{z}; #hat{t}_{z}; <#it{R}>", kTProfile, {{40, -1, 1}}); // Numerically stable and can also show the sign flip (essentially the <#it{R}> vs Eta Jet plot in another scale) - histos.add("HelicityEfficiencyQA/pRingOverJetZcomponent_VsJetEta", "<#it{R}>/#hat{t}_{z}; #eta_{Jet}; <#it{R}>/#hat{t}_{z}", kTProfile, {axisConfigurations.axisEtaCoarse}); - histos.add("HelicityEfficiencyQA/pRingOverJetZcomponent_VsCosThetaHEE", "<#it{R}>/#hat{t}_{z} Vs cos(#theta) HEE; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; <#it{R}>/#hat{t}_{z}", kTProfile, {axisConfigurations.axisCosTheta}); - histos.add("HelicityEfficiencyQA/pRingOverJetZcomponent_VsPhiStar", "<#it{R}>/#hat{t}_{z} Vs #phi^{*}; #phi^{*} = atan2(#vec{p}^{*}_{p} #cdot [#hat{p}_{#Lambda} #times (#hat{z} #times #hat{p}_{#Lambda})] , #vec{p}^{*}_{p} #cdot (#hat{z} #times #hat{p}_{#Lambda})); <#it{R}>/#hat{t}_{z}", kTProfile, {axisConfigurations.axisDeltaPhi}); - histos.add("HelicityEfficiencyQA/pRingOverJetZcomponent_VsJetEtaVsCosThetaHEE", "<#it{R}>/#hat{t}_{z}; #eta_{Jet}; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; <#it{R}>/#hat{t}_{z}", kTProfile2D, {axisConfigurations.axisEtaCoarse, axisConfigurations.axisCosTheta}); - histos.add("HelicityEfficiencyQA/pRingOverJetZcomponent_VsJetEtaVsPhiStar", "<#it{R}>/#hat{t}_{z}; #eta_{Jet}; #phi^{*}; <#it{R}>/#hat{t}_{z}", kTProfile2D, {axisConfigurations.axisEtaCoarse, axisConfigurations.axisDeltaPhi}); - - // Seeing if phi* is indeed influenced by the DCA between daughters: - histos.add("HelicityEfficiencyQA/hFakePolCountsJet_PhiStarVsDCAdau", "FakePolCounts - HasValidLeadJet OK; #phi^{*}; DCA_{V0 Daughters}", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDCAdau}); - histos.add("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAdau", "FakePolSignal; #phi^{*}; DCA_{V0 Daughters}", kTProfile2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDCAdau}); - - // Adding a way to check if the jet eta is positive or negative as well - // (AEE signal could be closer to zero otherwise: phi^* dependency may not make it fall to zero as we are no longer integrating in full solid angle, yet analyzing this other dependency is also important) - histos.add("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAdauVsEtaJet", "FakePolSignal; #phi^{*}; DCA_{V0 Daughters}; #eta_{Jet} sign", kTProfile3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDCAdau, {2, -0.9, 0.9}}); - histos.add("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAdauVsEtaLambda", "FakePolSignal; #phi^{*}; DCA_{V0 Daughters}; #eta_{#Lambda} sign", kTProfile3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDCAdau, {2, -0.9, 0.9}}); - - // Similar checks for dcaPosToPV and dcaNegToPV, which influence AEE the strongest: - histos.add("HelicityEfficiencyQA/hFakePolCountsJet_PhiStarVsDCAProLike", "FakePolCounts - HasValidLeadJet OK; #phi^{*}; DCA_{PosPV}", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDCAdauPV}); - histos.add("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAProLike", "FakePolSignal; #phi^{*}; DCA_{PosPV}", kTProfile2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDCAdauPV}); - histos.add("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAProLikeVsEtaJet", "FakePolSignal; #phi^{*}; DCA_{PosPV}; #eta_{Jet} sign", kTProfile3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDCAdauPV, {2, -0.9, 0.9}}); - histos.add("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAProLikeVsEtaLambda", "FakePolSignal; #phi^{*}; DCA_{PosPV}; #eta_{#Lambda} sign", kTProfile3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDCAdauPV, {2, -0.9, 0.9}}); - - histos.add("HelicityEfficiencyQA/hFakePolCountsJet_PhiStarVsDCAPiLike", "FakePolCounts - HasValidLeadJet OK; #phi^{*}; DCA_{NegPV}", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDCAdauPV}); - histos.add("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAPiLike", "FakePolSignal; #phi^{*}; DCA_{NegPV}", kTProfile2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDCAdauPV}); - histos.add("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAPiLikeVsEtaJet", "FakePolSignal; #phi^{*}; DCA_{NegPV}; #eta_{Jet} sign", kTProfile3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDCAdauPV, {2, -0.9, 0.9}}); - histos.add("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAPiLikeVsEtaLambda", "FakePolSignal; #phi^{*}; DCA_{NegPV}; #eta_{#Lambda} sign", kTProfile3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDCAdauPV, {2, -0.9, 0.9}}); - - // Doing the same HEE study for leading particles: - // (eta_{Jet} may be a bad estimator!) - histos.add("EtaStudy/hFakePolCountsLeadP", "FakePolCounts; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda};", kTH2D, {axisConfigurations.axisCosTheta, {9, 0, 9}}); - histos.get(HIST("EtaStudy/hFakePolCountsLeadP"))->GetZaxis()->SetTitle("N_{V0s}"); - histos.get(HIST("EtaStudy/hFakePolCountsLeadP"))->GetYaxis()->SetBinLabel(1, "All #Lambda"); - histos.get(HIST("EtaStudy/hFakePolCountsLeadP"))->GetYaxis()->SetBinLabel(2, "#eta_{LeadP} #geq 0"); - histos.get(HIST("EtaStudy/hFakePolCountsLeadP"))->GetYaxis()->SetBinLabel(3, "#eta_{LeadP} < 0"); - histos.get(HIST("EtaStudy/hFakePolCountsLeadP"))->GetYaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); - histos.get(HIST("EtaStudy/hFakePolCountsLeadP"))->GetYaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); - histos.get(HIST("EtaStudy/hFakePolCountsLeadP"))->GetYaxis()->SetBinLabel(6, "#eta_{LeadP} #geq 0, #eta_{#Lambda} #geq 0"); - histos.get(HIST("EtaStudy/hFakePolCountsLeadP"))->GetYaxis()->SetBinLabel(7, "#eta_{LeadP} #geq 0, #eta_{#Lambda} < 0"); - histos.get(HIST("EtaStudy/hFakePolCountsLeadP"))->GetYaxis()->SetBinLabel(8, "#eta_{LeadP} < 0, #eta_{#Lambda} #geq 0"); - histos.get(HIST("EtaStudy/hFakePolCountsLeadP"))->GetYaxis()->SetBinLabel(9, "#eta_{LeadP} < 0, #eta_{#Lambda} < 0"); - - // Avoid fake signal by jets boosting the Lambda in its own direction, then modifying efficiency of reconstruction in a similar way: - histos.add("EtaStudy/hFakePolCountsLambdaPtCut", "FakePol,p_{T}^{#Lambda}#in[0.5,1.5]; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda};", kTH2D, {axisConfigurations.axisCosTheta, {9, 0, 9}}); - histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtCut"))->GetZaxis()->SetTitle("N_{V0s}"); - histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtCut"))->GetYaxis()->SetBinLabel(1, "All #Lambda"); - histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtCut"))->GetYaxis()->SetBinLabel(2, "#eta_{Jet} #geq 0"); - histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtCut"))->GetYaxis()->SetBinLabel(3, "#eta_{Jet} < 0"); - histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtCut"))->GetYaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); - histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtCut"))->GetYaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); - histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtCut"))->GetYaxis()->SetBinLabel(6, "#eta_{Jet} #geq 0, #eta_{#Lambda} #geq 0"); - histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtCut"))->GetYaxis()->SetBinLabel(7, "#eta_{Jet} #geq 0, #eta_{#Lambda} < 0"); - histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtCut"))->GetYaxis()->SetBinLabel(8, "#eta_{Jet} < 0, #eta_{#Lambda} #geq 0"); - histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtCut"))->GetYaxis()->SetBinLabel(9, "#eta_{Jet} < 0, #eta_{#Lambda} < 0"); - - // Even stricter cut (also demands rapidity cut stricter than jets, so may see different boosting): - histos.add("EtaStudy/hFakePolCountsLambdaPtYCuts", "FakePol,p_{T}^{#Lambda}#in[0.5,1.5],|y_{#Lambda}|<0.5; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda};", kTH2D, {axisConfigurations.axisCosTheta, {9, 0, 9}}); - histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"))->GetZaxis()->SetTitle("N_{V0s}"); - histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"))->GetYaxis()->SetBinLabel(1, "All #Lambda"); - histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"))->GetYaxis()->SetBinLabel(2, "#eta_{Jet} #geq 0"); - histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"))->GetYaxis()->SetBinLabel(3, "#eta_{Jet} < 0"); - histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"))->GetYaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); - histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"))->GetYaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); - histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"))->GetYaxis()->SetBinLabel(6, "#eta_{Jet} #geq 0, #eta_{#Lambda} #geq 0"); - histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"))->GetYaxis()->SetBinLabel(7, "#eta_{Jet} #geq 0, #eta_{#Lambda} < 0"); - histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"))->GetYaxis()->SetBinLabel(8, "#eta_{Jet} < 0, #eta_{#Lambda} #geq 0"); - histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"))->GetYaxis()->SetBinLabel(9, "#eta_{Jet} < 0, #eta_{#Lambda} < 0"); - - // Another useful quantity -- How much is the fake signal related to the jet's momentum (how much the fake signal is correlated with the Jet-Lambda angular separation): - histos.add("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaJet", "FakePolCounts; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; #Delta#theta_{Jet}", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisDeltaTheta}); - histos.add("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaJetPosEta", "FakePolCounts; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; #Delta#theta_{Jet}", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisDeltaTheta}); - histos.add("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaJetNegEta", "FakePolCounts; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; #Delta#theta_{Jet}", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisDeltaTheta}); - histos.add("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaLeadP", "FakePolCounts; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; #Delta#theta_{LeadP}", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisDeltaTheta}); - histos.add("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaLeadPPosEta", "FakePolCounts; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; #Delta#theta_{LeadP}", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisDeltaTheta}); - histos.add("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaLeadPNegEta", "FakePolCounts; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; #Delta#theta_{LeadP}", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisDeltaTheta}); - - // Understanding the dip at the cos = -1 end: - histos.add("HelicityEfficiencyQA/hFakePolCountsCosThetaVsPtForJets", "FakePolCounts; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; p_{T}^{#Lambda}", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisPtCoarseQA}); - histos.add("HelicityEfficiencyQA/hFakePolCountsCosThetaVsPtForLeadP", "FakePolCounts; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; p_{T}^{#Lambda}", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisPtCoarseQA}); - - // Studying the magnetic field dependence of particle reconstruction efficiency (not magnitude, just sign of field): - // (also for the "negative helicity" problem) - if (analyseMagField) { - histos.add("HelicityEfficiencyQA/hLambdaMassDecayGeomRight", "hLambdaMassDecayGeomRight; m_{Inv}; Counts", kTH1D, {axisConfigurations.axisLambdaMass}); - histos.add("HelicityEfficiencyQA/hLambdaMassDecayGeomLeft", "hLambdaMassDecayGeomLeft; m_{Inv}; Counts", kTH1D, {axisConfigurations.axisLambdaMass}); - histos.add("HelicityEfficiencyQA/hAntiLambdaMassDecayGeomRight", "hAntiLambdaMassDecayGeomRight; m_{Inv}; Counts", kTH1D, {axisConfigurations.axisLambdaMass}); - histos.add("HelicityEfficiencyQA/hAntiLambdaMassDecayGeomLeft", "hAntiLambdaMassDecayGeomLeft; m_{Inv}; Counts", kTH1D, {axisConfigurations.axisLambdaMass}); - } + // Fake-polarization diagnostics: + if (qaSwitches.doFakePolDiagnosticsQA) { + // Integrated observable dependent on jet proxy #eta to unfold possible asymmetries in detector: + histos.add("EtaStudy/pRingEtaCuts", "pRingEtaCuts; ;<#it{R}>", kTProfile, {{15, 0, 15}}); + histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(1, "All #Lambda"); + histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(2, "#eta_{Jet} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(3, "#eta_{Jet} < 0"); + histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(6, "#eta_{Jet} #geq 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(7, "#eta_{Jet} #geq 0, #eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(8, "#eta_{Jet} < 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(9, "#eta_{Jet} < 0, #eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(10, "#eta_{Jet} > R"); + histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(11, "#eta_{Jet} < -R"); + histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(12, "#eta_{Jet} > R, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(13, "#eta_{Jet} > R, #eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(14, "#eta_{Jet} < -R, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(15, "#eta_{Jet} < -R, #eta_{#Lambda} < 0"); + + histos.add("EtaStudy/pRingEtaCutsSubLeadingJet", "pRingEtaCutsSubLeadingJet; ;<#it{R}>", kTProfile, {{15, 0, 15}}); + histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(1, "All #Lambda"); + histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(2, "#eta_{SubJet} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(3, "#eta_{SubJet} < 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(6, "#eta_{SubJet} #geq 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(7, "#eta_{SubJet} #geq 0, #eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(8, "#eta_{SubJet} < 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(9, "#eta_{SubJet} < 0, #eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(10, "#eta_{SubJet} > R"); + histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(11, "#eta_{SubJet} < -R"); + histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(12, "#eta_{SubJet} > R, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(13, "#eta_{SubJet} > R, #eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(14, "#eta_{SubJet} < -R, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(15, "#eta_{SubJet} < -R, #eta_{#Lambda} < 0"); + + histos.add("EtaStudy/pRingEtaCutsLeadingP", "pRingEtaCutsLeadingP; ;<#it{R}>", kTProfile, {{9, 0, 9}}); + histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP"))->GetXaxis()->SetBinLabel(1, "All #Lambda"); + histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP"))->GetXaxis()->SetBinLabel(2, "#eta_{LeadP} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP"))->GetXaxis()->SetBinLabel(3, "#eta_{LeadP} < 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP"))->GetXaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP"))->GetXaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP"))->GetXaxis()->SetBinLabel(6, "#eta_{LeadP} #geq 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP"))->GetXaxis()->SetBinLabel(7, "#eta_{LeadP} #geq 0, #eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP"))->GetXaxis()->SetBinLabel(8, "#eta_{LeadP} < 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP"))->GetXaxis()->SetBinLabel(9, "#eta_{LeadP} < 0, #eta_{#Lambda} < 0"); + + // Studying the signal Vs background integral (a very naive estimative of the invariant mass peak) + histos.add("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground", "pRingEtaCutsLeadingP_MassSignalVsBackground; ; ;<#it{R}>", kTProfile2D, {{9, 0, 9}, {2, 0, 2}}); + histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"))->GetXaxis()->SetBinLabel(1, "All #Lambda"); + histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"))->GetXaxis()->SetBinLabel(2, "#eta_{LeadP} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"))->GetXaxis()->SetBinLabel(3, "#eta_{LeadP} < 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"))->GetXaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"))->GetXaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"))->GetXaxis()->SetBinLabel(6, "#eta_{LeadP} #geq 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"))->GetXaxis()->SetBinLabel(7, "#eta_{LeadP} #geq 0, #eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"))->GetXaxis()->SetBinLabel(8, "#eta_{LeadP} < 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"))->GetXaxis()->SetBinLabel(9, "#eta_{LeadP} < 0, #eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"))->GetYaxis()->SetBinLabel(1, "#Lambda out of mass peak"); + histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"))->GetYaxis()->SetBinLabel(2, "#Lambda in mass peak"); + + // Fake polarization signal QA + // --> The "negative helicity problem", where topologies with a proton decaying opposite to the Lambda momentum are enhanced by + // efficiency of reconstruction. The geometries where the proton moves in the same direction as the boost will have a very small + // momentum pion, which is not as easily detected as the opposite case! This may insert a fake signal of polarization in the measurement! + histos.add("EtaStudy/hFakePolCounts", "FakePolCounts; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda};", kTH2D, {axisConfigurations.axisCosTheta, {9, 0, 9}}); + histos.get(HIST("EtaStudy/hFakePolCounts"))->GetZaxis()->SetTitle("N_{V0s}"); + histos.get(HIST("EtaStudy/hFakePolCounts"))->GetYaxis()->SetBinLabel(1, "All #Lambda"); + histos.get(HIST("EtaStudy/hFakePolCounts"))->GetYaxis()->SetBinLabel(2, "#eta_{Jet} #geq 0"); + histos.get(HIST("EtaStudy/hFakePolCounts"))->GetYaxis()->SetBinLabel(3, "#eta_{Jet} < 0"); + histos.get(HIST("EtaStudy/hFakePolCounts"))->GetYaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/hFakePolCounts"))->GetYaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/hFakePolCounts"))->GetYaxis()->SetBinLabel(6, "#eta_{Jet} #geq 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/hFakePolCounts"))->GetYaxis()->SetBinLabel(7, "#eta_{Jet} #geq 0, #eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/hFakePolCounts"))->GetYaxis()->SetBinLabel(8, "#eta_{Jet} < 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/hFakePolCounts"))->GetYaxis()->SetBinLabel(9, "#eta_{Jet} < 0, #eta_{#Lambda} < 0"); + + // The same, but for actual signal instead of counts: + histos.add("EtaStudy/pFakePolSignalVsCosTheta", "FakePolSignal; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda};", kTProfile2D, {axisConfigurations.axisCosTheta, {9, 0, 9}}); + histos.get(HIST("EtaStudy/pFakePolSignalVsCosTheta"))->GetZaxis()->SetTitle("<#it{R}>"); + histos.get(HIST("EtaStudy/pFakePolSignalVsCosTheta"))->GetYaxis()->SetBinLabel(1, "All #Lambda"); + histos.get(HIST("EtaStudy/pFakePolSignalVsCosTheta"))->GetYaxis()->SetBinLabel(2, "#eta_{Jet} #geq 0"); + histos.get(HIST("EtaStudy/pFakePolSignalVsCosTheta"))->GetYaxis()->SetBinLabel(3, "#eta_{Jet} < 0"); + histos.get(HIST("EtaStudy/pFakePolSignalVsCosTheta"))->GetYaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pFakePolSignalVsCosTheta"))->GetYaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/pFakePolSignalVsCosTheta"))->GetYaxis()->SetBinLabel(6, "#eta_{Jet} #geq 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pFakePolSignalVsCosTheta"))->GetYaxis()->SetBinLabel(7, "#eta_{Jet} #geq 0, #eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/pFakePolSignalVsCosTheta"))->GetYaxis()->SetBinLabel(8, "#eta_{Jet} < 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pFakePolSignalVsCosTheta"))->GetYaxis()->SetBinLabel(9, "#eta_{Jet} < 0, #eta_{#Lambda} < 0"); + + // Seeing the dependence between phi* = atan2(p_p_star \cdot (p_Lambda_hat \times (z_hat \cross p_Lambda)), p_p_star \cdot (z_hat \cross p_Lambda)) + // e_z = p_Lambda_hat; // e_x = normalize(z_hat cross p_Lambda); // e_y = e_z cross e_x; + // phi_star = atan2(p_p_star dot e_y, p_p_star dot e_x); + histos.add("HelicityEfficiencyQA/hFakePolCounts_CosThetaVsPhiStar", "FakePolCounts; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; #phi^{*}", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisDeltaPhi}); + histos.add("HelicityEfficiencyQA/pFakePolSignal_CosThetaVsPhiStar", "FakePolSignal; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; #phi^{*}", kTProfile2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisDeltaPhi}); + // Specific counter for when we have leading jets (relates directly to pFakePolSignal_CosThetaVsPhiStar): + histos.add("HelicityEfficiencyQA/hFakePolCountsJet_CosThetaVsPhiStar", "FakePolCounts - HasValidLeadJet OK; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; #phi^{*}", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisDeltaPhi}); + + // Similar split, but for AEE instead of HEE: + histos.add("EtaStudy/hCountsVsPhiStar", "FakePolCounts, AEE dependence; #phi^{*};", kTH2D, {axisConfigurations.axisDeltaPhi, {9, 0, 9}}); + histos.get(HIST("EtaStudy/hCountsVsPhiStar"))->GetZaxis()->SetTitle("Counts"); + histos.get(HIST("EtaStudy/hCountsVsPhiStar"))->GetYaxis()->SetBinLabel(1, "All #Lambda"); + histos.get(HIST("EtaStudy/hCountsVsPhiStar"))->GetYaxis()->SetBinLabel(2, "#eta_{Jet} #geq 0"); + histos.get(HIST("EtaStudy/hCountsVsPhiStar"))->GetYaxis()->SetBinLabel(3, "#eta_{Jet} < 0"); + histos.get(HIST("EtaStudy/hCountsVsPhiStar"))->GetYaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/hCountsVsPhiStar"))->GetYaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/hCountsVsPhiStar"))->GetYaxis()->SetBinLabel(6, "#eta_{Jet} #geq 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/hCountsVsPhiStar"))->GetYaxis()->SetBinLabel(7, "#eta_{Jet} #geq 0, #eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/hCountsVsPhiStar"))->GetYaxis()->SetBinLabel(8, "#eta_{Jet} < 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/hCountsVsPhiStar"))->GetYaxis()->SetBinLabel(9, "#eta_{Jet} < 0, #eta_{#Lambda} < 0"); + // For the ring observable as well: + // Explicitly, checking the Phi* dependence on a series of #eta cuts. + // The fake, AEE-induced, signal should be zero when integrating on full solid angle, and then have some shape for each eta slice. + histos.add("EtaStudy/pFakePolSignalvsPhiStar", "FakePolSignal, AEE dependence; #phi^{*};", kTProfile2D, {axisConfigurations.axisDeltaPhi, {9, 0, 9}}); + histos.get(HIST("EtaStudy/pFakePolSignalvsPhiStar"))->GetZaxis()->SetTitle("<#it{R}>"); + histos.get(HIST("EtaStudy/pFakePolSignalvsPhiStar"))->GetYaxis()->SetBinLabel(1, "All #Lambda"); + histos.get(HIST("EtaStudy/pFakePolSignalvsPhiStar"))->GetYaxis()->SetBinLabel(2, "#eta_{Jet} #geq 0"); + histos.get(HIST("EtaStudy/pFakePolSignalvsPhiStar"))->GetYaxis()->SetBinLabel(3, "#eta_{Jet} < 0"); + histos.get(HIST("EtaStudy/pFakePolSignalvsPhiStar"))->GetYaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pFakePolSignalvsPhiStar"))->GetYaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/pFakePolSignalvsPhiStar"))->GetYaxis()->SetBinLabel(6, "#eta_{Jet} #geq 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pFakePolSignalvsPhiStar"))->GetYaxis()->SetBinLabel(7, "#eta_{Jet} #geq 0, #eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/pFakePolSignalvsPhiStar"))->GetYaxis()->SetBinLabel(8, "#eta_{Jet} < 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pFakePolSignalvsPhiStar"))->GetYaxis()->SetBinLabel(9, "#eta_{Jet} < 0, #eta_{#Lambda} < 0"); + + // For the phi_Lambda - phi_D^* dependency as well: + histos.add("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar", "FakePolSignal, AEE dependence; #phi_{#Lambda} - #phi_{p}^{*};", kTProfile2D, {axisConfigurations.axisDeltaPhi, {9, 0, 9}}); + histos.get(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"))->GetZaxis()->SetTitle("<#it{R}>"); + histos.get(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"))->GetYaxis()->SetBinLabel(1, "All #Lambda"); + histos.get(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"))->GetYaxis()->SetBinLabel(2, "#eta_{Jet} #geq 0"); + histos.get(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"))->GetYaxis()->SetBinLabel(3, "#eta_{Jet} < 0"); + histos.get(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"))->GetYaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"))->GetYaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"))->GetYaxis()->SetBinLabel(6, "#eta_{Jet} #geq 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"))->GetYaxis()->SetBinLabel(7, "#eta_{Jet} #geq 0, #eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"))->GetYaxis()->SetBinLabel(8, "#eta_{Jet} < 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"))->GetYaxis()->SetBinLabel(9, "#eta_{Jet} < 0, #eta_{#Lambda} < 0"); + + // More about possible AEE dependencies (should see an invariance with JetEta and /Jz): + // TODO: think about these error bars: do they still make sense via regular TProfile's SEM error? These are just a quick check, so wouldn't bother much about it. + histos.add("HelicityEfficiencyQA/pRingVsJetZcomponent", "<#it{R}> vs #hat{t}_{z}; #hat{t}_{z}; <#it{R}>", kTProfile, {{40, -1, 1}}); // Numerically stable and can also show the sign flip (essentially the <#it{R}> vs Eta Jet plot in another scale) + histos.add("HelicityEfficiencyQA/pRingOverJetZcomponent_VsJetEta", "<#it{R}>/#hat{t}_{z}; #eta_{Jet}; <#it{R}>/#hat{t}_{z}", kTProfile, {axisConfigurations.axisEtaCoarse}); + histos.add("HelicityEfficiencyQA/pRingOverJetZcomponent_VsCosThetaHEE", "<#it{R}>/#hat{t}_{z} Vs cos(#theta) HEE; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; <#it{R}>/#hat{t}_{z}", kTProfile, {axisConfigurations.axisCosTheta}); + histos.add("HelicityEfficiencyQA/pRingOverJetZcomponent_VsPhiStar", "<#it{R}>/#hat{t}_{z} Vs #phi^{*}; #phi^{*} = atan2(#vec{p}^{*}_{p} #cdot [#hat{p}_{#Lambda} #times (#hat{z} #times #hat{p}_{#Lambda})] , #vec{p}^{*}_{p} #cdot (#hat{z} #times #hat{p}_{#Lambda})); <#it{R}>/#hat{t}_{z}", kTProfile, {axisConfigurations.axisDeltaPhi}); + histos.add("HelicityEfficiencyQA/pRingOverJetZcomponent_VsJetEtaVsCosThetaHEE", "<#it{R}>/#hat{t}_{z}; #eta_{Jet}; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; <#it{R}>/#hat{t}_{z}", kTProfile2D, {axisConfigurations.axisEtaCoarse, axisConfigurations.axisCosTheta}); + histos.add("HelicityEfficiencyQA/pRingOverJetZcomponent_VsJetEtaVsPhiStar", "<#it{R}>/#hat{t}_{z}; #eta_{Jet}; #phi^{*}; <#it{R}>/#hat{t}_{z}", kTProfile2D, {axisConfigurations.axisEtaCoarse, axisConfigurations.axisDeltaPhi}); + + // Seeing if phi* is indeed influenced by the DCA between daughters: + histos.add("HelicityEfficiencyQA/hFakePolCountsJet_PhiStarVsDCAdau", "FakePolCounts - HasValidLeadJet OK; #phi^{*}; DCA_{V0 Daughters}", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDCAdau}); + histos.add("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAdau", "FakePolSignal; #phi^{*}; DCA_{V0 Daughters}", kTProfile2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDCAdau}); + + // Adding a way to check if the jet eta is positive or negative as well + // (AEE signal could be closer to zero otherwise: phi^* dependency may not make it fall to zero as we are no longer integrating in full solid angle, yet analyzing this other dependency is also important) + histos.add("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAdauVsEtaJet", "FakePolSignal; #phi^{*}; DCA_{V0 Daughters}; #eta_{Jet} sign", kTProfile3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDCAdau, {2, -0.9, 0.9}}); + histos.add("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAdauVsEtaLambda", "FakePolSignal; #phi^{*}; DCA_{V0 Daughters}; #eta_{#Lambda} sign", kTProfile3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDCAdau, {2, -0.9, 0.9}}); + + // Similar checks for dcaPosToPV and dcaNegToPV, which influence AEE the strongest: + histos.add("HelicityEfficiencyQA/hFakePolCountsJet_PhiStarVsDCAProLike", "FakePolCounts - HasValidLeadJet OK; #phi^{*}; DCA_{PosPV}", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDCAdauPV}); + histos.add("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAProLike", "FakePolSignal; #phi^{*}; DCA_{PosPV}", kTProfile2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDCAdauPV}); + histos.add("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAProLikeVsEtaJet", "FakePolSignal; #phi^{*}; DCA_{PosPV}; #eta_{Jet} sign", kTProfile3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDCAdauPV, {2, -0.9, 0.9}}); + histos.add("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAProLikeVsEtaLambda", "FakePolSignal; #phi^{*}; DCA_{PosPV}; #eta_{#Lambda} sign", kTProfile3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDCAdauPV, {2, -0.9, 0.9}}); + + histos.add("HelicityEfficiencyQA/hFakePolCountsJet_PhiStarVsDCAPiLike", "FakePolCounts - HasValidLeadJet OK; #phi^{*}; DCA_{NegPV}", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDCAdauPV}); + histos.add("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAPiLike", "FakePolSignal; #phi^{*}; DCA_{NegPV}", kTProfile2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDCAdauPV}); + histos.add("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAPiLikeVsEtaJet", "FakePolSignal; #phi^{*}; DCA_{NegPV}; #eta_{Jet} sign", kTProfile3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDCAdauPV, {2, -0.9, 0.9}}); + histos.add("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAPiLikeVsEtaLambda", "FakePolSignal; #phi^{*}; DCA_{NegPV}; #eta_{#Lambda} sign", kTProfile3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDCAdauPV, {2, -0.9, 0.9}}); + + // Doing the same HEE study for leading particles: + // (eta_{Jet} may be a bad estimator!) + histos.add("EtaStudy/hFakePolCountsLeadP", "FakePolCounts; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda};", kTH2D, {axisConfigurations.axisCosTheta, {9, 0, 9}}); + histos.get(HIST("EtaStudy/hFakePolCountsLeadP"))->GetZaxis()->SetTitle("N_{V0s}"); + histos.get(HIST("EtaStudy/hFakePolCountsLeadP"))->GetYaxis()->SetBinLabel(1, "All #Lambda"); + histos.get(HIST("EtaStudy/hFakePolCountsLeadP"))->GetYaxis()->SetBinLabel(2, "#eta_{LeadP} #geq 0"); + histos.get(HIST("EtaStudy/hFakePolCountsLeadP"))->GetYaxis()->SetBinLabel(3, "#eta_{LeadP} < 0"); + histos.get(HIST("EtaStudy/hFakePolCountsLeadP"))->GetYaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/hFakePolCountsLeadP"))->GetYaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/hFakePolCountsLeadP"))->GetYaxis()->SetBinLabel(6, "#eta_{LeadP} #geq 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/hFakePolCountsLeadP"))->GetYaxis()->SetBinLabel(7, "#eta_{LeadP} #geq 0, #eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/hFakePolCountsLeadP"))->GetYaxis()->SetBinLabel(8, "#eta_{LeadP} < 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/hFakePolCountsLeadP"))->GetYaxis()->SetBinLabel(9, "#eta_{LeadP} < 0, #eta_{#Lambda} < 0"); + + // Avoid fake signal by jets boosting the Lambda in its own direction, then modifying efficiency of reconstruction in a similar way: + histos.add("EtaStudy/hFakePolCountsLambdaPtCut", "FakePol,p_{T}^{#Lambda}#in[0.5,1.5]; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda};", kTH2D, {axisConfigurations.axisCosTheta, {9, 0, 9}}); + histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtCut"))->GetZaxis()->SetTitle("N_{V0s}"); + histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtCut"))->GetYaxis()->SetBinLabel(1, "All #Lambda"); + histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtCut"))->GetYaxis()->SetBinLabel(2, "#eta_{Jet} #geq 0"); + histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtCut"))->GetYaxis()->SetBinLabel(3, "#eta_{Jet} < 0"); + histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtCut"))->GetYaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtCut"))->GetYaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtCut"))->GetYaxis()->SetBinLabel(6, "#eta_{Jet} #geq 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtCut"))->GetYaxis()->SetBinLabel(7, "#eta_{Jet} #geq 0, #eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtCut"))->GetYaxis()->SetBinLabel(8, "#eta_{Jet} < 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtCut"))->GetYaxis()->SetBinLabel(9, "#eta_{Jet} < 0, #eta_{#Lambda} < 0"); + + // Even stricter cut (also demands rapidity cut stricter than jets, so may see different boosting): + histos.add("EtaStudy/hFakePolCountsLambdaPtYCuts", "FakePol,p_{T}^{#Lambda}#in[0.5,1.5],|y_{#Lambda}|<0.5; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda};", kTH2D, {axisConfigurations.axisCosTheta, {9, 0, 9}}); + histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"))->GetZaxis()->SetTitle("N_{V0s}"); + histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"))->GetYaxis()->SetBinLabel(1, "All #Lambda"); + histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"))->GetYaxis()->SetBinLabel(2, "#eta_{Jet} #geq 0"); + histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"))->GetYaxis()->SetBinLabel(3, "#eta_{Jet} < 0"); + histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"))->GetYaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"))->GetYaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"))->GetYaxis()->SetBinLabel(6, "#eta_{Jet} #geq 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"))->GetYaxis()->SetBinLabel(7, "#eta_{Jet} #geq 0, #eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"))->GetYaxis()->SetBinLabel(8, "#eta_{Jet} < 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"))->GetYaxis()->SetBinLabel(9, "#eta_{Jet} < 0, #eta_{#Lambda} < 0"); + + // Another useful quantity -- How much is the fake signal related to the jet's momentum (how much the fake signal is correlated with the Jet-Lambda angular separation): + histos.add("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaJet", "FakePolCounts; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; #Delta#theta_{Jet}", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisDeltaTheta}); + histos.add("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaJetPosEta", "FakePolCounts; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; #Delta#theta_{Jet}", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisDeltaTheta}); + histos.add("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaJetNegEta", "FakePolCounts; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; #Delta#theta_{Jet}", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisDeltaTheta}); + histos.add("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaLeadP", "FakePolCounts; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; #Delta#theta_{LeadP}", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisDeltaTheta}); + histos.add("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaLeadPPosEta", "FakePolCounts; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; #Delta#theta_{LeadP}", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisDeltaTheta}); + histos.add("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaLeadPNegEta", "FakePolCounts; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; #Delta#theta_{LeadP}", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisDeltaTheta}); + + // Understanding the dip at the cos = -1 end: + histos.add("HelicityEfficiencyQA/hFakePolCountsCosThetaVsPtForJets", "FakePolCounts; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; p_{T}^{#Lambda}", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisPtCoarseQA}); + histos.add("HelicityEfficiencyQA/hFakePolCountsCosThetaVsPtForLeadP", "FakePolCounts; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; p_{T}^{#Lambda}", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisPtCoarseQA}); + + // Studying the magnetic field dependence of particle reconstruction efficiency (not magnitude, just sign of field): + // (also for the "negative helicity" problem) + if (analyseMagField) { + histos.add("HelicityEfficiencyQA/hLambdaMassDecayGeomRight", "hLambdaMassDecayGeomRight; m_{Inv}; Counts", kTH1D, {axisConfigurations.axisLambdaMass}); + histos.add("HelicityEfficiencyQA/hLambdaMassDecayGeomLeft", "hLambdaMassDecayGeomLeft; m_{Inv}; Counts", kTH1D, {axisConfigurations.axisLambdaMass}); + histos.add("HelicityEfficiencyQA/hAntiLambdaMassDecayGeomRight", "hAntiLambdaMassDecayGeomRight; m_{Inv}; Counts", kTH1D, {axisConfigurations.axisLambdaMass}); + histos.add("HelicityEfficiencyQA/hAntiLambdaMassDecayGeomLeft", "hAntiLambdaMassDecayGeomLeft; m_{Inv}; Counts", kTH1D, {axisConfigurations.axisLambdaMass}); + } - // Also including an observable that probes spectrum broadening due to the "Azimuthal Efficiency Effect": - histos.add("HelicityEfficiencyQA/hLambdaMassVsPhiLambdaMinusPhiProtonStar", "m_{#Lambda}, AEE probe; m_{Inv}; #phi_{#Lambda} - #phi_{p}^{*} ; Counts", kTH2D, {axisConfigurations.axisLambdaMass, axisConfigurations.axisDeltaPhi}); - histos.add("HelicityEfficiencyQA/hAntiLambdaMassVsPhiLambdaMinusPhiProtonStar", "m_{#bar{#Lambda}}, AEE probe; m_{Inv}; #phi_{#bar{#Lambda}} - #phi_{p}^{*} ; Counts", kTH2D, {axisConfigurations.axisLambdaMass, axisConfigurations.axisDeltaPhi}); - // Watching the effect on the ring observable as well: - histos.add("HelicityEfficiencyQA/p2dRing_LambdaMassVsPhiLambdaMinusPhiProtonStar", "<#it{R}>, AEE probe; m_{Inv}; #phi_{#Lambda} - #phi_{p}^{*} ; <#it{R}>", kTProfile2D, {axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisDeltaPhi}); - histos.add("HelicityEfficiencyQA/p2dRing_AntiLambdaMassVsPhiLambdaMinusPhiProtonStar", "<#it{R}>, AEE probe; m_{Inv}; #phi_{#bar{#Lambda}} - #phi_{p}^{*} ; <#it{R}>", kTProfile2D, {axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisDeltaPhi}); + // Also including an observable that probes spectrum broadening due to the "Azimuthal Efficiency Effect": + histos.add("HelicityEfficiencyQA/hLambdaMassVsPhiLambdaMinusPhiProtonStar", "m_{#Lambda}, AEE probe; m_{Inv}; #phi_{#Lambda} - #phi_{p}^{*} ; Counts", kTH2D, {axisConfigurations.axisLambdaMass, axisConfigurations.axisDeltaPhi}); + histos.add("HelicityEfficiencyQA/hAntiLambdaMassVsPhiLambdaMinusPhiProtonStar", "m_{#bar{#Lambda}}, AEE probe; m_{Inv}; #phi_{#bar{#Lambda}} - #phi_{p}^{*} ; Counts", kTH2D, {axisConfigurations.axisLambdaMass, axisConfigurations.axisDeltaPhi}); + // Watching the effect on the ring observable as well: + histos.add("HelicityEfficiencyQA/p2dRing_LambdaMassVsPhiLambdaMinusPhiProtonStar", "<#it{R}>, AEE probe; m_{Inv}; #phi_{#Lambda} - #phi_{p}^{*} ; <#it{R}>", kTProfile2D, {axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisDeltaPhi}); + histos.add("HelicityEfficiencyQA/p2dRing_AntiLambdaMassVsPhiLambdaMinusPhiProtonStar", "<#it{R}>, AEE probe; m_{Inv}; #phi_{#bar{#Lambda}} - #phi_{p}^{*} ; <#it{R}>", kTProfile2D, {axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisDeltaPhi}); + } // end doFakePolDiagnosticsQA bookings // Integrated observable for events with NLambda+NAntiLambda V0s per event // (an interesting measurement of correlation between and Lambda-like V0s multiplicity. A proxy of covariance) @@ -1028,40 +1076,55 @@ struct lambdajetpolarizationionsderived { // doMixedEventProxies QA: gauge the size of the "too few collisions per bin" problem (see resonanceMergeDF.cxx) // Booked per proxy, since the three mixings are independent and can succeed/fail at different rates. if (fakePolSwitches.doMixedEventProxies) { - histos.add("JetKinematicsQA/hMixedEventLeadPOutcome", "hMixedEventLeadPOutcome;Outcome (0=skipped, 1=found);Counts", kTH1D, {{2, -0.5f, 1.5f}}); - histos.add("JetKinematicsQA/hMixedEventLeadJetOutcome", "hMixedEventLeadJetOutcome;Outcome (0=skipped, 1=found);Counts", kTH1D, {{2, -0.5f, 1.5f}}); - histos.add("JetKinematicsQA/hMixedEventSubJetOutcome", "hMixedEventSubJetOutcome;Outcome (0=skipped, 1=found);Counts", kTH1D, {{2, -0.5f, 1.5f}}); - - histos.add("JetKinematicsQA/hMixedEventLeadPWindowNeighbours", "hMixedEventLeadPWindowNeighbours;Neighbours found in bin window;Counts", kTH1D, {{22, -0.5f, 21.5f}}); - histos.add("JetKinematicsQA/hMixedEventLeadJetWindowNeighbours", "hMixedEventLeadJetWindowNeighbours;Neighbours found in bin window;Counts", kTH1D, {{22, -0.5f, 21.5f}}); - histos.add("JetKinematicsQA/hMixedEventSubJetWindowNeighbours", "hMixedEventSubJetWindowNeighbours;Neighbours found in bin window;Counts", kTH1D, {{22, -0.5f, 21.5f}}); - - histos.add("JetKinematicsQA/h2dMixedLeadPEtaVsLeadPEta", "MixedLeadP #eta vs LeadP #eta;MixedLeadP #eta; LeadP #eta;Counts", kTH2D, {axisConfigurations.axisEtaCoarse, axisConfigurations.axisEtaCoarse}); - histos.add("JetKinematicsQA/h2dMixedLeadPPhiVsLeadPPhi", "MixedLeadP #phi vs LeadP #phi;MixedLeadP #phi; LeadP #phi;Counts", kTH2D, {axisConfigurations.axisPhi, axisConfigurations.axisPhi}); - histos.add("JetKinematicsQA/h2dMixedLeadJetEtaVsLeadJetEta", "MixedLeadJet #eta vs LeadJet #eta;MixedLeadJet #eta; LeadJet #eta;Counts", kTH2D, {axisConfigurations.axisEtaCoarse, axisConfigurations.axisEtaCoarse}); - histos.add("JetKinematicsQA/h2dMixedLeadJetPhiVsLeadJetPhi", "MixedLeadJet #phi vs LeadJet #phi;MixedLeadJet #phi; LeadJet #phi;Counts", kTH2D, {axisConfigurations.axisPhi, axisConfigurations.axisPhi}); - histos.add("JetKinematicsQA/h2dMixedSubJetEtaVsSubJetEta", "MixedSubJet #eta vs SubJet #eta;MixedSubJet #eta; SubJet #eta;Counts", kTH2D, {axisConfigurations.axisEtaCoarse, axisConfigurations.axisEtaCoarse}); - histos.add("JetKinematicsQA/h2dMixedSubJetPhiVsSubJetPhi", "MixedSubJet #phi vs SubJet #phi;MixedSubJet #phi; SubJet #phi;Counts", kTH2D, {axisConfigurations.axisPhi, axisConfigurations.axisPhi}); + histos.add("EventMixingQA/hMixedEventLeadPOutcome", "hMixedEventLeadPOutcome;Outcome (0=skipped, 1=found);Counts", kTH1D, {{2, -0.5f, 1.5f}}); + histos.add("EventMixingQA/hMixedEventLeadJetOutcome", "hMixedEventLeadJetOutcome;Outcome (0=skipped, 1=found);Counts", kTH1D, {{2, -0.5f, 1.5f}}); + histos.add("EventMixingQA/hMixedEventSubJetOutcome", "hMixedEventSubJetOutcome;Outcome (0=skipped, 1=found);Counts", kTH1D, {{2, -0.5f, 1.5f}}); + + histos.add("EventMixingQA/hMixedEventLeadPWindowNeighbours", "hMixedEventLeadPWindowNeighbours;Neighbours found in bin window;Counts", kTH1D, {{22, -0.5f, 21.5f}}); + histos.add("EventMixingQA/hMixedEventLeadJetWindowNeighbours", "hMixedEventLeadJetWindowNeighbours;Neighbours found in bin window;Counts", kTH1D, {{22, -0.5f, 21.5f}}); + histos.add("EventMixingQA/hMixedEventSubJetWindowNeighbours", "hMixedEventSubJetWindowNeighbours;Neighbours found in bin window;Counts", kTH1D, {{22, -0.5f, 21.5f}}); + + histos.add("EventMixingQA/h2dMixedLeadPEtaVsLeadPEta", "MixedLeadP #eta vs LeadP #eta;MixedLeadP #eta; LeadP #eta;Counts", kTH2D, {axisConfigurations.axisEtaCoarse, axisConfigurations.axisEtaCoarse}); + histos.add("EventMixingQA/h2dMixedLeadPPhiVsLeadPPhi", "MixedLeadP #phi vs LeadP #phi;MixedLeadP #phi; LeadP #phi;Counts", kTH2D, {axisConfigurations.axisPhi, axisConfigurations.axisPhi}); + histos.add("EventMixingQA/h2dMixedLeadJetEtaVsLeadJetEta", "MixedLeadJet #eta vs LeadJet #eta;MixedLeadJet #eta; LeadJet #eta;Counts", kTH2D, {axisConfigurations.axisEtaCoarse, axisConfigurations.axisEtaCoarse}); + histos.add("EventMixingQA/h2dMixedLeadJetPhiVsLeadJetPhi", "MixedLeadJet #phi vs LeadJet #phi;MixedLeadJet #phi; LeadJet #phi;Counts", kTH2D, {axisConfigurations.axisPhi, axisConfigurations.axisPhi}); + histos.add("EventMixingQA/h2dMixedSubJetEtaVsSubJetEta", "MixedSubJet #eta vs SubJet #eta;MixedSubJet #eta; SubJet #eta;Counts", kTH2D, {axisConfigurations.axisEtaCoarse, axisConfigurations.axisEtaCoarse}); + histos.add("EventMixingQA/h2dMixedSubJetPhiVsSubJetPhi", "MixedSubJet #phi vs SubJet #phi;MixedSubJet #phi; SubJet #phi;Counts", kTH2D, {axisConfigurations.axisPhi, axisConfigurations.axisPhi}); + + // Collision-index proximity QA: + // (To understand possible continous readout effects on the choice of event mixing sources -- do notice index and time are both monotonic, but there is a conversion factor) + // The shape of the whole candidate pool: + histos.add("EventMixingQA/hMixedEventLeadPDeltaIndexEligible", "hMixedEventLeadPDeltaIndexEligible;#Delta collision index (pair);Counts", kTH1D, {axisConfigurations.axisDeltaCollisionIndex}); + histos.add("EventMixingQA/hMixedEventLeadJetDeltaIndexEligible", "hMixedEventLeadJetDeltaIndexEligible;#Delta collision index (pair);Counts", kTH1D, {axisConfigurations.axisDeltaCollisionIndex}); + histos.add("EventMixingQA/hMixedEventSubJetDeltaIndexEligible", "hMixedEventSubJetDeltaIndexEligible;#Delta collision index (pair);Counts", kTH1D, {axisConfigurations.axisDeltaCollisionIndex}); + + // What the reservoir actually picked: + // (should be equally as narrow as the whole candidate pool, if no preferential mixing exists) + histos.add("EventMixingQA/hMixedEventLeadPDeltaIndexSelected", "hMixedEventLeadPDeltaIndexSelected;#Delta collision index (selected partner);Counts", kTH1D, {axisConfigurations.axisDeltaCollisionIndex}); + histos.add("EventMixingQA/hMixedEventLeadJetDeltaIndexSelected", "hMixedEventLeadJetDeltaIndexSelected;#Delta collision index (selected partner);Counts", kTH1D, {axisConfigurations.axisDeltaCollisionIndex}); + histos.add("EventMixingQA/hMixedEventSubJetDeltaIndexSelected", "hMixedEventSubJetDeltaIndexSelected;#Delta collision index (selected partner);Counts", kTH1D, {axisConfigurations.axisDeltaCollisionIndex}); // Event mixing source QA -- How repeated is each collision in the mix: // For each source collision for mixing, counts how many times it was used. Flat distribution is ideal. - histos.add("JetKinematicsQA/hMixedEventLeadPSourceUsageCount", "hMixedEventLeadPSourceUsageCount;Times collision was used;Counts", kTH1D, {{50, -0.5f, 49.5f}}); - histos.add("JetKinematicsQA/hMixedEventLeadJetSourceUsageCount", "hMixedEventLeadJetSourceUsageCount;Times collision was used;Counts", kTH1D, {{50, -0.5f, 49.5f}}); - histos.add("JetKinematicsQA/hMixedEventSubJetSourceUsageCount", "hMixedEventSubJetSourceUsageCount;Times collision was used;Counts", kTH1D, {{50, -0.5f, 49.5f}}); + histos.add("EventMixingQA/hMixedEventLeadPSourceUsageCount", "hMixedEventLeadPSourceUsageCount;Times collision was used;Counts", kTH1D, {{50, -0.5f, 49.5f}}); + histos.add("EventMixingQA/hMixedEventLeadJetSourceUsageCount", "hMixedEventLeadJetSourceUsageCount;Times collision was used;Counts", kTH1D, {{50, -0.5f, 49.5f}}); + histos.add("EventMixingQA/hMixedEventSubJetSourceUsageCount", "hMixedEventSubJetSourceUsageCount;Times collision was used;Counts", kTH1D, {{50, -0.5f, 49.5f}}); // Useful TProfiles -- the mean number of times a given source was used, as a function of eta or phi // (more convenient than a single TH1, as it gives an average, not the raw counter) - histos.add("JetKinematicsQA/pMixedEventLeadPSourceUsageVsEta", "pMixedEventLeadPSourceUsageVsEta;Source #eta;", kTProfile, {axisConfigurations.axisEtaCoarse}); - histos.add("JetKinematicsQA/pMixedEventLeadPSourceUsageVsPhi", "pMixedEventLeadPSourceUsageVsPhi;Source #varphi;", kTProfile, {axisConfigurations.axisPhi}); - histos.add("JetKinematicsQA/pMixedEventLeadJetSourceUsageVsEta", "pMixedEventLeadJetSourceUsageVsEta;Source #eta;", kTProfile, {axisConfigurations.axisEtaCoarse}); - histos.add("JetKinematicsQA/pMixedEventLeadJetSourceUsageVsPhi", "pMixedEventLeadJetSourceUsageVsPhi;Source #varphi;", kTProfile, {axisConfigurations.axisPhi}); - histos.add("JetKinematicsQA/pMixedEventSubJetSourceUsageVsEta", "pMixedEventSubJetSourceUsageVsEta;Source #eta;", kTProfile, {axisConfigurations.axisEtaCoarse}); - histos.add("JetKinematicsQA/pMixedEventSubJetSourceUsageVsPhi", "pMixedEventSubJetSourceUsageVsPhi;Source #varphi;", kTProfile, {axisConfigurations.axisPhi}); + histos.add("EventMixingQA/pMixedEventLeadPSourceUsageVsEta", "pMixedEventLeadPSourceUsageVsEta;Source #eta;", kTProfile, {axisConfigurations.axisEtaCoarse}); + histos.add("EventMixingQA/pMixedEventLeadPSourceUsageVsPhi", "pMixedEventLeadPSourceUsageVsPhi;Source #varphi;", kTProfile, {axisConfigurations.axisPhi}); + histos.add("EventMixingQA/pMixedEventLeadJetSourceUsageVsEta", "pMixedEventLeadJetSourceUsageVsEta;Source #eta;", kTProfile, {axisConfigurations.axisEtaCoarse}); + histos.add("EventMixingQA/pMixedEventLeadJetSourceUsageVsPhi", "pMixedEventLeadJetSourceUsageVsPhi;Source #varphi;", kTProfile, {axisConfigurations.axisPhi}); + histos.add("EventMixingQA/pMixedEventSubJetSourceUsageVsEta", "pMixedEventSubJetSourceUsageVsEta;Source #eta;", kTProfile, {axisConfigurations.axisEtaCoarse}); + histos.add("EventMixingQA/pMixedEventSubJetSourceUsageVsPhi", "pMixedEventSubJetSourceUsageVsPhi;Source #varphi;", kTProfile, {axisConfigurations.axisPhi}); } // Fetch the X-axes from one of the families (since they all share the same ConfigurableAxis binning) mAxisPt = histos.get(HIST("Ring/DeltaMethod/h2dLambdaPtVsDeltaComp"))->GetXaxis(); mAxisMass = histos.get(HIST("Ring/DeltaMethod/h2dMassVsDeltaComp"))->GetXaxis(); mAxisDTheta = histos.get(HIST("Ring/DeltaMethod/h2dDeltaThetaVsDeltaComp"))->GetXaxis(); + for (EventDeltaTracker* tracker : {&trackRing, &trackRingKinCuts, &trackJetKinCuts, &trackJetLambdaKinCuts}) + tracker->resize(mAxisPt->GetNbins(), mAxisMass->GetNbins(), mAxisDTheta->GetNbins()); } // Helper to get centrality (same from TableProducer, thanks to templating!): @@ -1090,19 +1153,27 @@ struct lambdajetpolarizationionsderived { std::discrete_distribution etaLeadPDist{etaLeadPWeights.begin(),etaLeadPWeights.end()}; // Will be passed as the etaDist variable std::discrete_distribution phiLeadPDist{phiLeadPWeights.begin(), phiLeadPWeights.end()}; - /// \brief Applies whichever fakePolSwitches distortion is active to a jet-proxy direction, in place. No-op if none are on. - /// \param hasValidProxy in/out (edited in-place): whether the proxy being distorted is usable. Re-evaluated against minPtThreshold after distortion. - /// \param pt,eta,phi in/out (edited in-place): the proxy's kinematics, overwritten by whichever distortion is active. - /// \param unitVec in/out (edited in-place): the proxy direction as a unit vector. - /// \param cacheHadProxy,cacheEta,cachePhi caches for either forcePreviousJet or doMixedEventProxies. - // forcePreviousJet updates these here for the next collision, in a simplistic event event mixing approach. + /// \brief One jet proxy (leadP, leadJet or subJet). Bound by reference so applyProxyDistortion() edits in-place. + struct ProxyState { + bool& hasValidProxy; //! whether the proxy is valid. Re-evaluated against minPtThreshold after distortion + float& pt; float& eta; float& phi; + XYZVector& unitVec; //! the proxy direction as a unit vector + }; + + /// \brief The cache slots shared by forcePreviousJet and doMixedEventProxies, also bound by reference. + /// \note Fallback skips this event using ProxyState::hasValidProxy. + /// \note forcePreviousJet updates these here for the next collision, in a simplistic event event mixing approach. // doMixedEventProxies instead overwrites the caller's cache fields with this collision's mixed proxy right before the call. - /// Fallback skips this event using hasValidProxy. + struct ProxyCacheRef { bool& hadProxy; float& eta; float& phi; }; + + /// \brief Applies whichever fakePolSwitches distortion is active to a jet-proxy direction, in place. No-op if none are on. + /// \param proxy input/edited in-place: the proxy's kinematics, overwritten by whichever distortion is active. + /// \param minPtThreshold pT threshold for re-evaluating hasValidProxy after distortion. + /// \param cache input/edited in-place: the previous-jet/mixed-event caches. /// \param etaDist,phiDist,rng sampling distributions/generator for forceRandJet and forceDatalikeJet. /// \note Shared across leadP/leadJet/subJet: the caller resolves which proxy-specific procedure (e.g., LUT for evtMixing) applies. // Helper to modify the jet direction for QA and for spurious signal baseline removal tests: - inline void applyProxyDistortion(bool& hasValidProxy, float& pt, float& eta, float& phi, XYZVector& unitVec, - float minPtThreshold, bool& cacheHadProxy, float& cacheEta, float& cachePhi, + inline void applyProxyDistortion(ProxyState proxy, float minPtThreshold, ProxyCacheRef cache, std::discrete_distribution& etaDist, std::discrete_distribution& phiDist, std::mt19937& rng){ if (!fakePolSwitches.forcePerpToJet && !fakePolSwitches.forceJetDirectionSmudge && !fakePolSwitches.forceRandJet && !fakePolSwitches.forcePreviousJet && !fakePolSwitches.forceDatalikeJet && !fakePolSwitches.doMixedEventProxies)[[likely]] { return; // Skip this function if none of the modifications are actually being executed! @@ -1112,16 +1183,16 @@ struct lambdajetpolarizationionsderived { if (fakePolSwitches.forcePerpToJet) { // First, we build a vector perpendicular to the jet by picking an arbitrary vector not parallel to the jet XYZVector perpVec; - if (std::abs(unitVec.X()) > 0.99) { - perpVec = XYZVector(-unitVec.Z(), 0., unitVec.X()).Unit(); // Cross product with Y-axis (0, 1, 0) + if (std::abs(proxy.unitVec.X()) > 0.99) { + perpVec = XYZVector(-proxy.unitVec.Z(), 0., proxy.unitVec.X()).Unit(); // Cross product with Y-axis (0, 1, 0) } else { - perpVec = XYZVector(0., unitVec.Z(), -unitVec.Y()).Unit(); // Cross product with X-axis (1, 0, 0) + perpVec = XYZVector(0., proxy.unitVec.Z(), -proxy.unitVec.Y()).Unit(); // Cross product with X-axis (1, 0, 0) } // Now we rotate around the jet axis by a random angle, just to make sure we are not introducing a bias in the QA: // We will use Rodrigues' rotation formula (v_rot = v*cos(randomAngle) + (Jet \cross v)*sin(randomAngle)) const double randomAngle = randomGen.Uniform(0., o2::constants::math::TwoPI); - unitVec = perpVec * std::cos(randomAngle) + unitVec.Cross(perpVec) * std::sin(randomAngle); + proxy.unitVec = perpVec * std::cos(randomAngle) + proxy.unitVec.Cross(perpVec) * std::sin(randomAngle); } else if (fakePolSwitches.forceJetDirectionSmudge) { // Smear the jet direction by a small random angle to estimate sensitivity to // jet axis uncertainty. We rotate the jet axis by angle theta around a uniformly @@ -1132,16 +1203,16 @@ struct lambdajetpolarizationionsderived { // 1) We pick a uniformly random axis perpendicular to the jet. // (re-using the same Rodrigues formula as in the forcePerpToJet block above) XYZVector perpVec; - if (std::abs(unitVec.X()) > 0.99) { - perpVec = XYZVector(-unitVec.Z(), 0., unitVec.X()).Unit(); // Cross product with Y-axis (0, 1, 0) + if (std::abs(proxy.unitVec.X()) > 0.99) { + perpVec = XYZVector(-proxy.unitVec.Z(), 0., proxy.unitVec.X()).Unit(); // Cross product with Y-axis (0, 1, 0) } else { - perpVec = XYZVector(0., unitVec.Z(), -unitVec.Y()).Unit(); // Cross product with X-axis (1, 0, 0) + perpVec = XYZVector(0., proxy.unitVec.Z(), -proxy.unitVec.Y()).Unit(); // Cross product with X-axis (1, 0, 0) } // Rotate perpVec around the jet axis by a uniform random azimuth to get // a uniformly distributed random perpendicular direction (the smear axis): const double smearAzimuth = randomGen.Uniform(0., o2::constants::math::TwoPI); - XYZVector smearAxis = perpVec * std::cos(smearAzimuth) + unitVec.Cross(perpVec) * std::sin(smearAzimuth); + XYZVector smearAxis = perpVec * std::cos(smearAzimuth) + proxy.unitVec.Cross(perpVec) * std::sin(smearAzimuth); // 2) draw the smearing polar angle from a Gaussian: // sigma = 0.05 * R --> ~68% of events smeared within 5% of R, @@ -1155,7 +1226,7 @@ struct lambdajetpolarizationionsderived { // 3) rotate the jet axis by smearAngle around smearAxis. // Rodrigues is v_rot = v*cos(theta) + (k \cross v)*sin(theta) + k*(k \cdot v)*(1-cos(theta)) // But the last term vanishes because smearAxis is perpendicular to unitVec: - unitVec = unitVec * std::cos(smearAngle) + smearAxis.Cross(unitVec) * std::sin(smearAngle); + proxy.unitVec = proxy.unitVec * std::cos(smearAngle) + smearAxis.Cross(proxy.unitVec) * std::sin(smearAngle); // Also, rotation preserves the norm, so no re-normalisation is needed for this to be a unit vector. } else if (fakePolSwitches.forceRandJet) { // This randomization was made different for each proxy (LeadP, LeadJet, SubLeadJet): bear that in mind! @@ -1166,24 +1237,24 @@ struct lambdajetpolarizationionsderived { const double randPhi = randomGen.Uniform(0., o2::constants::math::TwoPI); // 2) Construct the new random unit vector (there is no need to use the magnitude at all! We only need direction here): - unitVec = XYZVector(sinTheta * std::cos(randPhi), sinTheta * std::sin(randPhi), cosTheta); + proxy.unitVec = XYZVector(sinTheta * std::cos(randPhi), sinTheta * std::sin(randPhi), cosTheta); } else if (fakePolSwitches.forcePreviousJet) { // Use the jet direction from the immediately preceding collision. The simplest event mixing - const bool usableProxy = cacheHadProxy; + const bool usableProxy = cache.hadProxy; if (usableProxy) { - const double inverseCoshEta = 1.0 / std::cosh(cacheEta); - const double sinPhi = std::sin(cachePhi); - const double cosPhi = std::cos(cachePhi); - unitVec = XYZVector(cosPhi * inverseCoshEta, sinPhi * inverseCoshEta, std::tanh(cacheEta)); + const double inverseCoshEta = 1.0 / std::cosh(cache.eta); + const double sinPhi = std::sin(cache.phi); + const double cosPhi = std::cos(cache.phi); + proxy.unitVec = XYZVector(cosPhi * inverseCoshEta, sinPhi * inverseCoshEta, std::tanh(cache.eta)); } // Update cache with the current event data: - cacheHadProxy = hasValidProxy; - cacheEta = eta; - cachePhi = phi; + cache.hadProxy = proxy.hasValidProxy; + cache.eta = proxy.eta; + cache.phi = proxy.phi; // Current event cannot use previous-jet mixing if previous event lacked a proxy if (!usableProxy) - hasValidProxy = false; + proxy.hasValidProxy = false; } else if (fakePolSwitches.forceDatalikeJet){ // A compromise between forceRandJet and forcePreviousJet, using data-like weights for sampling jets const float etaMin = -0.92f; const float etaBinWidth = 0.04f; @@ -1194,53 +1265,53 @@ struct lambdajetpolarizationionsderived { const int binPhiIdx = phiDist(rng); // Uniformly smear inside the chosen bin: - eta = etaMin + etaBinWidth * (binEtaIdx + std::generate_canonical(rng)); - phi = phiBinWidth * (binPhiIdx + std::generate_canonical(rng)); + proxy.eta = etaMin + etaBinWidth * (binEtaIdx + std::generate_canonical(rng)); + proxy.phi = phiBinWidth * (binPhiIdx + std::generate_canonical(rng)); - const double inverseCoshEta = 1.0 / std::cosh(eta); - const double sinPhi = std::sin(phi); - const double cosPhi = std::cos(phi); - unitVec = XYZVector(cosPhi * inverseCoshEta, sinPhi * inverseCoshEta, std::tanh(eta)); + const double inverseCoshEta = 1.0 / std::cosh(proxy.eta); + const double sinPhi = std::sin(proxy.phi); + const double cosPhi = std::cos(proxy.phi); + proxy.unitVec = XYZVector(cosPhi * inverseCoshEta, sinPhi * inverseCoshEta, std::tanh(proxy.eta)); } else if (fakePolSwitches.doMixedEventProxies) { // Mixes this proxy's direction from a real, similar other collision, rather than sampling a fitted distribution as datalikeJet. // Only unitVec is rebuilt, eta/phi are left as this collision's own real values -- same convention forcePreviousJet uses above. - if (cacheHadProxy) { - const double inverseCoshEta = 1.0 / std::cosh(cacheEta); - const double sinPhi = std::sin(cachePhi); - const double cosPhi = std::cos(cachePhi); - unitVec = XYZVector(cosPhi * inverseCoshEta, sinPhi * inverseCoshEta, std::tanh(cacheEta)); + if (cache.hadProxy) { + const double inverseCoshEta = 1.0 / std::cosh(cache.eta); + const double sinPhi = std::sin(cache.phi); + const double cosPhi = std::cos(cache.phi); + proxy.unitVec = XYZVector(cosPhi * inverseCoshEta, sinPhi * inverseCoshEta, std::tanh(cache.eta)); } else { // No mixing partner found for this collision (sparse bins!). // Same skip that forcePreviousJet does above: an event without a valid mixed proxy cannot be used. - hasValidProxy = false; + proxy.hasValidProxy = false; } } // Recalculating pT, phi and eta after distortions: // (without this, later kinematic selections make no sense at all! In the forceRandJet case, the ring observable would always sum zero) - if (hasValidProxy) { // If you don't check this flag here, the "if (!usableProxy)" change would be silently overwritten + if (proxy.hasValidProxy) { // If you don't check this flag here, the "if (!usableProxy)" change would be silently overwritten if (!fakePolSwitches.forcePreviousJet && !fakePolSwitches.forceDatalikeJet){ // In these two cases, we already know the eta and phi variables. No need to recompute // Calculating total jet momentum, which should be preserved, just to recalculate the new jet Pt: - const double mag = pt * std::cosh(eta); + const double mag = proxy.pt * std::cosh(proxy.eta); // For stability (Rho is the projection on the transverse plane, badly behaved for high |eta|): - const double transverseNorm = std::max(unitVec.Rho(), 1e-12); // Stability guard + const double transverseNorm = std::max(proxy.unitVec.Rho(), 1e-12); // Stability guard // Recalculate pT: - pt = mag * transverseNorm; + proxy.pt = mag * transverseNorm; // Recalculate phi: - phi = std::atan2(unitVec.Y(), unitVec.X()); - if (phi < 0.0f) - phi += o2::constants::math::TwoPI; // Notice that atan2 outputs values from -PI to PI, and DataModel convention was from 0 to 2PI as per FastJet's phi() getter + proxy.phi = std::atan2(proxy.unitVec.Y(), proxy.unitVec.X()); + if (proxy.phi < 0.0f) + proxy.phi += o2::constants::math::TwoPI; // Notice that atan2 outputs values from -PI to PI, and DataModel convention was from 0 to 2PI as per FastJet's phi() getter // Stable eta computation: // Stabler than 0.5 * std::log((1. + cosTheta) / (1. - cosTheta)) - eta = std::asinh(unitVec.Z() / transverseNorm); + proxy.eta = std::asinh(proxy.unitVec.Z() / transverseNorm); } // Recalculate the bool after distortion to see if we proceed with this jet proxy: - hasValidProxy = pt > minPtThreshold; + proxy.hasValidProxy = proxy.pt > minPtThreshold; } } @@ -1283,16 +1354,19 @@ struct lambdajetpolarizationionsderived { usage[kv.second.sourceCollisionId]++; return usage; } + + // Defining filters for events: + Filter zvtxFilter = (nabs(o2::aod::RingCollisions::zvtx) < maxZVtxPosition); // Preslices for correct collisions association: - // (TODO: test using custom grouping) + // (tested custom grouping and performs worse here) Preslice perColJets = o2::aod::lambdajetpol::ringCollisionId; Preslice perColV0s = o2::aod::lambdajetpol::ringCollisionId; Preslice perColLeadPs = o2::aod::lambdajetpol::ringCollisionId; // For doMixedEventProxies: SliceCache mixCache; /// \brief Main analysis loop: for each collision, rebuilds the leading jet/particle proxies (with optional fakePolSwitches distortions), then loops over V0s computing the ring observable and polarization-vector profiles for every enabled kinematic-cut family. - void processPolarizationData(o2::aod::RingCollisions const& collisions, o2::aod::RingJets const& jets, o2::aod::RingLaV0s const& v0s, + void processPolarizationData(soa::Filtered const& collisions, o2::aod::RingJets const& jets, o2::aod::RingLaV0s const& v0s, o2::aod::RingLeadPs const& leadPs) { // Event mixing, built once per dataframe @@ -1304,10 +1378,6 @@ struct lambdajetpolarizationionsderived { // Both the main loop and the leadJet/subJet mixing functions read from this, so RingJets is scanned exactly once std::unordered_map jetProxyByCollision; for (auto const& collision : collisions) { - const float collisionPVz = collision.zvtx(); - if (std::abs(collisionPVz) > maxZVtxPosition) // Additional Zvtx filtering - continue; - const auto collId = collision.globalIndex(); JetProxyCache cache; // std::optional avoids undefined behaviour from a default-constructed iterator: @@ -1348,7 +1418,7 @@ struct lambdajetpolarizationionsderived { std::unordered_map mixedSubJetByCollision; // First we build lookup tables based on current dataframe's collisions (connects pairs of jet proxies from similar collisions): // (these proxies may come from collisions with no valid Lambdas, by construction, enabling more mixes) - if (fakePolSwitches.doMixedEventProxies) { + if (fakePolSwitches.doMixedEventProxies) { // This is performed out of the resampling loop, so nProxyResamples will not resample event mixing candidates auto getMixCentrality = [this](o2::aod::RingCollision const& col) { return this->getCentrality(col); }; // For the leading particle: @@ -1377,9 +1447,10 @@ struct lambdajetpolarizationionsderived { // Each side of the pair is one more candidate for the other collision's reservoir: reservoirInsert(leadPCandidateCount, mixedLeadPByCollision, c1.globalIndex(), {eta2, phi2, c2.globalIndex()}); reservoirInsert(leadPCandidateCount, mixedLeadPByCollision, c2.globalIndex(), {eta1, phi1, c1.globalIndex()}); + histos.fill(HIST("EventMixingQA/hMixedEventLeadPDeltaIndexEligible"), c2.globalIndex() - c1.globalIndex()); } if (it.isNewWindow()) { // Count each bin-window once, not once per pair inside it - histos.fill(HIST("JetKinematicsQA/hMixedEventLeadPWindowNeighbours"), it.currentWindowNeighbours()); + histos.fill(HIST("EventMixingQA/hMixedEventLeadPWindowNeighbours"), it.currentWindowNeighbours()); } } @@ -1414,9 +1485,10 @@ struct lambdajetpolarizationionsderived { {cachedLeadJet2->second.leadingJetEta, cachedLeadJet2->second.leadingJetPhi, c2.globalIndex()}); reservoirInsert(leadJetCandidateCount, mixedLeadJetByCollision, c2.globalIndex(), {cachedLeadJet1->second.leadingJetEta, cachedLeadJet1->second.leadingJetPhi, c1.globalIndex()}); + histos.fill(HIST("EventMixingQA/hMixedEventLeadJetDeltaIndexEligible"), c2.globalIndex() - c1.globalIndex()); } if (it.isNewWindow()) { - histos.fill(HIST("JetKinematicsQA/hMixedEventLeadJetWindowNeighbours"), it.currentWindowNeighbours()); + histos.fill(HIST("EventMixingQA/hMixedEventLeadJetWindowNeighbours"), it.currentWindowNeighbours()); } } @@ -1447,48 +1519,57 @@ struct lambdajetpolarizationionsderived { {cachedSubJet2->second.subleadingJetEta, cachedSubJet2->second.subleadingJetPhi, c2.globalIndex()}); reservoirInsert(subJetCandidateCount, mixedSubJetByCollision, c2.globalIndex(), {cachedSubJet1->second.subleadingJetEta, cachedSubJet1->second.subleadingJetPhi, c1.globalIndex()}); + histos.fill(HIST("EventMixingQA/hMixedEventSubJetDeltaIndexEligible"), c2.globalIndex() - c1.globalIndex()); } if (it.isNewWindow()) { - histos.fill(HIST("JetKinematicsQA/hMixedEventSubJetWindowNeighbours"), it.currentWindowNeighbours()); + histos.fill(HIST("EventMixingQA/hMixedEventSubJetWindowNeighbours"), it.currentWindowNeighbours()); } } + // Selected-partner proximity: |target - source| for the partner the reservoir actually kept. + for (auto const& kv : mixedLeadPByCollision) + histos.fill(HIST("EventMixingQA/hMixedEventLeadPDeltaIndexSelected"), std::abs(kv.first - kv.second.sourceCollisionId)); + for (auto const& kv : mixedLeadJetByCollision) + histos.fill(HIST("EventMixingQA/hMixedEventLeadJetDeltaIndexSelected"), std::abs(kv.first - kv.second.sourceCollisionId)); + for (auto const& kv : mixedSubJetByCollision) + histos.fill(HIST("EventMixingQA/hMixedEventSubJetDeltaIndexSelected"), std::abs(kv.first - kv.second.sourceCollisionId)); + // Source-usage QA: // (Per-proxy (HIST() needs literal names, so the fills stay unrolled here) auto leadPUsage = tallySourceUsage(mixedLeadPByCollision); for (auto const& kv : leadPUsage) - histos.fill(HIST("JetKinematicsQA/hMixedEventLeadPSourceUsageCount"), kv.second); + histos.fill(HIST("EventMixingQA/hMixedEventLeadPSourceUsageCount"), kv.second); // Eta/phi of a mixed proxy is stored under the collision that receives the mixing -- reuse the first hit found: for (auto const& kv : mixedLeadPByCollision) { auto usageIt = leadPUsage.find(kv.second.sourceCollisionId); if (usageIt == leadPUsage.end()) // already consumed below continue; - histos.fill(HIST("JetKinematicsQA/pMixedEventLeadPSourceUsageVsEta"), kv.second.eta, usageIt->second); - histos.fill(HIST("JetKinematicsQA/pMixedEventLeadPSourceUsageVsPhi"), kv.second.phi, usageIt->second); + histos.fill(HIST("EventMixingQA/pMixedEventLeadPSourceUsageVsEta"), kv.second.eta, usageIt->second); + histos.fill(HIST("EventMixingQA/pMixedEventLeadPSourceUsageVsPhi"), kv.second.phi, usageIt->second); leadPUsage.erase(usageIt); // Fill this source exactly once, not once per target it supplied } auto leadJetUsage = tallySourceUsage(mixedLeadJetByCollision); for (auto const& kv : leadJetUsage) - histos.fill(HIST("JetKinematicsQA/hMixedEventLeadJetSourceUsageCount"), kv.second); + histos.fill(HIST("EventMixingQA/hMixedEventLeadJetSourceUsageCount"), kv.second); for (auto const& kv : mixedLeadJetByCollision) { auto usageIt = leadJetUsage.find(kv.second.sourceCollisionId); if (usageIt == leadJetUsage.end()) continue; - histos.fill(HIST("JetKinematicsQA/pMixedEventLeadJetSourceUsageVsEta"), kv.second.eta, usageIt->second); - histos.fill(HIST("JetKinematicsQA/pMixedEventLeadJetSourceUsageVsPhi"), kv.second.phi, usageIt->second); + histos.fill(HIST("EventMixingQA/pMixedEventLeadJetSourceUsageVsEta"), kv.second.eta, usageIt->second); + histos.fill(HIST("EventMixingQA/pMixedEventLeadJetSourceUsageVsPhi"), kv.second.phi, usageIt->second); leadJetUsage.erase(usageIt); } auto subJetUsage = tallySourceUsage(mixedSubJetByCollision); for (auto const& kv : subJetUsage) - histos.fill(HIST("JetKinematicsQA/hMixedEventSubJetSourceUsageCount"), kv.second); + histos.fill(HIST("EventMixingQA/hMixedEventSubJetSourceUsageCount"), kv.second); for (auto const& kv : mixedSubJetByCollision) { auto usageIt = subJetUsage.find(kv.second.sourceCollisionId); if (usageIt == subJetUsage.end()) continue; - histos.fill(HIST("JetKinematicsQA/pMixedEventSubJetSourceUsageVsEta"), kv.second.eta, usageIt->second); - histos.fill(HIST("JetKinematicsQA/pMixedEventSubJetSourceUsageVsPhi"), kv.second.phi, usageIt->second); + histos.fill(HIST("EventMixingQA/pMixedEventSubJetSourceUsageVsEta"), kv.second.eta, usageIt->second); + histos.fill(HIST("EventMixingQA/pMixedEventSubJetSourceUsageVsPhi"), kv.second.phi, usageIt->second); subJetUsage.erase(usageIt); } } @@ -1496,16 +1577,13 @@ struct lambdajetpolarizationionsderived { for (int idxResampling = 0; idxResampling < fakePolSwitches.nProxyResamples; idxResampling++){ // resampling loop for forceRandJet and forceDatalikeJet for (auto const& collision : collisions) { const float collisionPVz = collision.zvtx(); - if (std::abs(collisionPVz) > maxZVtxPosition) // Additional Zvtx filtering - continue; const auto collId = collision.globalIndex(); // The self-index accessor const float centrality = getCentrality(collision); - // Fetch magnetic field only if DataModel is in the latest version: - float magField = 1.f; // Used this dummy for backwards compatibility, under the reasonable assumption that the field points always in the same *direction* in the used runs - // if (analyseMagField) - // magField = collision.magField(); + // Used this dummy for backwards compatibility, under the reasonable assumption that the field points always in the same *direction* in the used runs + // (it is not worth it to fetch and store the magnetic field in the datamodel) + float magField = 1.f; // Purely geometric. // Slice jets, V0s and leading particle belonging to this collision: // (global collision indices repeat a lot, but they are unique to a same TimeFrame (TF) subfolder in the derived data) @@ -1546,7 +1624,6 @@ struct lambdajetpolarizationionsderived { leadPPx = lp.leadParticlePx(); leadPPy = lp.leadParticlePy(); leadPPz = lp.leadParticlePz(); - break; } // // Discard events with no leading particle (FastJet didn't even run in these cases!): // if (leadPPt < 0.) @@ -1572,15 +1649,15 @@ struct lambdajetpolarizationionsderived { prevJetCache.leadPEta = itMix->second.eta; prevJetCache.leadPPhi = itMix->second.phi; } - histos.fill(HIST("JetKinematicsQA/hMixedEventLeadPOutcome"), prevJetCache.hadLeadP ? 1 : 0); + histos.fill(HIST("EventMixingQA/hMixedEventLeadPOutcome"), prevJetCache.hadLeadP ? 1 : 0); if (prevJetCache.hadLeadP) { // Only fill the comparison histograms on an actual hit: - histos.fill(HIST("JetKinematicsQA/h2dMixedLeadPEtaVsLeadPEta"), prevJetCache.leadPEta, leadPEta); - histos.fill(HIST("JetKinematicsQA/h2dMixedLeadPPhiVsLeadPPhi"), prevJetCache.leadPPhi, leadPPhi); + histos.fill(HIST("EventMixingQA/h2dMixedLeadPEtaVsLeadPEta"), prevJetCache.leadPEta, leadPEta); + histos.fill(HIST("EventMixingQA/h2dMixedLeadPPhiVsLeadPPhi"), prevJetCache.leadPPhi, leadPPhi); } } - applyProxyDistortion(hasValidLeadingP, leadPPt, leadPEta, leadPPhi, leadPUnitVec, - minLeadParticlePt, prevJetCache.hadLeadP, prevJetCache.leadPEta, prevJetCache.leadPPhi, + applyProxyDistortion({hasValidLeadingP, leadPPt, leadPEta, leadPPhi, leadPUnitVec}, + minLeadParticlePt, {prevJetCache.hadLeadP, prevJetCache.leadPEta, prevJetCache.leadPPhi}, etaLeadPDist, phiLeadPDist, rng); // Fill distorted-proxy QA histograms: @@ -1597,7 +1674,7 @@ struct lambdajetpolarizationionsderived { } // 3) Fetching leading jet and subleading jet -- Resolved once per collision in the jetProxyByCollision pre-pass: - const JetProxyCache& jetProxies = jetProxyByCollision[collId]; + const JetProxyCache& jetProxies = jetProxyByCollision.at(collId); // .at() makes it sure we don't create a new key in the map float leadingJetPt = jetProxies.leadingJetPt; float subleadingJetPt = jetProxies.subleadingJetPt; @@ -1627,14 +1704,14 @@ struct lambdajetpolarizationionsderived { prevJetCache.leadJetEta = itMix->second.eta; prevJetCache.leadJetPhi = itMix->second.phi; } - histos.fill(HIST("JetKinematicsQA/hMixedEventLeadJetOutcome"), prevJetCache.hadLeadJet ? 1 : 0); + histos.fill(HIST("EventMixingQA/hMixedEventLeadJetOutcome"), prevJetCache.hadLeadJet ? 1 : 0); if (prevJetCache.hadLeadJet) { - histos.fill(HIST("JetKinematicsQA/h2dMixedLeadJetEtaVsLeadJetEta"), prevJetCache.leadJetEta, leadingJetEta); - histos.fill(HIST("JetKinematicsQA/h2dMixedLeadJetPhiVsLeadJetPhi"), prevJetCache.leadJetPhi, leadingJetPhi); + histos.fill(HIST("EventMixingQA/h2dMixedLeadJetEtaVsLeadJetEta"), prevJetCache.leadJetEta, leadingJetEta); + histos.fill(HIST("EventMixingQA/h2dMixedLeadJetPhiVsLeadJetPhi"), prevJetCache.leadJetPhi, leadingJetPhi); } } - applyProxyDistortion(hasValidLeadingJet, leadingJetPt, leadingJetEta, leadingJetPhi, leadingJetUnitVec, - minLeadJetPt, prevJetCache.hadLeadJet, prevJetCache.leadJetEta, prevJetCache.leadJetPhi, + applyProxyDistortion({hasValidLeadingJet, leadingJetPt, leadingJetEta, leadingJetPhi, leadingJetUnitVec}, + minLeadJetPt, {prevJetCache.hadLeadJet, prevJetCache.leadJetEta, prevJetCache.leadJetPhi}, etaLeadPDist, phiLeadPDist, rng); // Fill distorted-proxy QA histograms: @@ -1669,14 +1746,14 @@ struct lambdajetpolarizationionsderived { prevJetCache.subJetEta = itMix->second.eta; prevJetCache.subJetPhi = itMix->second.phi; } - histos.fill(HIST("JetKinematicsQA/hMixedEventSubJetOutcome"), prevJetCache.hadSubJet ? 1 : 0); + histos.fill(HIST("EventMixingQA/hMixedEventSubJetOutcome"), prevJetCache.hadSubJet ? 1 : 0); if (prevJetCache.hadSubJet) { - histos.fill(HIST("JetKinematicsQA/h2dMixedSubJetEtaVsSubJetEta"), prevJetCache.subJetEta, subleadingJetEta); - histos.fill(HIST("JetKinematicsQA/h2dMixedSubJetPhiVsSubJetPhi"), prevJetCache.subJetPhi, subleadingJetPhi); + histos.fill(HIST("EventMixingQA/h2dMixedSubJetEtaVsSubJetEta"), prevJetCache.subJetEta, subleadingJetEta); + histos.fill(HIST("EventMixingQA/h2dMixedSubJetPhiVsSubJetPhi"), prevJetCache.subJetPhi, subleadingJetPhi); } } - applyProxyDistortion(hasValidSubJet, subleadingJetPt, subleadingJetEta, subleadingJetPhi, subJetUnitVec, - minSubLeadJetPt, prevJetCache.hadSubJet, prevJetCache.subJetEta, prevJetCache.subJetPhi, + applyProxyDistortion({hasValidSubJet, subleadingJetPt, subleadingJetEta, subleadingJetPhi, subJetUnitVec}, + minSubLeadJetPt, {prevJetCache.hadSubJet, prevJetCache.subJetEta, prevJetCache.subJetPhi}, etaLeadPDist, phiLeadPDist, rng); @@ -1723,8 +1800,9 @@ struct lambdajetpolarizationionsderived { // Code above was superseeded: new datamodel does not store ambiguous candidates! // The new getter comes at the very start of processPolarizationData() now. - // Initialize delta method accumulators: - EventDeltaTracker trackRing, trackRingKinCuts, trackJetKinCuts, trackJetLambdaKinCuts; + // Initialize delta method accumulators (reset for new dataframe): + for (EventDeltaTracker* tracker : {&trackRing, &trackRingKinCuts, &trackJetKinCuts, &trackJetLambdaKinCuts}) + tracker->reset(); for (auto const& v0 : v0sInColl) { const bool isLambda = v0.isLambda(); // true: is a Lambda. false: is an antiLambda. // For now, removing the ambiguous candidates from the analysis. New datamodel does NOT save ambiguous candidates. @@ -1767,6 +1845,12 @@ struct lambdajetpolarizationionsderived { const float lambdaRapidity = lambdaLike4Vec.Rapidity(); // For further kinematic selections const int v0InMassPeak = (v0LambdaLikeMass >= 1.1020593 && v0LambdaLikeMass <= 1.1288811); // Very naive estimator. Based on signal extractions from outside this code + // Inexpensive estimates of signal extraction effects on the observable: + if (excludeOutOfPeakQA && !v0InMassPeak) + continue; + else if (excludeInPeakQA && v0InMassPeak) + continue; + // Boosting proton into lambda frame: XYZVector beta = lambdaLike4Vec.BoostToCM(); // Boost trivector that goes from laboratory frame to Lambda's rest frame (convenient new function, different from TLorentzVector's BoostVector()) auto protonLike4VecStar = ROOT::Math::VectorUtil::boost(protonLike4Vec, beta); @@ -1804,29 +1888,31 @@ struct lambdajetpolarizationionsderived { float pY = protonLikeStarUnit3Vec.Z() - lambdaLikeUnit3Vec.Z() * cosFakePol; // (Reusing cosFakePol calculated earlier!) float phiStar = std::atan2(pY, pX); // This will give an output from -PI to PI - // Another reconstruction efficiency measure: - // (Formula is: p_{Lambda} \cross p_{Daughter}^{*} \cdot B, and B points in Z) - if (analyseMagField) { - auto crossGeom = lambdaLike3Vec.Cross(protonLikeStarUnit3Vec); - const bool positiveGeom = crossGeom.Z() * magField > 0; - - if (isLambda && positiveGeom) - histos.fill(HIST("HelicityEfficiencyQA/hLambdaMassDecayGeomRight"), v0LambdaLikeMass); - else if (isLambda && !positiveGeom) - histos.fill(HIST("HelicityEfficiencyQA/hLambdaMassDecayGeomLeft"), v0LambdaLikeMass); - else if (!isLambda && positiveGeom) - histos.fill(HIST("HelicityEfficiencyQA/hAntiLambdaMassDecayGeomRight"), v0LambdaLikeMass); - else - histos.fill(HIST("HelicityEfficiencyQA/hAntiLambdaMassDecayGeomLeft"), v0LambdaLikeMass); - } + if (qaSwitches.doFakePolDiagnosticsQA) { + // Another reconstruction efficiency measure: + // (Formula is: p_{Lambda} \cross p_{Daughter}^{*} \cdot B, and B points in Z) + if (analyseMagField) { + auto crossGeom = lambdaLike3Vec.Cross(protonLikeStarUnit3Vec); + const bool positiveGeom = crossGeom.Z() * magField > 0; + + if (isLambda && positiveGeom) + histos.fill(HIST("HelicityEfficiencyQA/hLambdaMassDecayGeomRight"), v0LambdaLikeMass); + else if (isLambda && !positiveGeom) + histos.fill(HIST("HelicityEfficiencyQA/hLambdaMassDecayGeomLeft"), v0LambdaLikeMass); + else if (!isLambda && positiveGeom) + histos.fill(HIST("HelicityEfficiencyQA/hAntiLambdaMassDecayGeomRight"), v0LambdaLikeMass); + else + histos.fill(HIST("HelicityEfficiencyQA/hAntiLambdaMassDecayGeomLeft"), v0LambdaLikeMass); + } - // Measuring the AEE effect differentially (azimuthal efficiency effect, which causes different V0 topologies to be enhanced/suppressed): - if (isLambda) - histos.fill(HIST("HelicityEfficiencyQA/hLambdaMassVsPhiLambdaMinusPhiProtonStar"), v0LambdaLikeMass, deltaPhiLambdaProtonStar); - else - histos.fill(HIST("HelicityEfficiencyQA/hAntiLambdaMassVsPhiLambdaMinusPhiProtonStar"), v0LambdaLikeMass, deltaPhiLambdaProtonStar); - // AEE and HEE correlation: - histos.fill(HIST("HelicityEfficiencyQA/hFakePolCounts_CosThetaVsPhiStar"), cosFakePol, phiStar); + // Measuring the AEE effect differentially (azimuthal efficiency effect, which causes different V0 topologies to be enhanced/suppressed): + if (isLambda) + histos.fill(HIST("HelicityEfficiencyQA/hLambdaMassVsPhiLambdaMinusPhiProtonStar"), v0LambdaLikeMass, deltaPhiLambdaProtonStar); + else + histos.fill(HIST("HelicityEfficiencyQA/hAntiLambdaMassVsPhiLambdaMinusPhiProtonStar"), v0LambdaLikeMass, deltaPhiLambdaProtonStar); + // AEE and HEE correlation: + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCounts_CosThetaVsPhiStar"), cosFakePol, phiStar); + } // end doFakePolDiagnosticsQA (per-V0 AEE/HEE) // Useful kinematic bools: const bool lambdaEtaPos = v0eta >= 0.; @@ -1929,15 +2015,15 @@ struct lambdajetpolarizationionsderived { // Calculating polarization observables (in the Lambda frame, because that is easier -- does not require boosts): // To be precise, not actually the polarization, but a part of the summand in P^*_Lambda = (3/\alpha_Lambda) * - float PolStarX = 0, PolStarY = 0, PolStarZ = 0; // Dummy initialization: avoid warnings in compile time + float polStarX = 0, polStarY = 0, polStarZ = 0; // Dummy initialization: avoid warnings in compile time if (isLambda) { // Notice there is no need to check analyseLambda again due to previous checks. - PolStarX = polPrefactorLambda * protonLikeStarUnit3Vec.X(); - PolStarY = polPrefactorLambda * protonLikeStarUnit3Vec.Y(); - PolStarZ = polPrefactorLambda * protonLikeStarUnit3Vec.Z(); + polStarX = polPrefactorLambda * protonLikeStarUnit3Vec.X(); + polStarY = polPrefactorLambda * protonLikeStarUnit3Vec.Y(); + polStarZ = polPrefactorLambda * protonLikeStarUnit3Vec.Z(); } else { - PolStarX = polPrefactorAntiLambda * protonLikeStarUnit3Vec.X(); - PolStarY = polPrefactorAntiLambda * protonLikeStarUnit3Vec.Y(); - PolStarZ = polPrefactorAntiLambda * protonLikeStarUnit3Vec.Z(); + polStarX = polPrefactorAntiLambda * protonLikeStarUnit3Vec.X(); + polStarY = polPrefactorAntiLambda * protonLikeStarUnit3Vec.Y(); + polStarZ = polPrefactorAntiLambda * protonLikeStarUnit3Vec.Z(); } float v0phiToFillHists = wrapToPiFast(v0phi); // A short wrap to reuse some predefined axes @@ -1981,39 +2067,41 @@ struct lambdajetpolarizationionsderived { trackRing.addV0(ringObservable, binPt, binMass, binDTheta); } - // Measuring the AEE differentially (azimuthal efficiency effect, which causes different V0 topologies to be enhanced/suppressed): - if (isLambda) - histos.fill(HIST("HelicityEfficiencyQA/p2dRing_LambdaMassVsPhiLambdaMinusPhiProtonStar"), v0LambdaLikeMass, deltaPhiLambdaProtonStar, ringObservable); - else - histos.fill(HIST("HelicityEfficiencyQA/p2dRing_AntiLambdaMassVsPhiLambdaMinusPhiProtonStar"), v0LambdaLikeMass, deltaPhiLambdaProtonStar, ringObservable); - // AEE and HEE correlation: - histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsJet_CosThetaVsPhiStar"), cosFakePol, phiStar); - histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignal_CosThetaVsPhiStar"), cosFakePol, phiStar, ringObservable); - - // AEE and DCA between daughters correlation: - histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsJet_PhiStarVsDCAdau"), phiStar, dcaDau); - histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAdau"), phiStar, dcaDau, ringObservable); - histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAdauVsEtaJet"), phiStar, dcaDau, leadingJetEta, ringObservable); - histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAdauVsEtaLambda"), phiStar, dcaDau, v0eta, ringObservable); - - // DCA dau to PV correlation: - // For proton-like daughter: - histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsJet_PhiStarVsDCAProLike"), phiStar, protonLikeDCADauToPV); - histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAProLike"), phiStar, protonLikeDCADauToPV, ringObservable); - histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAProLikeVsEtaJet"), phiStar, protonLikeDCADauToPV, leadingJetEta, ringObservable); - histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAProLikeVsEtaLambda"), phiStar, protonLikeDCADauToPV, v0eta, ringObservable); - // For pion-like daughter: - histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsJet_PhiStarVsDCAPiLike"), phiStar, pionLikeDCADauToPV); - histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAPiLike"), phiStar, pionLikeDCADauToPV, ringObservable); - histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAPiLikeVsEtaJet"), phiStar, pionLikeDCADauToPV, leadingJetEta, ringObservable); - histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAPiLikeVsEtaLambda"), phiStar, pionLikeDCADauToPV, v0eta, ringObservable); - - histos.fill(HIST("HelicityEfficiencyQA/pRingVsJetZcomponent"), Jz, ringObservable); - histos.fill(HIST("HelicityEfficiencyQA/pRingOverJetZcomponent_VsJetEta"), leadingJetEta, ringObservableOverJetZ); - histos.fill(HIST("HelicityEfficiencyQA/pRingOverJetZcomponent_VsCosThetaHEE"), cosFakePol, ringObservableOverJetZ); - histos.fill(HIST("HelicityEfficiencyQA/pRingOverJetZcomponent_VsPhiStar"), phiStar, ringObservableOverJetZ); - histos.fill(HIST("HelicityEfficiencyQA/pRingOverJetZcomponent_VsJetEtaVsCosThetaHEE"), leadingJetEta, cosFakePol, ringObservableOverJetZ); - histos.fill(HIST("HelicityEfficiencyQA/pRingOverJetZcomponent_VsJetEtaVsPhiStar"), leadingJetEta, phiStar, ringObservableOverJetZ); + if (qaSwitches.doFakePolDiagnosticsQA) { + // Measuring the AEE differentially (azimuthal efficiency effect, which causes different V0 topologies to be enhanced/suppressed): + if (isLambda) + histos.fill(HIST("HelicityEfficiencyQA/p2dRing_LambdaMassVsPhiLambdaMinusPhiProtonStar"), v0LambdaLikeMass, deltaPhiLambdaProtonStar, ringObservable); + else + histos.fill(HIST("HelicityEfficiencyQA/p2dRing_AntiLambdaMassVsPhiLambdaMinusPhiProtonStar"), v0LambdaLikeMass, deltaPhiLambdaProtonStar, ringObservable); + // AEE and HEE correlation: + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsJet_CosThetaVsPhiStar"), cosFakePol, phiStar); + histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignal_CosThetaVsPhiStar"), cosFakePol, phiStar, ringObservable); + + // AEE and DCA between daughters correlation: + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsJet_PhiStarVsDCAdau"), phiStar, dcaDau); + histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAdau"), phiStar, dcaDau, ringObservable); + histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAdauVsEtaJet"), phiStar, dcaDau, leadingJetEta, ringObservable); + histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAdauVsEtaLambda"), phiStar, dcaDau, v0eta, ringObservable); + + // DCA dau to PV correlation: + // For proton-like daughter: + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsJet_PhiStarVsDCAProLike"), phiStar, protonLikeDCADauToPV); + histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAProLike"), phiStar, protonLikeDCADauToPV, ringObservable); + histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAProLikeVsEtaJet"), phiStar, protonLikeDCADauToPV, leadingJetEta, ringObservable); + histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAProLikeVsEtaLambda"), phiStar, protonLikeDCADauToPV, v0eta, ringObservable); + // For pion-like daughter: + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsJet_PhiStarVsDCAPiLike"), phiStar, pionLikeDCADauToPV); + histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAPiLike"), phiStar, pionLikeDCADauToPV, ringObservable); + histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAPiLikeVsEtaJet"), phiStar, pionLikeDCADauToPV, leadingJetEta, ringObservable); + histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAPiLikeVsEtaLambda"), phiStar, pionLikeDCADauToPV, v0eta, ringObservable); + + histos.fill(HIST("HelicityEfficiencyQA/pRingVsJetZcomponent"), Jz, ringObservable); + histos.fill(HIST("HelicityEfficiencyQA/pRingOverJetZcomponent_VsJetEta"), leadingJetEta, ringObservableOverJetZ); + histos.fill(HIST("HelicityEfficiencyQA/pRingOverJetZcomponent_VsCosThetaHEE"), cosFakePol, ringObservableOverJetZ); + histos.fill(HIST("HelicityEfficiencyQA/pRingOverJetZcomponent_VsPhiStar"), phiStar, ringObservableOverJetZ); + histos.fill(HIST("HelicityEfficiencyQA/pRingOverJetZcomponent_VsJetEtaVsCosThetaHEE"), leadingJetEta, cosFakePol, ringObservableOverJetZ); + histos.fill(HIST("HelicityEfficiencyQA/pRingOverJetZcomponent_VsJetEtaVsPhiStar"), leadingJetEta, phiStar, ringObservableOverJetZ); + } // end doFakePolDiagnosticsQA (leading-jet AEE/HEE) } if (hasValidSubJet) { if (familySwitches.doFamilyRing) { @@ -2023,159 +2111,119 @@ struct lambdajetpolarizationionsderived { histos.fill(HIST("IntegratedCuts/hCountCutsSubLeadingJet"), 0); } - // Filling eta dependence QAs of the result (both for V0 and jet proxy): - if (hasValidLeadingJet) { - histos.fill(HIST("EtaStudy/pRingEtaCuts"), 0, ringObservable); - histos.fill(HIST("EtaStudy/pRingEtaCuts"), lambdaEtaPos ? 3 : 4, ringObservable); - - // HEE study (helicity efficiency effect): - histos.fill(HIST("EtaStudy/hFakePolCounts"), cosFakePol, 0); - histos.fill(HIST("EtaStudy/hFakePolCounts"), cosFakePol, lambdaEtaPos ? 3 : 4); - // Same for signal: - histos.fill(HIST("EtaStudy/pFakePolSignalVsCosTheta"), cosFakePol, 0, ringObservable); - histos.fill(HIST("EtaStudy/pFakePolSignalVsCosTheta"), cosFakePol, lambdaEtaPos ? 3 : 4, ringObservable); - // Counter and ring accumulators for AEE study: - histos.fill(HIST("EtaStudy/hCountsVsPhiStar"), phiStar, 0); - histos.fill(HIST("EtaStudy/hCountsVsPhiStar"), phiStar, lambdaEtaPos ? 3 : 4); - histos.fill(HIST("EtaStudy/pFakePolSignalvsPhiStar"), phiStar, 0, ringObservable); - histos.fill(HIST("EtaStudy/pFakePolSignalvsPhiStar"), phiStar, lambdaEtaPos ? 3 : 4, ringObservable); - - histos.fill(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"), deltaPhiLambdaProtonStar, 0, ringObservable); - histos.fill(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"), deltaPhiLambdaProtonStar, lambdaEtaPos ? 3 : 4, ringObservable); - - // Extra correlations test: - histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaJet"), cosFakePol, deltaThetaJet); - histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsCosThetaVsPtForJets"), cosFakePol, v0pt); - - if (pTLambdaCheck) { - histos.fill(HIST("EtaStudy/hFakePolCountsLambdaPtCut"), cosFakePol, 0); - histos.fill(HIST("EtaStudy/hFakePolCountsLambdaPtCut"), cosFakePol, lambdaEtaPos ? 3 : 4); - if (rapidityLambdaCheck){ // Stricter check - histos.fill(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"), cosFakePol, 0); - histos.fill(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"), cosFakePol, lambdaEtaPos ? 3 : 4); - } - } - if (jetEtaPos) { // Less readable than "if ( jetEtaPos && lambdaEtaPos)", yet more efficient - histos.fill(HIST("EtaStudy/pRingEtaCuts"), 1, ringObservable); - histos.fill(HIST("EtaStudy/pRingEtaCuts"), lambdaEtaPos ? 5 : 6, ringObservable); - - histos.fill(HIST("EtaStudy/hFakePolCounts"), cosFakePol, 1); - histos.fill(HIST("EtaStudy/hFakePolCounts"), cosFakePol, lambdaEtaPos ? 5 : 6); - // Same for signal: - histos.fill(HIST("EtaStudy/pFakePolSignalVsCosTheta"), cosFakePol, 1, ringObservable); - histos.fill(HIST("EtaStudy/pFakePolSignalVsCosTheta"), cosFakePol, lambdaEtaPos ? 5 : 6, ringObservable); - // Counter and ring accumulators for AEE study: - histos.fill(HIST("EtaStudy/hCountsVsPhiStar"), phiStar, 1); - histos.fill(HIST("EtaStudy/hCountsVsPhiStar"), phiStar, lambdaEtaPos ? 5 : 6); - histos.fill(HIST("EtaStudy/pFakePolSignalvsPhiStar"), phiStar, 1, ringObservable); - histos.fill(HIST("EtaStudy/pFakePolSignalvsPhiStar"), phiStar, lambdaEtaPos ? 5 : 6, ringObservable); - - histos.fill(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"), deltaPhiLambdaProtonStar, 1, ringObservable); - histos.fill(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"), deltaPhiLambdaProtonStar, lambdaEtaPos ? 5 : 6, ringObservable); - - histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaJetPosEta"), cosFakePol, deltaThetaJet); - if (pTLambdaCheck) { - histos.fill(HIST("EtaStudy/hFakePolCountsLambdaPtCut"), cosFakePol, 1); - histos.fill(HIST("EtaStudy/hFakePolCountsLambdaPtCut"), cosFakePol, lambdaEtaPos ? 5 : 6); - if (rapidityLambdaCheck){ // Stricter check - histos.fill(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"), cosFakePol, 1); - histos.fill(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"), cosFakePol, lambdaEtaPos ? 5 : 6); - } - } - if (jetEtaStrict) { // eta_{Jet} >= R - histos.fill(HIST("EtaStudy/pRingEtaCuts"), 9, ringObservable); - histos.fill(HIST("EtaStudy/pRingEtaCuts"), lambdaEtaPos ? 11 : 12, ringObservable); - } - } else { - histos.fill(HIST("EtaStudy/pRingEtaCuts"), 2, ringObservable); - histos.fill(HIST("EtaStudy/pRingEtaCuts"), lambdaEtaPos ? 7 : 8, ringObservable); - - histos.fill(HIST("EtaStudy/hFakePolCounts"), cosFakePol, 2); - histos.fill(HIST("EtaStudy/hFakePolCounts"), cosFakePol, lambdaEtaPos ? 7 : 8); + if (qaSwitches.doFakePolDiagnosticsQA) { + // Filling eta dependence QAs of the result (both for V0 and jet proxy): + // Defining shared binning which depend on the V0 only: + const int etaLambdaBin = lambdaEtaPos ? 3 : 4; + if (hasValidLeadingJet) { + histos.fill(HIST("EtaStudy/pRingEtaCuts"), 0, ringObservable); + histos.fill(HIST("EtaStudy/pRingEtaCuts"), etaLambdaBin, ringObservable); + + // Bin indices for this proxy: + // Bin 0: all, 1/2: proxy #eta sign, 3/4: #Lambda #eta sign, 5-8: joint, + // 9/10: |#eta_{proxy}| >= R, 11-14: strict joint. + const int etaProxyBin = jetEtaPos ? 1 : 2; + const int etaProxyLambdaBin = (jetEtaPos ? 5 : 7) + (lambdaEtaPos ? 0 : 1); + const int etaProxyStrictBin = jetEtaPos ? 9 : 10; + const int etaProxyStrictLambdaBin = (jetEtaPos ? 11 : 13) + (lambdaEtaPos ? 0 : 1); + + histos.fill(HIST("EtaStudy/pRingEtaCuts"), etaProxyBin, ringObservable); + histos.fill(HIST("EtaStudy/pRingEtaCuts"), etaProxyLambdaBin, ringObservable); + + // HEE study (helicity efficiency effect): + histos.fill(HIST("EtaStudy/hFakePolCounts"), cosFakePol, 0); + histos.fill(HIST("EtaStudy/hFakePolCounts"), cosFakePol, etaLambdaBin); + histos.fill(HIST("EtaStudy/hFakePolCounts"), cosFakePol, etaProxyBin); + histos.fill(HIST("EtaStudy/hFakePolCounts"), cosFakePol, etaProxyLambdaBin); // Same for signal: - histos.fill(HIST("EtaStudy/pFakePolSignalVsCosTheta"), cosFakePol, 2, ringObservable); - histos.fill(HIST("EtaStudy/pFakePolSignalVsCosTheta"), cosFakePol, lambdaEtaPos ? 7 : 8, ringObservable); + histos.fill(HIST("EtaStudy/pFakePolSignalVsCosTheta"), cosFakePol, 0, ringObservable); + histos.fill(HIST("EtaStudy/pFakePolSignalVsCosTheta"), cosFakePol, etaLambdaBin, ringObservable); + histos.fill(HIST("EtaStudy/pFakePolSignalVsCosTheta"), cosFakePol, etaProxyBin, ringObservable); + histos.fill(HIST("EtaStudy/pFakePolSignalVsCosTheta"), cosFakePol, etaProxyLambdaBin, ringObservable); // Counter and ring accumulators for AEE study: - histos.fill(HIST("EtaStudy/hCountsVsPhiStar"), phiStar, 2); - histos.fill(HIST("EtaStudy/hCountsVsPhiStar"), phiStar, lambdaEtaPos ? 7 : 8); - histos.fill(HIST("EtaStudy/pFakePolSignalvsPhiStar"), phiStar, 2, ringObservable); - histos.fill(HIST("EtaStudy/pFakePolSignalvsPhiStar"), phiStar, lambdaEtaPos ? 7 : 8, ringObservable); - - histos.fill(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"), deltaPhiLambdaProtonStar, 2, ringObservable); - histos.fill(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"), deltaPhiLambdaProtonStar, lambdaEtaPos ? 7 : 8, ringObservable); - - histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaJetNegEta"), cosFakePol, deltaThetaJet); + histos.fill(HIST("EtaStudy/hCountsVsPhiStar"), phiStar, 0); + histos.fill(HIST("EtaStudy/hCountsVsPhiStar"), phiStar, etaLambdaBin); + histos.fill(HIST("EtaStudy/hCountsVsPhiStar"), phiStar, etaProxyBin); + histos.fill(HIST("EtaStudy/hCountsVsPhiStar"), phiStar, etaProxyLambdaBin); + histos.fill(HIST("EtaStudy/pFakePolSignalvsPhiStar"), phiStar, 0, ringObservable); + histos.fill(HIST("EtaStudy/pFakePolSignalvsPhiStar"), phiStar, etaLambdaBin, ringObservable); + histos.fill(HIST("EtaStudy/pFakePolSignalvsPhiStar"), phiStar, etaProxyBin, ringObservable); + histos.fill(HIST("EtaStudy/pFakePolSignalvsPhiStar"), phiStar, etaProxyLambdaBin, ringObservable); + + histos.fill(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"), deltaPhiLambdaProtonStar, 0, ringObservable); + histos.fill(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"), deltaPhiLambdaProtonStar, etaLambdaBin, ringObservable); + histos.fill(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"), deltaPhiLambdaProtonStar, etaProxyBin, ringObservable); + histos.fill(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"), deltaPhiLambdaProtonStar, etaProxyLambdaBin, ringObservable); + + // Extra correlations test: + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaJet"), cosFakePol, deltaThetaJet); + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsCosThetaVsPtForJets"), cosFakePol, v0pt); + // Split by proxy #eta sign: + if (jetEtaPos) + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaJetPosEta"), cosFakePol, deltaThetaJet); + else + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaJetNegEta"), cosFakePol, deltaThetaJet); if (pTLambdaCheck) { - histos.fill(HIST("EtaStudy/hFakePolCountsLambdaPtCut"), cosFakePol, 2); - histos.fill(HIST("EtaStudy/hFakePolCountsLambdaPtCut"), cosFakePol, lambdaEtaPos ? 7 : 8); + histos.fill(HIST("EtaStudy/hFakePolCountsLambdaPtCut"), cosFakePol, 0); + histos.fill(HIST("EtaStudy/hFakePolCountsLambdaPtCut"), cosFakePol, etaLambdaBin); + histos.fill(HIST("EtaStudy/hFakePolCountsLambdaPtCut"), cosFakePol, etaProxyBin); + histos.fill(HIST("EtaStudy/hFakePolCountsLambdaPtCut"), cosFakePol, etaProxyLambdaBin); if (rapidityLambdaCheck){ // Stricter check - histos.fill(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"), cosFakePol, 2); - histos.fill(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"), cosFakePol, lambdaEtaPos ? 7 : 8); + histos.fill(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"), cosFakePol, 0); + histos.fill(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"), cosFakePol, etaLambdaBin); + histos.fill(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"), cosFakePol, etaProxyBin); + histos.fill(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"), cosFakePol, etaProxyLambdaBin); } } - if (jetEtaStrict) { // eta_{Jet} <= -R - histos.fill(HIST("EtaStudy/pRingEtaCuts"), 10, ringObservable); - histos.fill(HIST("EtaStudy/pRingEtaCuts"), lambdaEtaPos ? 13 : 14, ringObservable); + if (jetEtaStrict) { // |eta_{Jet}| >= R + histos.fill(HIST("EtaStudy/pRingEtaCuts"), etaProxyStrictBin, ringObservable); + histos.fill(HIST("EtaStudy/pRingEtaCuts"), etaProxyStrictLambdaBin, ringObservable); } } - } - if (hasValidSubJet) { - histos.fill(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"), 0, ringObservable2ndJet); - histos.fill(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"), lambdaEtaPos ? 3 : 4, ringObservable2ndJet); - if (subJetEtaPos) { - histos.fill(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"), 1, ringObservable2ndJet); - histos.fill(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"), lambdaEtaPos ? 5 : 6, ringObservable2ndJet); - if (subJetEtaStrict) { // eta_{SubJet} >= R - histos.fill(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"), 9, ringObservable2ndJet); - histos.fill(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"), lambdaEtaPos ? 11 : 12, ringObservable2ndJet); - } - } else { - histos.fill(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"), 2, ringObservable2ndJet); - histos.fill(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"), lambdaEtaPos ? 7 : 8, ringObservable2ndJet); - if (subJetEtaStrict) { // eta_{SubJet} <= -R - histos.fill(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"), 10, ringObservable2ndJet); - histos.fill(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"), lambdaEtaPos ? 13 : 14, ringObservable2ndJet); + if (hasValidSubJet) { + // Same bin scheme as the leading jet above: + const int etaProxyBin = subJetEtaPos ? 1 : 2; + const int etaProxyLambdaBin = (subJetEtaPos ? 5 : 7) + (lambdaEtaPos ? 0 : 1); + const int etaProxyStrictBin = subJetEtaPos ? 9 : 10; + const int etaProxyStrictLambdaBin = (subJetEtaPos ? 11 : 13) + (lambdaEtaPos ? 0 : 1); + + histos.fill(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"), 0, ringObservable2ndJet); + histos.fill(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"), etaLambdaBin, ringObservable2ndJet); + histos.fill(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"), etaProxyBin, ringObservable2ndJet); + histos.fill(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"), etaProxyLambdaBin, ringObservable2ndJet); + if (subJetEtaStrict) { // |eta_{SubJet}| >= R + histos.fill(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"), etaProxyStrictBin, ringObservable2ndJet); + histos.fill(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"), etaProxyStrictLambdaBin, ringObservable2ndJet); } } - } - if (hasValidLeadingP) { - histos.fill(HIST("EtaStudy/pRingEtaCutsLeadingP"), 0, ringObservableLeadP); - histos.fill(HIST("EtaStudy/pRingEtaCutsLeadingP"), lambdaEtaPos ? 3 : 4, ringObservableLeadP); - histos.fill(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"), 0, v0InMassPeak, ringObservableLeadP); - histos.fill(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"), lambdaEtaPos ? 3 : 4, v0InMassPeak, ringObservableLeadP); - - histos.fill(HIST("EtaStudy/hFakePolCountsLeadP"), cosFakePol, 0); - histos.fill(HIST("EtaStudy/hFakePolCountsLeadP"), cosFakePol, lambdaEtaPos ? 3 : 4); - - histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsCosThetaVsPtForLeadP"), cosFakePol, v0pt); // Understanding the population of events that has a leading particle (even though this does not need one to be calculated!) - histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaLeadP"), cosFakePol, deltaThetaLeadP); - - // Extra correlations test: - // histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsLeadP"), cosFakePol, deltaThetaLeadP); - if (leadPEtaPos) { - histos.fill(HIST("EtaStudy/pRingEtaCutsLeadingP"), 1, ringObservableLeadP); - histos.fill(HIST("EtaStudy/pRingEtaCutsLeadingP"), lambdaEtaPos ? 5 : 6, ringObservableLeadP); - histos.fill(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"), 1, v0InMassPeak, ringObservableLeadP); - histos.fill(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"), lambdaEtaPos ? 5 : 6, v0InMassPeak, ringObservableLeadP); - - histos.fill(HIST("EtaStudy/hFakePolCountsLeadP"), cosFakePol, 1); - histos.fill(HIST("EtaStudy/hFakePolCountsLeadP"), cosFakePol, lambdaEtaPos ? 5 : 6); - - histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaLeadPPosEta"), cosFakePol, deltaThetaLeadP); - } - else { - histos.fill(HIST("EtaStudy/pRingEtaCutsLeadingP"), 2, ringObservableLeadP); - histos.fill(HIST("EtaStudy/pRingEtaCutsLeadingP"), lambdaEtaPos ? 7 : 8, ringObservableLeadP); - histos.fill(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"), 2, v0InMassPeak, ringObservableLeadP); - histos.fill(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"), lambdaEtaPos ? 7 : 8, v0InMassPeak, ringObservableLeadP); - - histos.fill(HIST("EtaStudy/hFakePolCountsLeadP"), cosFakePol, 2); - histos.fill(HIST("EtaStudy/hFakePolCountsLeadP"), cosFakePol, lambdaEtaPos ? 7 : 8); - - histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaLeadPNegEta"), cosFakePol, deltaThetaLeadP); + if (hasValidLeadingP) { + // Same bin scheme, without the strict variant (this axis stops at 9 bins): + const int etaProxyBin = leadPEtaPos ? 1 : 2; + const int etaProxyLambdaBin = (leadPEtaPos ? 5 : 7) + (lambdaEtaPos ? 0 : 1); + + histos.fill(HIST("EtaStudy/pRingEtaCutsLeadingP"), 0, ringObservableLeadP); + histos.fill(HIST("EtaStudy/pRingEtaCutsLeadingP"), etaLambdaBin, ringObservableLeadP); + histos.fill(HIST("EtaStudy/pRingEtaCutsLeadingP"), etaProxyBin, ringObservableLeadP); + histos.fill(HIST("EtaStudy/pRingEtaCutsLeadingP"), etaProxyLambdaBin, ringObservableLeadP); + histos.fill(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"), 0, v0InMassPeak, ringObservableLeadP); + histos.fill(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"), etaLambdaBin, v0InMassPeak, ringObservableLeadP); + histos.fill(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"), etaProxyBin, v0InMassPeak, ringObservableLeadP); + histos.fill(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"), etaProxyLambdaBin, v0InMassPeak, ringObservableLeadP); + + histos.fill(HIST("EtaStudy/hFakePolCountsLeadP"), cosFakePol, 0); + histos.fill(HIST("EtaStudy/hFakePolCountsLeadP"), cosFakePol, etaLambdaBin); + histos.fill(HIST("EtaStudy/hFakePolCountsLeadP"), cosFakePol, etaProxyBin); + histos.fill(HIST("EtaStudy/hFakePolCountsLeadP"), cosFakePol, etaProxyLambdaBin); + + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsCosThetaVsPtForLeadP"), cosFakePol, v0pt); // Understanding the population of events that has a leading particle (even though this does not need one to be calculated!) + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaLeadP"), cosFakePol, deltaThetaLeadP); + if (leadPEtaPos) + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaLeadPPosEta"), cosFakePol, deltaThetaLeadP); + else + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaLeadPNegEta"), cosFakePol, deltaThetaLeadP); } - } + } // end doFakePolDiagnosticsQA (eta-dependence block) // Extra kinematic criteria for Lambda candidates (removes polarization background): if (kinematicLambdaCheck) { From 132c0b898bfc882e6b11ad9f03de3a4cef5abd42 Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Wed, 19 Aug 2026 17:04:16 +0000 Subject: [PATCH 24/31] Fixing filter declaration --- PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx index 44379219658..b0e5fa00e2a 100644 --- a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx @@ -1356,7 +1356,7 @@ struct lambdajetpolarizationionsderived { } // Defining filters for events: - Filter zvtxFilter = (nabs(o2::aod::RingCollisions::zvtx) < maxZVtxPosition); + Filter zvtxFilter = (nabs(o2::aod::lambdajetpol::zvtx) < maxZVtxPosition); // Preslices for correct collisions association: // (tested custom grouping and performs worse here) From 02e8d985c8ed5620a1cbc80b76a40d058b3dba66 Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Wed, 19 Aug 2026 19:22:39 +0000 Subject: [PATCH 25/31] Linter + clang formatting changes for standardized code production. This anticipates some CI check in the central repo, via a custom task I create in my own .vscode! --- .../Strangeness/lambdaJetPolarizationIons.cxx | 101 +- .../lambdaJetPolarizationIonsDerived.cxx | 1975 +++++++++-------- 2 files changed, 1071 insertions(+), 1005 deletions(-) diff --git a/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx b/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx index 57d938f6a49..968eadeae8e 100644 --- a/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx +++ b/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx @@ -179,8 +179,8 @@ struct lambdajetpolarizationions { Configurable requireSel8{"requireSel8", true, "require sel8 event selection"}; Configurable requireTriggerTVX{"requireTriggerTVX", true, "require FT0 vertex (acceptable FT0C-FT0A time difference) at trigger level"}; // part of sel8, actually // Detector status: - Configurable rejectITSROFBorder{"rejectITSROFBorder", true, "reject events at ITS ROF border (Run 3 only)"}; // part of sel8, actually - Configurable rejectTFBorder{"rejectTFBorder", true, "reject events at TF border (Run 3 only)"}; // part of sel8, actually + Configurable rejectITSROFBorder{"rejectITSROFBorder", true, "reject events at ITS ROF border (Run 3 only)"}; // part of sel8, actually + Configurable rejectTFBorder{"rejectTFBorder", true, "reject events at TF border (Run 3 only)"}; // part of sel8, actually Configurable requireGoodITSLayersAll{"requireGoodITSLayersAll", false, "require number of inactive chips on all ITS layers to be below maximum allowed values"}; // Based off ITS holes DPG AOT selection // Vertex quality: Configurable requireIsVertexITSTPC{"requireIsVertexITSTPC", false, "require events with at least one ITS-TPC track (Run 3 only)"}; @@ -224,7 +224,7 @@ struct lambdajetpolarizationions { // Configurable dcanegtopv{"dcanegtopv", .2, "min DCA Neg To PV (cm)"}; // Default is .05 // Configurable dcapostopv{"dcapostopv", .05, "min DCA Pos To PV (cm)"}; // Default is .05 // Renamed for better consistency of candidate selection (the cut is not determined by charge, but by mass and how deflected the daughter is): - Configurable dcaPionToPV{"dcaPionToPV", .05, "min DCA pion-like daughter To PV (cm)"}; // .2 would suppress primary pion background, but we need to reduce fake-polarization signals enhanced by DCA-to-PV cuts + Configurable dcaPionToPV{"dcaPionToPV", .05, "min DCA pion-like daughter To PV (cm)"}; // .2 would suppress primary pion background, but we need to reduce fake-polarization signals enhanced by DCA-to-PV cuts Configurable dcaProtonToPV{"dcaProtonToPV", .05, "min DCA proton-like daughter To PV (cm)"}; // Default is .05 Configurable v0radius{"v0radius", 1.0, "minimum V0 radius (cm)"}; // Default is 1.2 Configurable v0radiusMax{"v0radiusMax", 1E5, "maximum V0 radius (cm)"}; @@ -318,15 +318,15 @@ struct lambdajetpolarizationions { ConfigurableAxis axisLambdaMassSigExtract{ "axisLambdaMassSigExtract", {VARIABLE_WIDTH, - // Left sideband (7 bins, 0.004 width) - 1.0800, 1.0840, 1.0880, 1.0920, - 1.0960, 1.1000, 1.1040, 1.1080, - // Fine peak region (8 bins, 0.0016 width) - 1.1096, 1.1112, 1.1128, 1.1144, - 1.1160, 1.1176, 1.1192, 1.1208, - // Right sideband (7 bins, 0.004 width) - 1.1248, 1.1288, 1.1328, 1.1368, - 1.1408, 1.1448, 1.1488}, + // Left sideband (7 bins, 0.004 width) + 1.0800, 1.0840, 1.0880, 1.0920, + 1.0960, 1.1000, 1.1040, 1.1080, + // Fine peak region (8 bins, 0.0016 width) + 1.1096, 1.1112, 1.1128, 1.1144, + 1.1160, 1.1176, 1.1192, 1.1208, + // Right sideband (7 bins, 0.004 width) + 1.1248, 1.1288, 1.1328, 1.1368, + 1.1408, 1.1448, 1.1488}, "Lambda mass in GeV/c"}; ConfigurableAxis axisPVz{"axisPVz", {100, -20.0f, +20.0f}, "Primary Vertex Z [cm]"}; ConfigurableAxis axisPVzCoarse{"axisPVzCoarse", {20, -20.0f, +20.0f}, "Primary Vertex Z [cm]"}; @@ -341,7 +341,7 @@ struct lambdajetpolarizationions { ConfigurableAxis axisOccupancy{"axisOccupancy", {VARIABLE_WIDTH, 0.0f, 250.0f, 500.0f, 750.0f, 1000.0f, 1500.0f, 2000.0f, 3000.0f, 4500.0f, 6000.0f, 8000.0f, 10000.0f, 50000.0f}, "Occupancy"}; // Topological variable QA axes: ConfigurableAxis axisDCAtoPV{"axisDCAtoPV", {30, 0.0f, 2.0f}, "DCA (cm)"}; - ConfigurableAxis axisDCAdau{"axisDCAdau", {20, 0.0f, 1.2f}, "DCA (cm)"}; // This should follow v0Selections.dcav0dau's default + ConfigurableAxis axisDCAdau{"axisDCAdau", {20, 0.0f, 1.2f}, "DCA (cm)"}; // This should follow v0Selections.dcav0dau's default ConfigurableAxis axisPointingAngle{"axisPointingAngle", {20, 0.0f, 0.4f}, "pointing angle (rad)"}; // cos(0.4 rad) is ~0.92 for cosPA ConfigurableAxis axisV0Radius{"axisV0Radius", {30, 0.0f, 60.0f}, "V0 2D radius (cm)"}; // PID: @@ -353,7 +353,7 @@ struct lambdajetpolarizationions { ConfigurableAxis axisPhiMod{"axisPhiMod", {100, 0.0f, constants::math::TwoPI / 18}, "Azimuth angle wrt TPC sector (rad.)"}; ConfigurableAxis axisEta{"axisEta", {50, -1.0f, 1.0f}, "#eta"}; ConfigurableAxis axisRapidity{"axisRapidity", {50, -1.0f, 1.0f}, "y"}; - // AP plot axes + // AP plot axes ConfigurableAxis axisAPAlpha{"axisAPAlpha", {220, -1.1f, 1.1f}, "V0 AP alpha"}; ConfigurableAxis axisAPQt{"axisAPQt", {220, 0.0f, 0.5f}, "V0 AP alpha"}; // Track quality axes @@ -395,7 +395,7 @@ struct lambdajetpolarizationions { Configurable jetAlgorithm{"jetAlgorithm", kAntiKt, "jet clustering algorithm. 0 = kT, 1 = C/A, 2 = Anti-kT"}; Configurable jetRecombScheme{"jetRecombScheme", kEScheme, "Jet recombination scheme: 0: E_scheme, 1: pT-scheme, 2: pt2-scheme, 7: WTA_pt_scheme"}; // See PWGJE/JetFinders/jetFinder.h for more info. Configurable bkgSubtraction{"bkgSubtraction", kNoSubtraction, "Jet background subtraction: No subtraction (false), Area (true), Constituent (TODO)"}; // Selection bool for background subtraction strategy - Configurable GhostedAreaSpecRapidity{"GhostedAreaSpecRapidity", 1.1, "Max ghost particle rapidity for jet area estimates"}; // At least 1.0 for tracks and jets within the |eta| < 0.9 window of ITS+TPC + Configurable ghostedAreaSpecRapidity{"ghostedAreaSpecRapidity", 1.1, "Max ghost particle rapidity for jet area estimates"}; // At least 1.0 for tracks and jets within the |eta| < 0.9 window of ITS+TPC // Using an enum for readability: Configurable jetType{"jetType", kChargedJet, "Jet type: 0: Charged Jet, 1: Full Jet, 2: Photon-tagged, 3: Z-tagged"}; // TODO: implement full, photon and Z jets // (TODO: check the maximum pT of jets used in my analyses! If it is way too hard, it might not be the best jet to use!) @@ -651,8 +651,7 @@ struct lambdajetpolarizationions { {p + "TOF PID #pi", v0Selections.tofPidNsigmaCutLaPi < 1e+6}, {p + "c#tau", v0Selections.lambdaLifetimeCut > 0}, {p + "Ambiguous rejection", true}, - {p + "Final accepted", true}} - ); + {p + "Final accepted", true}}); }; constexpr bool Lambda = true; // Some constexpr to make it more readable (works at compile level) constexpr bool AntiLambda = false; // "false" is just a flag for this addHypothesis function! It just means fill "AntiLambda" labels @@ -671,7 +670,7 @@ struct lambdajetpolarizationions { // TH2D: "selection flow" vs "Lambda invariant mass" (to investigate whether we are removing background or just signal) auto h2dSelectionLambdaMass = histos.add("GeneralQA/h2dSelectionLambdaMass", "V0 selection flow vs M_{#Lambda}; ; M_{#Lambda} (GeV/#it{c}^{2})", kTH2D, {{static_cast(v0LambdaSelectionLabels.size()), -0.5, static_cast(v0LambdaSelectionLabels.size()) - 0.5}, - axisConfigurations.axisLambdaMassSigExtract}); + axisConfigurations.axisLambdaMassSigExtract}); for (size_t i = 0; i < v0LambdaSelectionLabels.size(); ++i) { auto lbl = v0LambdaSelectionLabels[i].label; if (!v0LambdaSelectionLabels[i].enabled) @@ -682,7 +681,7 @@ struct lambdajetpolarizationions { // Same for AntiLambda mass hypothesis: auto h2dSelectionAntiLambdaMass = histos.add("GeneralQA/h2dSelectionAntiLambdaMass", "V0 selection flow vs M_{#bar{#Lambda}}; ; M_{#bar{#Lambda}} (GeV/#it{c}^{2})", kTH2D, {{static_cast(v0LambdaSelectionLabels.size()), -0.5, static_cast(v0LambdaSelectionLabels.size()) - 0.5}, - axisConfigurations.axisLambdaMassSigExtract}); + axisConfigurations.axisLambdaMassSigExtract}); for (size_t i = 0; i < v0LambdaSelectionLabels.size(); ++i) { auto lbl = v0LambdaSelectionLabels[i].label; if (!v0LambdaSelectionLabels[i].enabled) @@ -797,7 +796,7 @@ struct lambdajetpolarizationions { if (analyseLambda && analyseAntiLambda) { // histos.add("hAmbiguousLambdaCandidates", "hAmbiguousLambdaCandidates;Integrated counts;Counts", kTH1D, {{1, 0, 1}}); // No longer required: v0 selection flow already considers this histos.add("hAmbiguousPerEvent", "hAmbiguousPerEvent;N_{ambiguous} per event;Counts", kTH1D, {{15, 0, 15}}); - histos.add("hNonAmbiguousPerEvent", "hNonAmbiguousPerEvent;N_{non-ambiguous} per event;Counts", kTH1D, {{25, 0, 25}}); // To understand the population of correlated Lambda-likes per event + histos.add("hNonAmbiguousPerEvent", "hNonAmbiguousPerEvent;N_{non-ambiguous} per event;Counts", kTH1D, {{25, 0, 25}}); // To understand the population of correlated Lambda-likes per event histos.add("hLambdasAndAntiLambdasPerEvent", "hLambdasAndAntiLambdasPerEvent;N_{#Lambda}+N_{#bar{#Lambda}} per event;Counts", kTH1D, {{25, 0, 25}}); // Alternative check that shows how bad is the possibly correlated full population (Ambig+NonAmbig) } @@ -876,8 +875,8 @@ struct lambdajetpolarizationions { histos.add("GeneralQA/h2dArmenterosFullSelectedAmbiguous", "h2dArmenterosFullSelectedAmbiguous;Armenteros #alpha;Armenteros q_{T} (GeV/c)", kTH2D, {axisConfigurations.axisAPAlpha, axisConfigurations.axisAPQt}); // Jets histograms: - // Histograms that need to be present even out of extensive QA-mode: - histos.add("hEventsWithJet", "hEventsWithJet;Integrated counts;Counts", kTH1D, {{1, 0, 1}}); // counter of events with jet (could be interesting to compare with the minimum pT cut or between the background subtraction vs no background subtraction cases) + // Histograms that need to be present even out of extensive QA-mode: + histos.add("hEventsWithJet", "hEventsWithJet;Integrated counts;Counts", kTH1D, {{1, 0, 1}}); // counter of events with jet (could be interesting to compare with the minimum pT cut or between the background subtraction vs no background subtraction cases) histos.add("hJetsPerEvent", "hJetsPerEvent;Jets per event;Counts", kTH1D, {axisConfigurations.axisJetsPerEvent}); // number of jets per event if (doJetKinematicsQA) { histos.add("JetKinematicsQA/hJetPt", "hJetPt;Jet p_{T} [GeV/c];Counts", kTH1D, {axisConfigurations.axisJetPt}); @@ -1007,12 +1006,18 @@ struct lambdajetpolarizationions { // Made it like this because we use ++binValue when filling, so the first filled // bin will always be x=0 due to operator precedence. HistogramRegistry* histos = nullptr; // Had to pass the histos group to this struct, as it was not visible to the members of the struct - float mL = -1.f; // mLambda for current V0, -1 at initialization - float mAL = -1.f; // mAntiLambda for current V0, -1 at initialization - void resetForNewV0(float massLambda, float massAntiLambda) { binValue = -1; mL = massLambda; mAL = massAntiLambda; } + float mL = -1.f; // mLambda for current V0, -1 at initialization + float mAL = -1.f; // mAntiLambda for current V0, -1 at initialization + void resetForNewV0(float massLambda, float massAntiLambda) + { + binValue = -1; + mL = massLambda; + mAL = massAntiLambda; + } void advanceTo(int targetBinX) { binValue = targetBinX - 1; } // next fill() lands at targetBin. Needed to deal with early exits at isLambda vs isAntiLambda checks - void fill() { + void fill() + { histos->fill(HIST("GeneralQA/hSelectionV0s"), ++binValue); // Hardcoded hSelectionV0s histogram, as it will not change. Increments before filling, by default histos->fill(HIST("GeneralQA/h2dSelectionLambdaMass"), binValue, mL); histos->fill(HIST("GeneralQA/h2dSelectionAntiLambdaMass"), binValue, mAL); @@ -1020,14 +1025,18 @@ struct lambdajetpolarizationions { // Use this for DISABLED cuts within a single hypothesis // (shows pass-through count as a flat line, making it visually clear that the stage was not active. Replaces N dummy fill() calls) // fillUpTo advances through disabled bins, filling all three histograms uniformly. - void fillUpTo(int targetBinX) { while (binValue < targetBinX) fill(); } + void fillUpTo(int targetBinX) + { + while (binValue < targetBinX) + fill(); + } }; - V0SelectionFlowCounter v0SelCounter{-1, &histos}; // Could initialize with any index (resetForNewV0 is always called for a new V0 anyways) - // Calculating some bins, for convenience: - const int nGenericCuts = 31; // x=0 to x=30 (bins 1 and 31, as the lower-edge is inclusive) - const int nHypoCuts = 11; // per hypothesis (x=31..41 for Lambda) - const int lambdaHypoEnd = nGenericCuts + nHypoCuts - 1; // x=41 (bin 42) - const int antiLambdaHypoEnd = nGenericCuts + 2*nHypoCuts - 1; // x = 52 (bin 53) + V0SelectionFlowCounter v0SelCounter{-1, &histos}; // Could initialize with any index (resetForNewV0 is always called for a new V0 anyways) + // Calculating some bins, for convenience: + const int nGenericCuts = 31; // x=0 to x=30 (bins 1 and 31, as the lower-edge is inclusive) + const int nHypoCuts = 11; // per hypothesis (x=31..41 for Lambda) + const int lambdaHypoEnd = nGenericCuts + nHypoCuts - 1; // x=41 (bin 42) + const int antiLambdaHypoEnd = nGenericCuts + 2 * nHypoCuts - 1; // x = 52 (bin 53) // Minimal helper to fill hSelectionJetTracks, mirroring V0SelectionFlowCounter. // Reset once per track candidate, fill once per passed cut stage. @@ -1106,10 +1115,10 @@ struct lambdajetpolarizationions { return dot / (magA * magB); } - const float NSigmaScale = 100.f; // The factor for packing the float as an int16_t + const float nSigmaScale = 100.f; // The factor for packing the float as an int16_t inline int16_t packNSigma(float x) { - return static_cast(std::lround(x * NSigmaScale)); + return static_cast(std::lround(x * nSigmaScale)); } ///////////////////////////////////////////// @@ -1574,7 +1583,7 @@ struct lambdajetpolarizationions { // Cluster particles with the configured algorithm: fastjet::JetDefinition jetDef(mapFJAlgorithm(jetConfigurations.jetAlgorithm), jetConfigurations.radiusJet, mapFJRecombScheme(jetConfigurations.jetRecombScheme)); if (jetConfigurations.bkgSubtraction == kAreaBased) { - fastjet::AreaDefinition areaDef(fastjet::active_area, fastjet::GhostedAreaSpec(jetConfigurations.GhostedAreaSpecRapidity)); + fastjet::AreaDefinition areaDef(fastjet::active_area, fastjet::GhostedAreaSpec(jetConfigurations.ghostedAreaSpecRapidity)); fastjet::ClusterSequenceArea clustSeq(fjParticles, jetDef, areaDef); // Attributes an area for each pseudojet in the list std::vector jets = fastjet::sorted_by_pt(clustSeq.inclusive_jets()); // No minimum pt before background subtraction if (jets.empty()) @@ -1616,7 +1625,7 @@ struct lambdajetpolarizationions { jetMinusBkg.pt(), jetMinusBkg.eta(), // Using eta instead of rapidity jetMinusBkg.phi()); - // jetMinusBkg.constituents().size()); + // jetMinusBkg.constituents().size()); // Finding the leading jet after subtraction (leading jet is NOT known a priori!): if (jetMinusBkg.pt() > leadingJetPt) { @@ -1738,8 +1747,8 @@ struct lambdajetpolarizationions { jet.pt(), jetEta, // Using eta instead of rapidity jet.phi()); - // jet.constituents().size()); // Currently removed from datamodel. - // Other variables can better reveal jet quenching and help identify good selection criteria for quenched jets proxies + // jet.constituents().size()); // Currently removed from datamodel. + // Other variables can better reveal jet quenching and help identify good selection criteria for quenched jets proxies if (doJetKinematicsQA) { histos.fill(HIST("JetKinematicsQA/hJetPt"), jet.pt()); @@ -1917,7 +1926,7 @@ struct lambdajetpolarizationions { histos.fill(HIST("GeneralQA/h2dArmenterosFullSelectedAntiLambda"), v0.alpha(), v0.qtarm()); // XOR check: - if (isLambda ^ isAntiLambda){ + if (isLambda ^ isAntiLambda) { histos.fill(HIST("GeneralQA/h2dArmenterosFullSelectedNonAmbiguous"), v0.alpha(), v0.qtarm()); nNonAmbiguous++; } @@ -1941,12 +1950,12 @@ struct lambdajetpolarizationions { // v0.mLambda() // Bookkeeping for the candidates that passed eveything until the ambiguity check: - if (isLambda) { // Go back to Lambda bin to fill it as well: - v0SelCounter.advanceTo(lambdaHypoEnd-1); // Bin 41: Ambiguous Lambda rejection + if (isLambda) { // Go back to Lambda bin to fill it as well: + v0SelCounter.advanceTo(lambdaHypoEnd - 1); // Bin 41: Ambiguous Lambda rejection v0SelCounter.fill(); } if (isAntiLambda) { - v0SelCounter.advanceTo(antiLambdaHypoEnd-1); // Bin 52: Ambiguous antiLambda rejection + v0SelCounter.advanceTo(antiLambdaHypoEnd - 1); // Bin 52: Ambiguous antiLambda rejection v0SelCounter.fill(); } @@ -1958,7 +1967,7 @@ struct lambdajetpolarizationions { if (isLambda) { v0SelCounter.advanceTo(lambdaHypoEnd); // Bin 42: Final accepted Lambda V0s v0SelCounter.fill(); - } else { // i.e., isAntiLambda + } else { // i.e., isAntiLambda v0SelCounter.advanceTo(antiLambdaHypoEnd); // Bin 53: Final accepted anti-Lambda V0s v0SelCounter.fill(); } @@ -1967,14 +1976,14 @@ struct lambdajetpolarizationions { auto const v0pt = v0.pt(); tableV0s(ringCollIdx, v0pt, v0.eta(), v0.phi(), // Using eta instead of rapidity - isLambda, // 0: antiLambda, 1: Lambda. No ambiguous candidates (isLambda && isAntiLambda) are stored + isLambda, // 0: antiLambda, 1: Lambda. No ambiguous candidates (isLambda && isAntiLambda) are stored isLambda ? v0.mLambda() : v0.mAntiLambda(), v0.positivept(), v0.positiveeta(), v0.positivephi(), v0.negativept(), v0.negativeeta(), v0.negativephi(), packNSigma(isLambda ? posTrackExtra.tpcNSigmaPr() : negTrackExtra.tpcNSigmaPr()), // Proton-like track, packed into int16_t packNSigma(isLambda ? negTrackExtra.tpcNSigmaPi() : posTrackExtra.tpcNSigmaPi()), // Pion-like track v0.v0cosPA(), v0.v0radius(), v0.dcaV0daughters(), v0.dcapostopv(), v0.dcanegtopv()); - + // Perform extensive QAs for non-ambiguous Lambda/antiLambda candidates only: if (doEventQA && !validV0AlreadyFound) fillEventSelectionQA(mBinHasRingV0, centrality); // hasRingV0 passes diff --git a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx index b0e5fa00e2a..65b353e0ec2 100644 --- a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx @@ -9,9 +9,8 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. // -/// \file lambdajetpolarizationionsderived.cxx +/// \file lambdaJetPolarizationIonsDerived.cxx /// \brief Lambda and antiLambda polarization analysis task using derived data -/// /// \author Cicero Domenico Muncinelli , Campinas State University // // Jet Polarization Ions task -- Derived data @@ -32,6 +31,8 @@ #include "PWGLF/DataModel/lambdaJetPolarizationIons.h" +#include "Common/Core/RecoDecay.h" + #include #include #include @@ -56,13 +57,13 @@ #include // For perpendicular jet direction QAs #include // std::fill, for resetting the Delta Method accumulators +#include #include #include -#include -#include #include -#include +#include #include +#include using namespace o2; using namespace o2::framework; @@ -72,11 +73,11 @@ using ROOT::Math::XYZVector; // using namespace o2::aod::lambdajetpol; // Used it explicitly along the code for clarity // Declaring constants: -constexpr double protonMass = o2::constants::physics::MassProton; // Assumes particle identification for daughter is perfect -constexpr double lambdaWeakDecayConstant = 0.749; // DPG 2025 update -constexpr double antiLambdaWeakDecayConstant = -0.758; // DPG 2025 update -constexpr double polPrefactorLambda = 3.0 / lambdaWeakDecayConstant; -constexpr double polPrefactorAntiLambda = 3.0 / antiLambdaWeakDecayConstant; +constexpr double ProtonMass = o2::constants::physics::MassProton; // Assumes particle identification for daughter is perfect +constexpr double LambdaWeakDecayConstant = 0.749; // DPG 2025 update +constexpr double AntiLambdaWeakDecayConstant = -0.758; // DPG 2025 update +constexpr double PolPrefactorLambda = 3.0 / LambdaWeakDecayConstant; +constexpr double PolPrefactorAntiLambda = 3.0 / AntiLambdaWeakDecayConstant; enum CentEstimator { kCentFT0C = 0, @@ -163,25 +164,25 @@ enum CentEstimator { // (TODO: add counters for regular TH2Ds about centrality) // For leading particle -#define RING_OBSERVABLE_LEADP_FILL_LIST(X, FOLDER) \ - X(FOLDER "/QA/hDeltaPhiLeadP", deltaPhiLeadP) \ - X(FOLDER "/QA/hDeltaThetaLeadP", deltaThetaLeadP) \ - X(FOLDER "/QA/hPtLeadP", leadPPt) \ - X(FOLDER "/QA/hCosDeltaThetaLeadP", cosDeltaThetaLeadP) \ - X(FOLDER "/hRingObservableLeadPCounts", ringObservableLeadP) \ - X(FOLDER "/pRingObservableLeadPDeltaPhi", deltaPhiLeadP, ringObservableLeadP) \ - X(FOLDER "/pRingObservableLeadPDeltaTheta", deltaThetaLeadP, ringObservableLeadP) \ - X(FOLDER "/EtaDependence/pRingObservableEtaLambdaLeadP", v0eta, ringObservableLeadP) \ - X(FOLDER "/EtaDependence/pRingObservableEtaLeadP", leadPEta, ringObservableLeadP) \ - X(FOLDER "/pRingObservableLeadPIntegrated", 0., ringObservableLeadP) \ - X(FOLDER "/pRingObservableLeadPLambdaPt", v0pt, ringObservableLeadP) \ - X(FOLDER "/pRingObservableLeadPPVz", collisionPVz, ringObservableLeadP) \ - X(FOLDER "/ProxyPtDependence/pRingVsPtLeadP", leadPPt, ringObservableLeadP) \ - X(FOLDER "/ProxyPtDependence/pRingVsPtLeadPVsEtaLeadP", leadPPt, leadPEta, ringObservableLeadP) \ - X(FOLDER "/ProxyPtDependence/pRingVsPtLeadPVsEtaV0", leadPPt, v0eta, ringObservableLeadP) \ +#define RING_OBSERVABLE_LEADP_FILL_LIST(X, FOLDER) \ + X(FOLDER "/QA/hDeltaPhiLeadP", deltaPhiLeadP) \ + X(FOLDER "/QA/hDeltaThetaLeadP", deltaThetaLeadP) \ + X(FOLDER "/QA/hPtLeadP", leadPPt) \ + X(FOLDER "/QA/hCosDeltaThetaLeadP", cosDeltaThetaLeadP) \ + X(FOLDER "/hRingObservableLeadPCounts", ringObservableLeadP) \ + X(FOLDER "/pRingObservableLeadPDeltaPhi", deltaPhiLeadP, ringObservableLeadP) \ + X(FOLDER "/pRingObservableLeadPDeltaTheta", deltaThetaLeadP, ringObservableLeadP) \ + X(FOLDER "/EtaDependence/pRingObservableEtaLambdaLeadP", v0eta, ringObservableLeadP) \ + X(FOLDER "/EtaDependence/pRingObservableEtaLeadP", leadPEta, ringObservableLeadP) \ + X(FOLDER "/pRingObservableLeadPIntegrated", 0., ringObservableLeadP) \ + X(FOLDER "/pRingObservableLeadPLambdaPt", v0pt, ringObservableLeadP) \ + X(FOLDER "/pRingObservableLeadPPVz", collisionPVz, ringObservableLeadP) \ + X(FOLDER "/ProxyPtDependence/pRingVsPtLeadP", leadPPt, ringObservableLeadP) \ + X(FOLDER "/ProxyPtDependence/pRingVsPtLeadPVsEtaLeadP", leadPPt, leadPEta, ringObservableLeadP) \ + X(FOLDER "/ProxyPtDependence/pRingVsPtLeadPVsEtaV0", leadPPt, v0eta, ringObservableLeadP) \ X(FOLDER "/EtaDependence/p2dRingObservableEtaLambdaVsEtaLeadP", v0eta, leadPEta, ringObservableLeadP) \ - X(FOLDER "/EtaDependence/h2dCounterEtaLambdaVsEtaLeadP", v0eta, leadPEta) \ - X(FOLDER "/p2dRingObservableLeadPVsPxPy", v0px, v0py, ringObservableLeadP) \ + X(FOLDER "/EtaDependence/h2dCounterEtaLambdaVsEtaLeadP", v0eta, leadPEta) \ + X(FOLDER "/p2dRingObservableLeadPVsPxPy", v0px, v0py, ringObservableLeadP) \ X(FOLDER "/p2dRingObservableLeadPVsPzPx", v0pz, v0px, ringObservableLeadP) // A macro that encapsulates all eta checks for leading particle and V0s, along with the fills @@ -189,58 +190,58 @@ enum CentEstimator { // FOLDER -- histogram folder string (compile-time literal) // LEADP_IS_POS -- bool: leadPEtaPos // V0_IS_POS -- bool: lambdaEtaPos -#define RING_OBSERVABLE_LEADP_ETA_SPLIT_FILL_LIST(FOLDER, LEADP_IS_POS, V0_IS_POS) \ - do { \ - if (LEADP_IS_POS) { \ - /* leadP marginal: positive side */ \ - APPLY_HISTO_FILL(FOLDER "/ProxyPtDependence/pRingVsPtLeadP_PosEtaLeadP", leadPPt, ringObservableLeadP) \ - if (V0_IS_POS) { \ - /* V0 marginal: positive side */ \ - APPLY_HISTO_FILL(FOLDER "/ProxyPtDependence/pRingVsPtLeadP_PosEtaV0", leadPPt, ringObservableLeadP) \ - /* Joint: (+leadP, +V0) */ \ +#define RING_OBSERVABLE_LEADP_ETA_SPLIT_FILL_LIST(FOLDER, LEADP_IS_POS, V0_IS_POS) \ + do { \ + if (LEADP_IS_POS) { \ + /* leadP marginal: positive side */ \ + APPLY_HISTO_FILL(FOLDER "/ProxyPtDependence/pRingVsPtLeadP_PosEtaLeadP", leadPPt, ringObservableLeadP) \ + if (V0_IS_POS) { \ + /* V0 marginal: positive side */ \ + APPLY_HISTO_FILL(FOLDER "/ProxyPtDependence/pRingVsPtLeadP_PosEtaV0", leadPPt, ringObservableLeadP) \ + /* Joint: (+leadP, +V0) */ \ APPLY_HISTO_FILL(FOLDER "/ProxyPtDependence/pRingVsPtLeadP_PosEtaLeadP_PosEtaV0", leadPPt, ringObservableLeadP) \ - } else { \ - /* V0 marginal: negative side */ \ - APPLY_HISTO_FILL(FOLDER "/ProxyPtDependence/pRingVsPtLeadP_NegEtaV0", leadPPt, ringObservableLeadP) \ - /* Joint: (+leadP, -V0) */ \ + } else { \ + /* V0 marginal: negative side */ \ + APPLY_HISTO_FILL(FOLDER "/ProxyPtDependence/pRingVsPtLeadP_NegEtaV0", leadPPt, ringObservableLeadP) \ + /* Joint: (+leadP, -V0) */ \ APPLY_HISTO_FILL(FOLDER "/ProxyPtDependence/pRingVsPtLeadP_PosEtaLeadP_NegEtaV0", leadPPt, ringObservableLeadP) \ - } \ - } else { \ - /* leadP marginal: negative side */ \ - APPLY_HISTO_FILL(FOLDER "/ProxyPtDependence/pRingVsPtLeadP_NegEtaLeadP", leadPPt, ringObservableLeadP) \ - if (V0_IS_POS) { \ - /* V0 marginal: positive side */ \ - APPLY_HISTO_FILL(FOLDER "/ProxyPtDependence/pRingVsPtLeadP_PosEtaV0", leadPPt, ringObservableLeadP) \ - /* Joint: (-leadP, +V0) */ \ + } \ + } else { \ + /* leadP marginal: negative side */ \ + APPLY_HISTO_FILL(FOLDER "/ProxyPtDependence/pRingVsPtLeadP_NegEtaLeadP", leadPPt, ringObservableLeadP) \ + if (V0_IS_POS) { \ + /* V0 marginal: positive side */ \ + APPLY_HISTO_FILL(FOLDER "/ProxyPtDependence/pRingVsPtLeadP_PosEtaV0", leadPPt, ringObservableLeadP) \ + /* Joint: (-leadP, +V0) */ \ APPLY_HISTO_FILL(FOLDER "/ProxyPtDependence/pRingVsPtLeadP_NegEtaLeadP_PosEtaV0", leadPPt, ringObservableLeadP) \ - } else { \ - /* V0 marginal: negative side */ \ - APPLY_HISTO_FILL(FOLDER "/ProxyPtDependence/pRingVsPtLeadP_NegEtaV0", leadPPt, ringObservableLeadP) \ - /* Joint: (-leadP, -V0) */ \ + } else { \ + /* V0 marginal: negative side */ \ + APPLY_HISTO_FILL(FOLDER "/ProxyPtDependence/pRingVsPtLeadP_NegEtaV0", leadPPt, ringObservableLeadP) \ + /* Joint: (-leadP, -V0) */ \ APPLY_HISTO_FILL(FOLDER "/ProxyPtDependence/pRingVsPtLeadP_NegEtaLeadP_NegEtaV0", leadPPt, ringObservableLeadP) \ - } \ - } \ + } \ + } \ } while (0) // For subleading jet: -#define RING_OBSERVABLE_2NDJET_FILL_LIST(X, FOLDER) \ - X(FOLDER "/QA/hDeltaPhi2ndJet", deltaPhi2ndJet) \ - X(FOLDER "/QA/hDeltaTheta2ndJet", deltaTheta2ndJet) \ - X(FOLDER "/QA/hCosDeltaTheta2ndJet", cosDeltaTheta2ndJet) \ - X(FOLDER "/QA/hPt2ndJet", subleadingJetPt) \ - X(FOLDER "/hRingObservable2ndJetCounter", ringObservable2ndJet) \ - X(FOLDER "/pRingObservable2ndJetDeltaPhi", deltaPhi2ndJet, ringObservable2ndJet) \ - X(FOLDER "/pRingObservable2ndJetDeltaTheta", deltaTheta2ndJet, ringObservable2ndJet) \ - X(FOLDER "/EtaDependence/pRingObservableEtaLambda2ndJet", v0eta, ringObservable2ndJet) \ - X(FOLDER "/EtaDependence/pRingObservableEta2ndJet", subleadingJetEta, ringObservable2ndJet) \ - X(FOLDER "/pRingObservable2ndJetIntegrated", 0., ringObservable2ndJet) \ - X(FOLDER "/pRingObservable2ndJetLambdaPt", v0pt, ringObservable2ndJet) \ - X(FOLDER "/pRingObservableSubLeadPVz", collisionPVz, ringObservable2ndJet) \ - X(FOLDER "/ProxyPtDependence/pRingVsPt2ndJet", subleadingJetPt, ringObservable2ndJet) \ +#define RING_OBSERVABLE_2NDJET_FILL_LIST(X, FOLDER) \ + X(FOLDER "/QA/hDeltaPhi2ndJet", deltaPhi2ndJet) \ + X(FOLDER "/QA/hDeltaTheta2ndJet", deltaTheta2ndJet) \ + X(FOLDER "/QA/hCosDeltaTheta2ndJet", cosDeltaTheta2ndJet) \ + X(FOLDER "/QA/hPt2ndJet", subleadingJetPt) \ + X(FOLDER "/hRingObservable2ndJetCounter", ringObservable2ndJet) \ + X(FOLDER "/pRingObservable2ndJetDeltaPhi", deltaPhi2ndJet, ringObservable2ndJet) \ + X(FOLDER "/pRingObservable2ndJetDeltaTheta", deltaTheta2ndJet, ringObservable2ndJet) \ + X(FOLDER "/EtaDependence/pRingObservableEtaLambda2ndJet", v0eta, ringObservable2ndJet) \ + X(FOLDER "/EtaDependence/pRingObservableEta2ndJet", subleadingJetEta, ringObservable2ndJet) \ + X(FOLDER "/pRingObservable2ndJetIntegrated", 0., ringObservable2ndJet) \ + X(FOLDER "/pRingObservable2ndJetLambdaPt", v0pt, ringObservable2ndJet) \ + X(FOLDER "/pRingObservableSubLeadPVz", collisionPVz, ringObservable2ndJet) \ + X(FOLDER "/ProxyPtDependence/pRingVsPt2ndJet", subleadingJetPt, ringObservable2ndJet) \ X(FOLDER "/ProxyPtDependence/pRingVsPt2ndJetVsEta2ndJet", subleadingJetPt, subleadingJetEta, ringObservable2ndJet) \ X(FOLDER "/ProxyPtDependence/pRingVsPt2ndJetVsEtaV0", subleadingJetPt, v0eta, ringObservable2ndJet) \ - X(FOLDER "/EtaDependence/p2dRingObservableEtaLambdaVsEta2ndJet", v0eta, subleadingJetEta, ringObservable2ndJet) \ - X(FOLDER "/EtaDependence/h2dCounterEtaLambdaVsEta2ndJet", v0eta, subleadingJetEta) \ + X(FOLDER "/EtaDependence/p2dRingObservableEtaLambdaVsEta2ndJet", v0eta, subleadingJetEta, ringObservable2ndJet) \ + X(FOLDER "/EtaDependence/h2dCounterEtaLambdaVsEta2ndJet", v0eta, subleadingJetEta) #define POLARIZATION_PROFILE_FILL_LIST(X, FOLDER) \ /* =============================== */ \ @@ -274,47 +275,49 @@ enum CentEstimator { // would not know to end each statement with a semicolon): #define APPLY_HISTO_FILL(NAME, ...) histos.fill(HIST(NAME), __VA_ARGS__); - // Delta Method Fill Lists -#define DELTA_INTEGRATED_FILL_LIST(X, FOLDER, r, n) \ - X(FOLDER "/DeltaMethod/hIntegrated", 0.5, r) \ - X(FOLDER "/DeltaMethod/hIntegrated", 1.5, (double)(n)) \ - X(FOLDER "/DeltaMethod/hIntegrated", 2.5, (r)*(r)) \ - X(FOLDER "/DeltaMethod/hIntegrated", 3.5, (double)((n)*(n))) \ - X(FOLDER "/DeltaMethod/hIntegrated", 4.5, (r)*(n)) - -#define DELTA_2D_FILL_LIST(X, FOLDER, HIST_NAME, center, r, n) \ - X(FOLDER "/DeltaMethod/" HIST_NAME, center, 0.5, r) \ - X(FOLDER "/DeltaMethod/" HIST_NAME, center, 1.5, (double)(n)) \ - X(FOLDER "/DeltaMethod/" HIST_NAME, center, 2.5, (r)*(r)) \ - X(FOLDER "/DeltaMethod/" HIST_NAME, center, 3.5, (double)((n)*(n))) \ - X(FOLDER "/DeltaMethod/" HIST_NAME, center, 4.5, (r)*(n)) +#define DELTA_INTEGRATED_FILL_LIST(X, FOLDER, r, n) \ + X(FOLDER "/DeltaMethod/hIntegrated", 0.5, r) \ + X(FOLDER "/DeltaMethod/hIntegrated", 1.5, (double)(n)) \ + X(FOLDER "/DeltaMethod/hIntegrated", 2.5, (r) * (r)) \ + X(FOLDER "/DeltaMethod/hIntegrated", 3.5, (double)((n) * (n))) \ + X(FOLDER "/DeltaMethod/hIntegrated", 4.5, (r) * (n)) + +#define DELTA_2D_FILL_LIST(X, FOLDER, HIST_NAME, center, r, n) \ + X(FOLDER "/DeltaMethod/" HIST_NAME, center, 0.5, r) \ + X(FOLDER "/DeltaMethod/" HIST_NAME, center, 1.5, (double)(n)) \ + X(FOLDER "/DeltaMethod/" HIST_NAME, center, 2.5, (r) * (r)) \ + X(FOLDER "/DeltaMethod/" HIST_NAME, center, 3.5, (double)((n) * (n))) \ + X(FOLDER "/DeltaMethod/" HIST_NAME, center, 4.5, (r) * (n)) // Master flush macro to dump an event tracker into the histograms: -#define FLUSH_DELTA_TRACKER(FOLDER, TRACKER, AXIS_PT, AXIS_MASS, AXIS_DTHETA) \ - if ((TRACKER).n_int > 0) { \ - DELTA_INTEGRATED_FILL_LIST(APPLY_HISTO_FILL, FOLDER, (TRACKER).r_int, (TRACKER).n_int) \ - } \ - for (size_t bin = 0; bin < (TRACKER).r_pt.size(); ++bin) { \ - int n_val = (TRACKER).n_pt[bin]; \ - if (n_val == 0) continue; \ - double r_val = (TRACKER).r_pt[bin]; \ - double center = (AXIS_PT)->GetBinCenter(bin); \ - DELTA_2D_FILL_LIST(APPLY_HISTO_FILL, FOLDER, "h2dLambdaPtVsDeltaComp", center, r_val, n_val) \ - } \ - for (size_t bin = 0; bin < (TRACKER).r_mass.size(); ++bin) { \ - int n_val = (TRACKER).n_mass[bin]; \ - if (n_val == 0) continue; \ - double r_val = (TRACKER).r_mass[bin]; \ - double center = (AXIS_MASS)->GetBinCenter(bin); \ - DELTA_2D_FILL_LIST(APPLY_HISTO_FILL, FOLDER, "h2dMassVsDeltaComp", center, r_val, n_val) \ - } \ - for (size_t bin = 0; bin < (TRACKER).r_dtheta.size(); ++bin) { \ - int n_val = (TRACKER).n_dtheta[bin]; \ - if (n_val == 0) continue; \ - double r_val = (TRACKER).r_dtheta[bin]; \ - double center = (AXIS_DTHETA)->GetBinCenter(bin); \ - DELTA_2D_FILL_LIST(APPLY_HISTO_FILL, FOLDER, "h2dDeltaThetaVsDeltaComp", center, r_val, n_val) \ +#define FLUSH_DELTA_TRACKER(FOLDER, TRACKER, AXIS_PT, AXIS_MASS, AXIS_DTHETA) \ + if ((TRACKER).nInt > 0) { \ + DELTA_INTEGRATED_FILL_LIST(APPLY_HISTO_FILL, FOLDER, (TRACKER).rInt, (TRACKER).nInt) \ + } \ + for (size_t bin = 0; bin < (TRACKER).rPt.size(); ++bin) { \ + int nVal = (TRACKER).nPt[bin]; \ + if (nVal == 0) \ + continue; \ + double rVal = (TRACKER).rPt[bin]; \ + double center = (AXIS_PT)->GetBinCenter(bin); \ + DELTA_2D_FILL_LIST(APPLY_HISTO_FILL, FOLDER, "h2dLambdaPtVsDeltaComp", center, rVal, nVal) \ + } \ + for (size_t bin = 0; bin < (TRACKER).rMass.size(); ++bin) { \ + int nVal = (TRACKER).nMass[bin]; \ + if (nVal == 0) \ + continue; \ + double rVal = (TRACKER).rMass[bin]; \ + double center = (AXIS_MASS)->GetBinCenter(bin); \ + DELTA_2D_FILL_LIST(APPLY_HISTO_FILL, FOLDER, "h2dMassVsDeltaComp", center, rVal, nVal) \ + } \ + for (size_t bin = 0; bin < (TRACKER).rDtheta.size(); ++bin) { \ + int nVal = (TRACKER).nDtheta[bin]; \ + if (nVal == 0) \ + continue; \ + double rVal = (TRACKER).rDtheta[bin]; \ + double center = (AXIS_DTHETA)->GetBinCenter(bin); \ + DELTA_2D_FILL_LIST(APPLY_HISTO_FILL, FOLDER, "h2dDeltaThetaVsDeltaComp", center, rVal, nVal) \ } struct lambdajetpolarizationionsderived { @@ -331,7 +334,7 @@ struct lambdajetpolarizationionsderived { // A very inexpensive "signal extraction" imitation based on v0InMassPeak bool: // (Uses a mass interval to remove or include V0s from the final AnalysisResults to take advantage of existing post-processing codes) Configurable excludeOutOfPeakQA{"excludeOutOfPeakQA", false, "removes all V0s outside an approximate +/- 5*sigma window from the mass peak"}; // A naive estimator of signal - Configurable excludeInPeakQA{"excludeInPeakQA", false, "uses only the V0s outside an approximate +/- 5*sigma window from the mass peak"}; // A naive estimator of background + Configurable excludeInPeakQA{"excludeInPeakQA", false, "uses only the V0s outside an approximate +/- 5*sigma window from the mass peak"}; // A naive estimator of background // Per-family histogram switches: // (Each family books >100 histograms, so it is necessary to keep some of these switches off to avoid the HistogramRegistry limit) @@ -345,13 +348,13 @@ struct lambdajetpolarizationionsderived { // QA switches: struct : ConfigurableGroup { - std::string prefix = "qaSwitches"; // JSON group name + std::string prefix = "qaSwitches"; // JSON group name Configurable doFakePolDiagnosticsQA{"doFakePolDiagnosticsQA", true, "Book and fill the EtaStudy/ and HelicityEfficiencyQA/ folders."}; // The largest per-V0 fill cost in this task } qaSwitches; // Centrality: Configurable centralityEstimator{"centralityEstimator", kCentFT0M, "Run 3 centrality estimator (0:CentFT0C, 1:CentFT0M, 2:CentFV0A)"}; // Default is FT0M - Configurable maxZVtxPosition{"maxZVtxPosition", 10., "max Z vtx position [cm]"}; // An additional post-processing cut after derived data was written. Same default as lambdaJetPolarizationIons.cxx producer + Configurable maxZVtxPosition{"maxZVtxPosition", 10., "max Z vtx position [cm]"}; // An additional post-processing cut after derived data was written. Same default as lambdaJetPolarizationIons.cxx producer // QAs that purposefully "break" the analysis // -- All of these tests should give us zero signal if the source is truly Lambda Polarization from vortices @@ -364,8 +367,8 @@ struct lambdajetpolarizationionsderived { Configurable forcePreviousJet{"forcePreviousJet", false, "uses previous event's jet direction instead of a random sample. A baseline for fake signal removal"}; Configurable forceDatalikeJet{"forceDatalikeJet", false, "a compromise between forceRandJet and forcePreviousJet. Parameterized distribution from data"}; Configurable doMixedEventProxies{"doMixedEventProxies", false, "mix leadP/leadJet/subJet directions between events using (proxy pt, Zvtx, centrality) bins -- three independent mixings, one per proxy"}; - Configurable mixedEventWindowSize{"mixedEventWindowSize", 10, "number of neighbours for doMixedEventProxies: how many similar collisions stay eligible as mixing partners at once (shared by all 3 proxies)."}; - Configurable nProxyResamples{"nProxyResamples", 1, "The amount of resamplings of jet direction per event. Use ONLY for forceRandJet and forceDatalikeJet"}; + Configurable mixedEventWindowSize{"mixedEventWindowSize", 10, "number of neighbours for doMixedEventProxies: how many similar collisions stay eligible as mixing partners at once (shared by all 3 proxies)."}; + Configurable nProxyResamples{"nProxyResamples", 1, "The amount of resamplings of jet direction per event. Use ONLY for forceRandJet and forceDatalikeJet"}; } fakePolSwitches; // Configurable jetRForSmudging{"jetRForSmudging", 0.4, "QA quantity: the chosen R scale for the jet direction smudge"}; // Superseeded by jetR: kept the same scale in analysis and QA @@ -397,11 +400,11 @@ struct lambdajetpolarizationionsderived { ConfigurableAxis axisJetPt{ "axisJetPt", {VARIABLE_WIDTH, - 0, 2, 4, 6, 8, 10, // 2 GeV bins - 15, 20, // 5 GeV bins - 30, 40, // 10 GeV bins - 60, 80, // 20 GeV bins - 120, 160, 200}, // 40 GeV bins + 0, 2, 4, 6, 8, 10, // 2 GeV bins + 15, 20, // 5 GeV bins + 30, 40, // 10 GeV bins + 60, 80, // 20 GeV bins + 120, 160, 200}, // 40 GeV bins "Jet p_{T} (GeV)"}; ConfigurableAxis axisJetPtSigExtract{"axisJetPtSigExtract", {VARIABLE_WIDTH, 0, 5, 10, 12, 16, 20, 25, 30, 35, 40, 60, 100, 200}, "Jet p_{t} (GeV)"}; ConfigurableAxis axisEta{"axisEta", {50, -1.0f, 1.0f}, "#eta"}; @@ -426,15 +429,15 @@ struct lambdajetpolarizationionsderived { ConfigurableAxis axisLambdaMassSigExtract{ "axisLambdaMassSigExtract", {VARIABLE_WIDTH, - // Left sideband (7 bins, 0.004 width) - 1.0800, 1.0840, 1.0880, 1.0920, - 1.0960, 1.1000, 1.1040, 1.1080, - // Fine peak region (8 bins, 0.0016 width) - 1.1096, 1.1112, 1.1128, 1.1144, - 1.1160, 1.1176, 1.1192, 1.1208, - // Right sideband (7 bins, 0.004 width) - 1.1248, 1.1288, 1.1328, 1.1368, - 1.1408, 1.1448, 1.1488}, + // Left sideband (7 bins, 0.004 width) + 1.0800, 1.0840, 1.0880, 1.0920, + 1.0960, 1.1000, 1.1040, 1.1080, + // Fine peak region (8 bins, 0.0016 width) + 1.1096, 1.1112, 1.1128, 1.1144, + 1.1160, 1.1176, 1.1192, 1.1208, + // Right sideband (7 bins, 0.004 width) + 1.1248, 1.1288, 1.1328, 1.1368, + 1.1408, 1.1448, 1.1488}, "Lambda mass in GeV/c"}; // ConfigurableAxis axisLeadingParticlePtSigExtract{"axisLeadingParticlePtSigExtract", {VARIABLE_WIDTH, 0, 4, 8, 12, 16, 20, 25, 30, 35, 40, 60, 100, 200}, "Leading particle p_{T} (GeV/c)"}; // Simpler version! @@ -462,28 +465,45 @@ struct lambdajetpolarizationionsderived { // A small tracker struct for convenience -- Accumulates values for the Delta Method error estimator: struct EventDeltaTracker { - double r_int = 0.0; int n_int = 0; - std::vector r_pt, r_mass, r_dtheta; - std::vector n_pt, n_mass, n_dtheta; + double rInt = 0.0; // Ring accumulator + int nInt = 0; // Counts accumulator + std::vector rPt, rMass, rDtheta; + std::vector nPt, nMass, nDtheta; /// \brief Resizes every accumulator. Size includes ROOT's under/overflow bins, so the indices /// returned by TAxis::FindBin() (0 .. nBins+1) can be used directly for dereferencing here. - void resize(int nBinsPt, int nBinsMass, int nBinsDTheta) { - r_pt.assign(nBinsPt + 2, 0.0); r_mass.assign(nBinsMass + 2, 0.0); r_dtheta.assign(nBinsDTheta + 2, 0.0); - n_pt.assign(nBinsPt + 2, 0); n_mass.assign(nBinsMass + 2, 0); n_dtheta.assign(nBinsDTheta + 2, 0); + void resize(int nBinsPt, int nBinsMass, int nBinsDTheta) + { + rPt.assign(nBinsPt + 2, 0.0); + rMass.assign(nBinsMass + 2, 0.0); + rDtheta.assign(nBinsDTheta + 2, 0.0); + nPt.assign(nBinsPt + 2, 0); + nMass.assign(nBinsMass + 2, 0); + nDtheta.assign(nBinsDTheta + 2, 0); } - void reset() { - r_int = 0.0; n_int = 0; - std::fill(r_pt.begin(), r_pt.end(), 0.0); std::fill(r_mass.begin(), r_mass.end(), 0.0); std::fill(r_dtheta.begin(), r_dtheta.end(), 0.0); - std::fill(n_pt.begin(), n_pt.end(), 0); std::fill(n_mass.begin(), n_mass.end(), 0); std::fill(n_dtheta.begin(), n_dtheta.end(), 0); + void reset() + { + rInt = 0.0; + nInt = 0; + std::fill(rPt.begin(), rPt.end(), 0.0); + std::fill(rMass.begin(), rMass.end(), 0.0); + std::fill(rDtheta.begin(), rDtheta.end(), 0.0); + std::fill(nPt.begin(), nPt.end(), 0); + std::fill(nMass.begin(), nMass.end(), 0); + std::fill(nDtheta.begin(), nDtheta.end(), 0); } - void addV0(double ringObs, int binPt, int binMass, int binDTheta) { - r_int += ringObs; n_int += 1; - r_pt[binPt] += ringObs; n_pt[binPt] += 1; - r_mass[binMass] += ringObs; n_mass[binMass] += 1; - r_dtheta[binDTheta] += ringObs; n_dtheta[binDTheta] += 1; + void addV0(double ringObs, int binPt, int binMass, int binDTheta) + { + rInt += ringObs; + rPt[binPt] += ringObs; + rMass[binMass] += ringObs; + rDtheta[binDTheta] += ringObs; + nInt += 1; + nPt[binPt] += 1; + nMass[binMass] += 1; + nDtheta[binDTheta] += 1; } }; @@ -576,7 +596,7 @@ struct lambdajetpolarizationionsderived { // =============================== histos.add((folder + "/p2dRingObservableVsPxPy").c_str(), "<#it{R}> vs (p_{x}^{#Lambda},p_{y}^{#Lambda});p_{x}^{#Lambda} (GeV/c);p_{y}^{#Lambda} (GeV/c);<#it{R}>", kTProfile2D, {axisConfigurations.axisLambdaPx, axisConfigurations.axisLambdaPy}); histos.add((folder + "/p2dRingObservableVsPzPx").c_str(), "<#it{R}> vs (p_{z}^{#Lambda},p_{x}^{#Lambda});p_{z}^{#Lambda} (GeV/c);p_{x}^{#Lambda} (GeV/c);<#it{R}>", kTProfile2D, {axisConfigurations.axisLambdaPz, axisConfigurations.axisLambdaPx}); - // For LeadP estimators: + // For LeadP estimators: histos.add((folder + "/p2dRingObservableLeadPVsPxPy").c_str(), "<#it{R}>_{LeadP} vs (p_{x}^{#Lambda},p_{y}^{#Lambda});p_{x}^{#Lambda} (GeV/c);p_{y}^{#Lambda} (GeV/c);<#it{R}>", kTProfile2D, {axisConfigurations.axisLambdaPx, axisConfigurations.axisLambdaPy}); histos.add((folder + "/p2dRingObservableLeadPVsPzPx").c_str(), "<#it{R}>_{LeadP} vs (p_{z}^{#Lambda},p_{x}^{#Lambda});p_{z}^{#Lambda} (GeV/c);p_{x}^{#Lambda} (GeV/c);<#it{R}>", kTProfile2D, {axisConfigurations.axisLambdaPz, axisConfigurations.axisLambdaPx}); @@ -592,7 +612,7 @@ struct lambdajetpolarizationionsderived { // 1D TProfiles // =============================== histos.add((folder + "/pRingObservableDeltaPhi").c_str(), "<#it{R}> vs #Delta#varphi_{jet};#Delta#varphi_{jet};<#it{R}>", kTProfile, {axisConfigurations.axisDeltaPhi}); - // To see the actual distribution of counts in data (another differential-like shape of the distribution we are taking an average of): + // To see the actual distribution of counts in data (another differential-like shape of the distribution we are taking an average of): histos.add((folder + "/hRingObservableCounts").c_str(), "Counts vs <#it{R}>_{jet};<#it{R}>; Counts", kTH1D, {axisConfigurations.axisRingCounts}); histos.add((folder + "/pRingObservablePhiJet").c_str(), "<#it{R}> vs #varphi_{jet};#varphi_{jet};<#it{R}>", kTProfile, {axisConfigurations.axisPhi}); histos.add((folder + "/pRingObservablePhiLambda").c_str(), "<#it{R}> vs #varphi_{#Lambda};#varphi_{#Lambda};<#it{R}>", kTProfile, {axisConfigurations.axisPhi}); @@ -604,16 +624,16 @@ struct lambdajetpolarizationionsderived { histos.add((folder + "/ProxyPtDependence/pRingVsPtJet").c_str(), "<#it{R}> vs Jet #it{p}_{T};#it{p}_{T}^{Jet} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisJetPt}); histos.add((folder + "/ProxyPtDependence/pRingVsPtLeadP").c_str(), "<#it{R}> vs LeadP #it{p}_{T};#it{p}_{T}^{LeadP} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisJetPt}); histos.add((folder + "/ProxyPtDependence/pRingVsPt2ndJet").c_str(), "<#it{R}> vs SubJet #it{p}_{T};#it{p}_{T}^{SubJet} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisJetPt}); - // And some counters to be aware of the amount of Lambdas (and jets) in each pT interval: + // And some counters to be aware of the amount of Lambdas (and jets) in each pT interval: histos.add((folder + "/QA/hPtJet").c_str(), "Jet #it{p}_{T};#it{p}_{T}^{Jet} (GeV/c);Counts", kTH1D, {axisConfigurations.axisJetPt}); histos.add((folder + "/QA/hPtLeadP").c_str(), "LeadP #it{p}_{T};#it{p}_{T}^{LeadP} (GeV/c);Counts", kTH1D, {axisConfigurations.axisJetPt}); histos.add((folder + "/QA/hPt2ndJet").c_str(), "SubJet #it{p}_{T};#it{p}_{T}^{SubJet} (GeV/c);Counts", kTH1D, {axisConfigurations.axisJetPt}); - + // Splitting into positive and negative eta contributions: histos.add((folder + "/ProxyPtDependence/pRingVsPtJetVsEtaJet").c_str(), "<#it{R}> vs Jet #it{p}_{T} vs #eta_{Jet};#it{p}_{T}^{Jet} (GeV/c);#eta_{Jet};<#it{R}>", kTProfile2D, {axisConfigurations.axisJetPt, {2, -0.9, 0.9}}); histos.add((folder + "/ProxyPtDependence/pRingVsPtLeadPVsEtaLeadP").c_str(), "<#it{R}> vs LeadP #it{p}_{T} vs #eta_{LeadP};#it{p}_{T}^{LeadP} (GeV/c);#eta_{LeadP};<#it{R}>", kTProfile2D, {axisConfigurations.axisJetPt, {2, -0.9, 0.9}}); histos.add((folder + "/ProxyPtDependence/pRingVsPt2ndJetVsEta2ndJet").c_str(), "<#it{R}> vs SubJet #it{p}_{T} vs #eta_{SubJet};#it{p}_{T}^{SubJet} (GeV/c);#eta_{SubJet};<#it{R}>", kTProfile2D, {axisConfigurations.axisJetPt, {2, -0.9, 0.9}}); - // For each Lambda's eta: + // For each Lambda's eta: histos.add((folder + "/ProxyPtDependence/pRingVsPtJetVsEtaV0").c_str(), "<#it{R}> vs Jet #it{p}_{T} vs #eta_{V0};#it{p}_{T}^{Jet} (GeV/c);#eta_{V0};<#it{R}>", kTProfile2D, {axisConfigurations.axisJetPt, {2, -0.9, 0.9}}); histos.add((folder + "/ProxyPtDependence/pRingVsPtLeadPVsEtaV0").c_str(), "<#it{R}> vs LeadP #it{p}_{T} vs #eta_{V0};#it{p}_{T}^{LeadP} (GeV/c);#eta_{V0};<#it{R}>", kTProfile2D, {axisConfigurations.axisJetPt, {2, -0.9, 0.9}}); histos.add((folder + "/ProxyPtDependence/pRingVsPt2ndJetVsEtaV0").c_str(), "<#it{R}> vs SubJet #it{p}_{T} vs #eta_{V0};#it{p}_{T}^{SubJet} (GeV/c);#eta_{V0};<#it{R}>", kTProfile2D, {axisConfigurations.axisJetPt, {2, -0.9, 0.9}}); @@ -621,10 +641,10 @@ struct lambdajetpolarizationionsderived { // Rasterizing, only for LeadP the TProfile2D into two TProfile 1Ds (easier to draw with "same") histos.add((folder + "/ProxyPtDependence/pRingVsPtLeadP_PosEtaLeadP").c_str(), "<#it{R}> vs LeadP #it{p}_{T} (#eta_{LeadP}>0);#it{p}_{T}^{LeadP} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisJetPt}); histos.add((folder + "/ProxyPtDependence/pRingVsPtLeadP_NegEtaLeadP").c_str(), "<#it{R}> vs LeadP #it{p}_{T} (#eta_{LeadP}<0);#it{p}_{T}^{LeadP} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisJetPt}); - // V0 eta: + // V0 eta: histos.add((folder + "/ProxyPtDependence/pRingVsPtLeadP_PosEtaV0").c_str(), "<#it{R}> vs LeadP #it{p}_{T} (#eta_{V0}>0);#it{p}_{T}^{LeadP} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisJetPt}); histos.add((folder + "/ProxyPtDependence/pRingVsPtLeadP_NegEtaV0").c_str(), "<#it{R}> vs LeadP #it{p}_{T} (#eta_{V0}<0);#it{p}_{T}^{LeadP} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisJetPt}); - + // Looking at V0Eta and JetEta combinations (only for LeadP): histos.add((folder + "/ProxyPtDependence/pRingVsPtLeadP_PosEtaLeadP_PosEtaV0").c_str(), "<#it{R}> vs LeadP #it{p}_{T} (#eta_{LeadP}>0, #eta_{V0}>0);#it{p}_{T}^{LeadP} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisJetPt}); histos.add((folder + "/ProxyPtDependence/pRingVsPtLeadP_NegEtaLeadP_PosEtaV0").c_str(), "<#it{R}> vs LeadP #it{p}_{T} (#eta_{LeadP}<0, #eta_{V0}>0);#it{p}_{T}^{LeadP} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisJetPt}); @@ -634,10 +654,10 @@ struct lambdajetpolarizationionsderived { // Understanding eta dependence seen in pRingEtaCuts: histos.add((folder + "/EtaDependence/pRingObservableEtaLambda").c_str(), "<#it{R}> vs #eta_{#Lambda};#eta_{#Lambda};<#it{R}>", kTProfile, {axisConfigurations.axisV0EtaCoarse}); histos.add((folder + "/EtaDependence/pRingObservableEtaJet").c_str(), "<#it{R}> vs #eta_{Jet};#eta_{Jet};<#it{R}>", kTProfile, {axisConfigurations.axisEtaCoarse}); - + histos.add((folder + "/EtaDependence/pRingObservableEtaLambda2ndJet").c_str(), "<#it{R}> vs #eta_{#Lambda} (SubJet);#eta_{#Lambda};<#it{R}>", kTProfile, {axisConfigurations.axisV0EtaCoarse}); histos.add((folder + "/EtaDependence/pRingObservableEta2ndJet").c_str(), "<#it{R}> vs #eta_{SubJet};#eta_{SubJet};<#it{R}>", kTProfile, {axisConfigurations.axisEtaCoarse}); - + histos.add((folder + "/EtaDependence/pRingObservableEtaLambdaLeadP").c_str(), "<#it{R}> vs #eta_{#Lambda} (LeadP);#eta_{#Lambda};<#it{R}>", kTProfile, {axisConfigurations.axisV0EtaCoarse}); histos.add((folder + "/EtaDependence/pRingObservableEtaLeadP").c_str(), "<#it{R}> vs #eta_{LeadP};#eta_{LeadP};<#it{R}>", kTProfile, {axisConfigurations.axisEtaCoarse}); // For the leading particle: @@ -705,7 +725,7 @@ struct lambdajetpolarizationionsderived { histos.add((folder + "/EtaDependence/p2dRingObservableEtaLambdaVsEtaJet_FineBins").c_str(), "<#it{R}> vs #eta_{#Lambda} vs #eta_{Jet} (fine bins);#eta_{#Lambda};#eta_{Jet};<#it{R}>", kTProfile2D, {axisConfigurations.axisV0Eta, axisConfigurations.axisEta}); histos.add((folder + "/EtaDependence/p2dRingObservableEtaLambdaVsEtaLeadP").c_str(), "<#it{R}> vs #eta_{#Lambda} vs #eta_{LeadP};#eta_{#Lambda};#eta_{LeadP};<#it{R}>", kTProfile2D, {axisConfigurations.axisV0EtaCoarse, axisConfigurations.axisEtaCoarse}); histos.add((folder + "/EtaDependence/p2dRingObservableEtaLambdaVsEta2ndJet").c_str(), "<#it{R}> vs #eta_{#Lambda} vs #eta_{SubJet};#eta_{#Lambda};#eta_{SubJet};<#it{R}>", kTProfile2D, {axisConfigurations.axisV0EtaCoarse, axisConfigurations.axisEtaCoarse}); - // Counters for these histograms, instead of only TProfile2Ds: + // Counters for these histograms, instead of only TProfile2Ds: histos.add((folder + "/EtaDependence/h2dCounterEtaLambdaVsEtaJet").c_str(), "Counts, #eta_{#Lambda} vs #eta_{Jet};#eta_{#Lambda};#eta_{Jet};Counts", kTH2D, {axisConfigurations.axisV0EtaCoarse, axisConfigurations.axisEtaCoarse}); histos.add((folder + "/EtaDependence/h2dCounterEtaLambdaVsEtaJet_FineBins").c_str(), "Counts (fine bins), #eta_{#Lambda} vs #eta_{Jet};#eta_{#Lambda};#eta_{Jet};Counts", kTH2D, {axisConfigurations.axisV0Eta, axisConfigurations.axisEta}); histos.add((folder + "/EtaDependence/h2dCounterEtaLambdaVsEtaLeadP").c_str(), "Counts, #eta_{#Lambda} vs #eta_{LeadP};#eta_{#Lambda};#eta_{LeadP};Counts", kTH2D, {axisConfigurations.axisV0EtaCoarse, axisConfigurations.axisEtaCoarse}); @@ -744,7 +764,6 @@ struct lambdajetpolarizationionsderived { // histos.add(("QA_Numbers/" + folder + "/hValidLeadJets").c_str(), "hValidLeadJets", kTH1D, {{1,0,1}}); // TODO: Add "frequency of jets per pT" histograms either here or in the TableProducer - // Estimating error bars with the Delta Method for = A/B: // 1D Delta Method for Integrated observable: histos.add((folder + "/DeltaMethod/hIntegrated").c_str(), "Delta Method Accumulators Integrated;Component;Counts", kTH1D, {axisConfigurations.axisDeltaComponents}); @@ -755,10 +774,14 @@ struct lambdajetpolarizationionsderived { histos.add((folder + "/DeltaMethod/h2dMassVsDeltaComp").c_str(), "Delta Method vs Mass;m_{p#pi} (GeV/c^{2});Component", kTH2D, {axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisDeltaComponents}); }; // Execute local lambda to register histogram families: - if (familySwitches.doFamilyRing) addRingObservableFamily("Ring"); - if (familySwitches.doFamilyRingKinematicCuts) addRingObservableFamily("RingKinematicCuts"); - if (familySwitches.doFamilyJetKinematicCuts) addRingObservableFamily("JetKinematicCuts"); - if (familySwitches.doFamilyJetAndLambdaKinematicCuts) addRingObservableFamily("JetAndLambdaKinematicCuts"); + if (familySwitches.doFamilyRing) + addRingObservableFamily("Ring"); + if (familySwitches.doFamilyRingKinematicCuts) + addRingObservableFamily("RingKinematicCuts"); + if (familySwitches.doFamilyJetKinematicCuts) + addRingObservableFamily("JetKinematicCuts"); + if (familySwitches.doFamilyJetAndLambdaKinematicCuts) + addRingObservableFamily("JetAndLambdaKinematicCuts"); histos.add("IntegratedCuts/pRingCuts", "pRingCuts; ;<#it{R}>", kTProfile, {{4, 0, 4}}); histos.get(HIST("IntegratedCuts/pRingCuts"))->GetXaxis()->SetBinLabel(1, "All #Lambda"); @@ -799,20 +822,19 @@ struct lambdajetpolarizationionsderived { histos.get(HIST("IntegratedCuts/hCountCutsLeadingP"))->GetXaxis()->SetBinLabel(3, "|LeadP_{#eta}|<0.5"); histos.get(HIST("IntegratedCuts/hCountCutsLeadingP"))->GetXaxis()->SetBinLabel(4, "#Lambda + LeadP cuts"); - // Fake-polarization diagnostics: if (qaSwitches.doFakePolDiagnosticsQA) { // Integrated observable dependent on jet proxy #eta to unfold possible asymmetries in detector: histos.add("EtaStudy/pRingEtaCuts", "pRingEtaCuts; ;<#it{R}>", kTProfile, {{15, 0, 15}}); - histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(1, "All #Lambda"); - histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(2, "#eta_{Jet} #geq 0"); - histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(3, "#eta_{Jet} < 0"); - histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); - histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); - histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(6, "#eta_{Jet} #geq 0, #eta_{#Lambda} #geq 0"); - histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(7, "#eta_{Jet} #geq 0, #eta_{#Lambda} < 0"); - histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(8, "#eta_{Jet} < 0, #eta_{#Lambda} #geq 0"); - histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(9, "#eta_{Jet} < 0, #eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(1, "All #Lambda"); + histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(2, "#eta_{Jet} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(3, "#eta_{Jet} < 0"); + histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(6, "#eta_{Jet} #geq 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(7, "#eta_{Jet} #geq 0, #eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(8, "#eta_{Jet} < 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(9, "#eta_{Jet} < 0, #eta_{#Lambda} < 0"); histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(10, "#eta_{Jet} > R"); histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(11, "#eta_{Jet} < -R"); histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(12, "#eta_{Jet} > R, #eta_{#Lambda} #geq 0"); @@ -821,15 +843,15 @@ struct lambdajetpolarizationionsderived { histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(15, "#eta_{Jet} < -R, #eta_{#Lambda} < 0"); histos.add("EtaStudy/pRingEtaCutsSubLeadingJet", "pRingEtaCutsSubLeadingJet; ;<#it{R}>", kTProfile, {{15, 0, 15}}); - histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(1, "All #Lambda"); - histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(2, "#eta_{SubJet} #geq 0"); - histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(3, "#eta_{SubJet} < 0"); - histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); - histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); - histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(6, "#eta_{SubJet} #geq 0, #eta_{#Lambda} #geq 0"); - histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(7, "#eta_{SubJet} #geq 0, #eta_{#Lambda} < 0"); - histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(8, "#eta_{SubJet} < 0, #eta_{#Lambda} #geq 0"); - histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(9, "#eta_{SubJet} < 0, #eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(1, "All #Lambda"); + histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(2, "#eta_{SubJet} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(3, "#eta_{SubJet} < 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(6, "#eta_{SubJet} #geq 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(7, "#eta_{SubJet} #geq 0, #eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(8, "#eta_{SubJet} < 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(9, "#eta_{SubJet} < 0, #eta_{#Lambda} < 0"); histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(10, "#eta_{SubJet} > R"); histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(11, "#eta_{SubJet} < -R"); histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(12, "#eta_{SubJet} > R, #eta_{#Lambda} #geq 0"); @@ -878,7 +900,7 @@ struct lambdajetpolarizationionsderived { histos.get(HIST("EtaStudy/hFakePolCounts"))->GetYaxis()->SetBinLabel(8, "#eta_{Jet} < 0, #eta_{#Lambda} #geq 0"); histos.get(HIST("EtaStudy/hFakePolCounts"))->GetYaxis()->SetBinLabel(9, "#eta_{Jet} < 0, #eta_{#Lambda} < 0"); - // The same, but for actual signal instead of counts: + // The same, but for actual signal instead of counts: histos.add("EtaStudy/pFakePolSignalVsCosTheta", "FakePolSignal; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda};", kTProfile2D, {axisConfigurations.axisCosTheta, {9, 0, 9}}); histos.get(HIST("EtaStudy/pFakePolSignalVsCosTheta"))->GetZaxis()->SetTitle("<#it{R}>"); histos.get(HIST("EtaStudy/pFakePolSignalVsCosTheta"))->GetYaxis()->SetBinLabel(1, "All #Lambda"); @@ -892,14 +914,14 @@ struct lambdajetpolarizationionsderived { histos.get(HIST("EtaStudy/pFakePolSignalVsCosTheta"))->GetYaxis()->SetBinLabel(9, "#eta_{Jet} < 0, #eta_{#Lambda} < 0"); // Seeing the dependence between phi* = atan2(p_p_star \cdot (p_Lambda_hat \times (z_hat \cross p_Lambda)), p_p_star \cdot (z_hat \cross p_Lambda)) - // e_z = p_Lambda_hat; // e_x = normalize(z_hat cross p_Lambda); // e_y = e_z cross e_x; - // phi_star = atan2(p_p_star dot e_y, p_p_star dot e_x); + // e_z = p_Lambda_hat; // e_x = normalize(z_hat cross p_Lambda); // e_y = e_z cross e_x; + // phi_star = atan2(p_p_star dot e_y, p_p_star dot e_x); histos.add("HelicityEfficiencyQA/hFakePolCounts_CosThetaVsPhiStar", "FakePolCounts; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; #phi^{*}", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisDeltaPhi}); histos.add("HelicityEfficiencyQA/pFakePolSignal_CosThetaVsPhiStar", "FakePolSignal; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; #phi^{*}", kTProfile2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisDeltaPhi}); - // Specific counter for when we have leading jets (relates directly to pFakePolSignal_CosThetaVsPhiStar): + // Specific counter for when we have leading jets (relates directly to pFakePolSignal_CosThetaVsPhiStar): histos.add("HelicityEfficiencyQA/hFakePolCountsJet_CosThetaVsPhiStar", "FakePolCounts - HasValidLeadJet OK; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; #phi^{*}", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisDeltaPhi}); - // Similar split, but for AEE instead of HEE: + // Similar split, but for AEE instead of HEE: histos.add("EtaStudy/hCountsVsPhiStar", "FakePolCounts, AEE dependence; #phi^{*};", kTH2D, {axisConfigurations.axisDeltaPhi, {9, 0, 9}}); histos.get(HIST("EtaStudy/hCountsVsPhiStar"))->GetZaxis()->SetTitle("Counts"); histos.get(HIST("EtaStudy/hCountsVsPhiStar"))->GetYaxis()->SetBinLabel(1, "All #Lambda"); @@ -911,9 +933,9 @@ struct lambdajetpolarizationionsderived { histos.get(HIST("EtaStudy/hCountsVsPhiStar"))->GetYaxis()->SetBinLabel(7, "#eta_{Jet} #geq 0, #eta_{#Lambda} < 0"); histos.get(HIST("EtaStudy/hCountsVsPhiStar"))->GetYaxis()->SetBinLabel(8, "#eta_{Jet} < 0, #eta_{#Lambda} #geq 0"); histos.get(HIST("EtaStudy/hCountsVsPhiStar"))->GetYaxis()->SetBinLabel(9, "#eta_{Jet} < 0, #eta_{#Lambda} < 0"); - // For the ring observable as well: - // Explicitly, checking the Phi* dependence on a series of #eta cuts. - // The fake, AEE-induced, signal should be zero when integrating on full solid angle, and then have some shape for each eta slice. + // For the ring observable as well: + // Explicitly, checking the Phi* dependence on a series of #eta cuts. + // The fake, AEE-induced, signal should be zero when integrating on full solid angle, and then have some shape for each eta slice. histos.add("EtaStudy/pFakePolSignalvsPhiStar", "FakePolSignal, AEE dependence; #phi^{*};", kTProfile2D, {axisConfigurations.axisDeltaPhi, {9, 0, 9}}); histos.get(HIST("EtaStudy/pFakePolSignalvsPhiStar"))->GetZaxis()->SetTitle("<#it{R}>"); histos.get(HIST("EtaStudy/pFakePolSignalvsPhiStar"))->GetYaxis()->SetBinLabel(1, "All #Lambda"); @@ -926,7 +948,7 @@ struct lambdajetpolarizationionsderived { histos.get(HIST("EtaStudy/pFakePolSignalvsPhiStar"))->GetYaxis()->SetBinLabel(8, "#eta_{Jet} < 0, #eta_{#Lambda} #geq 0"); histos.get(HIST("EtaStudy/pFakePolSignalvsPhiStar"))->GetYaxis()->SetBinLabel(9, "#eta_{Jet} < 0, #eta_{#Lambda} < 0"); - // For the phi_Lambda - phi_D^* dependency as well: + // For the phi_Lambda - phi_D^* dependency as well: histos.add("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar", "FakePolSignal, AEE dependence; #phi_{#Lambda} - #phi_{p}^{*};", kTProfile2D, {axisConfigurations.axisDeltaPhi, {9, 0, 9}}); histos.get(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"))->GetZaxis()->SetTitle("<#it{R}>"); histos.get(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"))->GetYaxis()->SetBinLabel(1, "All #Lambda"); @@ -940,7 +962,7 @@ struct lambdajetpolarizationionsderived { histos.get(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"))->GetYaxis()->SetBinLabel(9, "#eta_{Jet} < 0, #eta_{#Lambda} < 0"); // More about possible AEE dependencies (should see an invariance with JetEta and /Jz): - // TODO: think about these error bars: do they still make sense via regular TProfile's SEM error? These are just a quick check, so wouldn't bother much about it. + // TODO: think about these error bars: do they still make sense via regular TProfile's SEM error? These are just a quick check, so wouldn't bother much about it. histos.add("HelicityEfficiencyQA/pRingVsJetZcomponent", "<#it{R}> vs #hat{t}_{z}; #hat{t}_{z}; <#it{R}>", kTProfile, {{40, -1, 1}}); // Numerically stable and can also show the sign flip (essentially the <#it{R}> vs Eta Jet plot in another scale) histos.add("HelicityEfficiencyQA/pRingOverJetZcomponent_VsJetEta", "<#it{R}>/#hat{t}_{z}; #eta_{Jet}; <#it{R}>/#hat{t}_{z}", kTProfile, {axisConfigurations.axisEtaCoarse}); histos.add("HelicityEfficiencyQA/pRingOverJetZcomponent_VsCosThetaHEE", "<#it{R}>/#hat{t}_{z} Vs cos(#theta) HEE; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; <#it{R}>/#hat{t}_{z}", kTProfile, {axisConfigurations.axisCosTheta}); @@ -948,16 +970,16 @@ struct lambdajetpolarizationionsderived { histos.add("HelicityEfficiencyQA/pRingOverJetZcomponent_VsJetEtaVsCosThetaHEE", "<#it{R}>/#hat{t}_{z}; #eta_{Jet}; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; <#it{R}>/#hat{t}_{z}", kTProfile2D, {axisConfigurations.axisEtaCoarse, axisConfigurations.axisCosTheta}); histos.add("HelicityEfficiencyQA/pRingOverJetZcomponent_VsJetEtaVsPhiStar", "<#it{R}>/#hat{t}_{z}; #eta_{Jet}; #phi^{*}; <#it{R}>/#hat{t}_{z}", kTProfile2D, {axisConfigurations.axisEtaCoarse, axisConfigurations.axisDeltaPhi}); - // Seeing if phi* is indeed influenced by the DCA between daughters: + // Seeing if phi* is indeed influenced by the DCA between daughters: histos.add("HelicityEfficiencyQA/hFakePolCountsJet_PhiStarVsDCAdau", "FakePolCounts - HasValidLeadJet OK; #phi^{*}; DCA_{V0 Daughters}", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDCAdau}); histos.add("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAdau", "FakePolSignal; #phi^{*}; DCA_{V0 Daughters}", kTProfile2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDCAdau}); - // Adding a way to check if the jet eta is positive or negative as well - // (AEE signal could be closer to zero otherwise: phi^* dependency may not make it fall to zero as we are no longer integrating in full solid angle, yet analyzing this other dependency is also important) + // Adding a way to check if the jet eta is positive or negative as well + // (AEE signal could be closer to zero otherwise: phi^* dependency may not make it fall to zero as we are no longer integrating in full solid angle, yet analyzing this other dependency is also important) histos.add("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAdauVsEtaJet", "FakePolSignal; #phi^{*}; DCA_{V0 Daughters}; #eta_{Jet} sign", kTProfile3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDCAdau, {2, -0.9, 0.9}}); histos.add("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAdauVsEtaLambda", "FakePolSignal; #phi^{*}; DCA_{V0 Daughters}; #eta_{#Lambda} sign", kTProfile3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDCAdau, {2, -0.9, 0.9}}); - // Similar checks for dcaPosToPV and dcaNegToPV, which influence AEE the strongest: + // Similar checks for dcaPosToPV and dcaNegToPV, which influence AEE the strongest: histos.add("HelicityEfficiencyQA/hFakePolCountsJet_PhiStarVsDCAProLike", "FakePolCounts - HasValidLeadJet OK; #phi^{*}; DCA_{PosPV}", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDCAdauPV}); histos.add("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAProLike", "FakePolSignal; #phi^{*}; DCA_{PosPV}", kTProfile2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDCAdauPV}); histos.add("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAProLikeVsEtaJet", "FakePolSignal; #phi^{*}; DCA_{PosPV}; #eta_{Jet} sign", kTProfile3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDCAdauPV, {2, -0.9, 0.9}}); @@ -1032,7 +1054,7 @@ struct lambdajetpolarizationionsderived { // Also including an observable that probes spectrum broadening due to the "Azimuthal Efficiency Effect": histos.add("HelicityEfficiencyQA/hLambdaMassVsPhiLambdaMinusPhiProtonStar", "m_{#Lambda}, AEE probe; m_{Inv}; #phi_{#Lambda} - #phi_{p}^{*} ; Counts", kTH2D, {axisConfigurations.axisLambdaMass, axisConfigurations.axisDeltaPhi}); histos.add("HelicityEfficiencyQA/hAntiLambdaMassVsPhiLambdaMinusPhiProtonStar", "m_{#bar{#Lambda}}, AEE probe; m_{Inv}; #phi_{#bar{#Lambda}} - #phi_{p}^{*} ; Counts", kTH2D, {axisConfigurations.axisLambdaMass, axisConfigurations.axisDeltaPhi}); - // Watching the effect on the ring observable as well: + // Watching the effect on the ring observable as well: histos.add("HelicityEfficiencyQA/p2dRing_LambdaMassVsPhiLambdaMinusPhiProtonStar", "<#it{R}>, AEE probe; m_{Inv}; #phi_{#Lambda} - #phi_{p}^{*} ; <#it{R}>", kTProfile2D, {axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisDeltaPhi}); histos.add("HelicityEfficiencyQA/p2dRing_AntiLambdaMassVsPhiLambdaMinusPhiProtonStar", "<#it{R}>, AEE probe; m_{Inv}; #phi_{#bar{#Lambda}} - #phi_{p}^{*} ; <#it{R}>", kTProfile2D, {axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisDeltaPhi}); } // end doFakePolDiagnosticsQA bookings @@ -1047,7 +1069,7 @@ struct lambdajetpolarizationionsderived { histos.add("JetKinematicsQA/hLeadJetEta", "hLeadJetEta;#eta;Counts", kTH1D, {axisConfigurations.axisEta}); histos.add("JetKinematicsQA/hSubLeadJetEta", "hSubLeadJetEta;#eta;Counts", kTH1D, {axisConfigurations.axisEta}); histos.add("JetKinematicsQA/hLeadPEta", "hLeadPEta;#eta;Counts", kTH1D, {axisConfigurations.axisEta}); - + // Proxy Phi QA: histos.add("JetKinematicsQA/hLeadJetPhi", "hLeadJetPhi;#varphi;Counts", kTH1D, {axisConfigurations.axisPhi}); histos.add("JetKinematicsQA/hSubLeadJetPhi", "hSubLeadJetPhi;#varphi;Counts", kTH1D, {axisConfigurations.axisPhi}); @@ -1066,11 +1088,11 @@ struct lambdajetpolarizationionsderived { // For building and event-mixing-like procedure similar to forceDatalikeJet: if (doJetProxy5dQA) { histos.add("JetKinematicsQA/h5dLeadJetEtaPhiPtPVzCent", "h5dLeadJetEtaPhiPtPVzCent;#eta;#phi;p_{t};Primary Vertex Z [cm];Centrality (%);Counts", kTHnSparseF, - {axisConfigurations.axisEtaCoarse, axisConfigurations.axisPhi, axisConfigurations.axisJetPt, axisConfigurations.axisPVz, axisConfigurations.axisCentrality}); + {axisConfigurations.axisEtaCoarse, axisConfigurations.axisPhi, axisConfigurations.axisJetPt, axisConfigurations.axisPVz, axisConfigurations.axisCentrality}); histos.add("JetKinematicsQA/h5dSubLeadJetEtaPhiPtPVzCent", "h5dSubLeadJetEtaPhiPtPVzCent;#eta;#phi;p_{t};Primary Vertex Z [cm];Centrality (%);Counts", kTHnSparseF, - {axisConfigurations.axisEtaCoarse, axisConfigurations.axisPhi, axisConfigurations.axisJetPt, axisConfigurations.axisPVz, axisConfigurations.axisCentrality}); + {axisConfigurations.axisEtaCoarse, axisConfigurations.axisPhi, axisConfigurations.axisJetPt, axisConfigurations.axisPVz, axisConfigurations.axisCentrality}); histos.add("JetKinematicsQA/h5dLeadPEtaPhiPtPVzCent", "h5dLeadPEtaPhiPtPVzCent;#eta;#phi;p_{t};Primary Vertex Z [cm];Centrality (%);Counts", kTHnSparseF, - {axisConfigurations.axisEtaCoarse, axisConfigurations.axisPhi, axisConfigurations.axisJetPt, axisConfigurations.axisPVz, axisConfigurations.axisCentrality}); + {axisConfigurations.axisEtaCoarse, axisConfigurations.axisPhi, axisConfigurations.axisJetPt, axisConfigurations.axisPVz, axisConfigurations.axisCentrality}); } // doMixedEventProxies QA: gauge the size of the "too few collisions per bin" problem (see resonanceMergeDF.cxx) @@ -1109,8 +1131,8 @@ struct lambdajetpolarizationionsderived { histos.add("EventMixingQA/hMixedEventLeadPSourceUsageCount", "hMixedEventLeadPSourceUsageCount;Times collision was used;Counts", kTH1D, {{50, -0.5f, 49.5f}}); histos.add("EventMixingQA/hMixedEventLeadJetSourceUsageCount", "hMixedEventLeadJetSourceUsageCount;Times collision was used;Counts", kTH1D, {{50, -0.5f, 49.5f}}); histos.add("EventMixingQA/hMixedEventSubJetSourceUsageCount", "hMixedEventSubJetSourceUsageCount;Times collision was used;Counts", kTH1D, {{50, -0.5f, 49.5f}}); - // Useful TProfiles -- the mean number of times a given source was used, as a function of eta or phi - // (more convenient than a single TH1, as it gives an average, not the raw counter) + // Useful TProfiles -- the mean number of times a given source was used, as a function of eta or phi + // (more convenient than a single TH1, as it gives an average, not the raw counter) histos.add("EventMixingQA/pMixedEventLeadPSourceUsageVsEta", "pMixedEventLeadPSourceUsageVsEta;Source #eta;", kTProfile, {axisConfigurations.axisEtaCoarse}); histos.add("EventMixingQA/pMixedEventLeadPSourceUsageVsPhi", "pMixedEventLeadPSourceUsageVsPhi;Source #varphi;", kTProfile, {axisConfigurations.axisPhi}); histos.add("EventMixingQA/pMixedEventLeadJetSourceUsageVsEta", "pMixedEventLeadJetSourceUsageVsEta;Source #eta;", kTProfile, {axisConfigurations.axisEtaCoarse}); @@ -1123,7 +1145,7 @@ struct lambdajetpolarizationionsderived { mAxisPt = histos.get(HIST("Ring/DeltaMethod/h2dLambdaPtVsDeltaComp"))->GetXaxis(); mAxisMass = histos.get(HIST("Ring/DeltaMethod/h2dMassVsDeltaComp"))->GetXaxis(); mAxisDTheta = histos.get(HIST("Ring/DeltaMethod/h2dDeltaThetaVsDeltaComp"))->GetXaxis(); - for (EventDeltaTracker* tracker : {&trackRing, &trackRingKinCuts, &trackJetKinCuts, &trackJetLambdaKinCuts}) + for (auto const& tracker : {&trackRing, &trackRingKinCuts, &trackJetKinCuts, &trackJetLambdaKinCuts}) tracker->resize(mAxisPt->GetNbins(), mAxisMass->GetNbins(), mAxisDTheta->GetNbins()); } @@ -1147,24 +1169,30 @@ struct lambdajetpolarizationionsderived { // Pre-computed values for helper below: const double smearSigma = 0.05 * jetR; // Normalized eta weights spanning -0.9 to 0.9 (46 bins) and phi weights for the 50 bins spanning [0, 2pi]: - static constexpr std::array etaLeadPWeights = {{0.01782505198123039,0.01826119427561306,0.01890047073124532,0.01942224199989093,0.01993380780273602,0.02047274597515178,0.02094135547756474,0.02140259654932778,0.02178490245182078,0.02218346916434517,0.02252861343224298,0.02278214932340838,0.02297395476452691,0.02311861709583109,0.02322295246943318,0.02329274166449468,0.02335344516182264,0.02335971904087711,0.02340163522424806,0.02352868368676468,0.02345839093195849,0.02295391718536531,0.02306543716698383,0.02293131780181040,0.02265098126991631,0.02318893563623931,0.02322457978177088,0.02316188601954564,0.02308812992419636,0.02305831097751334,0.02300695504254397,0.02296398287895598,0.02286956017362988,0.02274321888486162,0.02254049413132752,0.02233024817234042,0.02204811997596179,0.02170252191737713,0.02130517220864903,0.02086349641950970,0.02036590755841183,0.01987946074337928,0.01934550418314029,0.01879487530409137,0.01812577211265362,0.01766945805710696}}; - static constexpr std::array phiLeadPWeights = {{0.01907529231698144,0.02044679008716434,0.01948618554713157,0.02046288887443206,0.02142057576726765,0.01961361841611185,0.02174981627752354,0.02160945937846856,0.02027231236207667,0.02153799273983672,0.02107609996106984,0.02001899849606885,0.02196516947939817,0.02047654705787587,0.02059561382369167,0.02148625027035289,0.02001510925188416,0.02183059661361331,0.02111406548114694,0.01881826666371129,0.02112797285609031,0.02034071592473790,0.01968993216337670,0.02126946766383166,0.02025580366897253,0.02061136834815962,0.02083881238552183,0.01994368379135331,0.02046280212696892,0.02131631148368759,0.01967275608960357,0.02064965975476278,0.02155758091535052,0.02012557837329328,0.02084718400924722,0.02065094443305587,0.01969546187428681,0.02136531489392276,0.02084491659074202,0.01970883494847568,0.02080349992636310,0.02098440611808174,0.02159984658204099,0.02045819959592145,0.01952755742791563,0.02166002908586709,0.02017512590511229,0.01932658087972190,0.02108933627170306,0.02019288778648293}}; - // Build discrete eta distribution for sampling: - std::discrete_distribution etaLeadPDist{etaLeadPWeights.begin(),etaLeadPWeights.end()}; // Will be passed as the etaDist variable + static constexpr std::array etaLeadPWeights = {{0.01782505198123039, 0.01826119427561306, 0.01890047073124532, 0.01942224199989093, 0.01993380780273602, 0.02047274597515178, 0.02094135547756474, 0.02140259654932778, 0.02178490245182078, 0.02218346916434517, 0.02252861343224298, 0.02278214932340838, 0.02297395476452691, 0.02311861709583109, 0.02322295246943318, 0.02329274166449468, 0.02335344516182264, 0.02335971904087711, 0.02340163522424806, 0.02352868368676468, 0.02345839093195849, 0.02295391718536531, 0.02306543716698383, 0.02293131780181040, 0.02265098126991631, 0.02318893563623931, 0.02322457978177088, 0.02316188601954564, 0.02308812992419636, 0.02305831097751334, 0.02300695504254397, 0.02296398287895598, 0.02286956017362988, 0.02274321888486162, 0.02254049413132752, 0.02233024817234042, 0.02204811997596179, 0.02170252191737713, 0.02130517220864903, 0.02086349641950970, 0.02036590755841183, 0.01987946074337928, 0.01934550418314029, 0.01879487530409137, 0.01812577211265362, 0.01766945805710696}}; + static constexpr std::array phiLeadPWeights = {{0.01907529231698144, 0.02044679008716434, 0.01948618554713157, 0.02046288887443206, 0.02142057576726765, 0.01961361841611185, 0.02174981627752354, 0.02160945937846856, 0.02027231236207667, 0.02153799273983672, 0.02107609996106984, 0.02001899849606885, 0.02196516947939817, 0.02047654705787587, 0.02059561382369167, 0.02148625027035289, 0.02001510925188416, 0.02183059661361331, 0.02111406548114694, 0.01881826666371129, 0.02112797285609031, 0.02034071592473790, 0.01968993216337670, 0.02126946766383166, 0.02025580366897253, 0.02061136834815962, 0.02083881238552183, 0.01994368379135331, 0.02046280212696892, 0.02131631148368759, 0.01967275608960357, 0.02064965975476278, 0.02155758091535052, 0.02012557837329328, 0.02084718400924722, 0.02065094443305587, 0.01969546187428681, 0.02136531489392276, 0.02084491659074202, 0.01970883494847568, 0.02080349992636310, 0.02098440611808174, 0.02159984658204099, 0.02045819959592145, 0.01952755742791563, 0.02166002908586709, 0.02017512590511229, 0.01932658087972190, 0.02108933627170306, 0.02019288778648293}}; + // Build discrete eta distribution for sampling: + std::discrete_distribution etaLeadPDist{etaLeadPWeights.begin(), etaLeadPWeights.end()}; // Will be passed as the etaDist variable std::discrete_distribution phiLeadPDist{phiLeadPWeights.begin(), phiLeadPWeights.end()}; - + /// \brief One jet proxy (leadP, leadJet or subJet). Bound by reference so applyProxyDistortion() edits in-place. struct ProxyState { bool& hasValidProxy; //! whether the proxy is valid. Re-evaluated against minPtThreshold after distortion - float& pt; float& eta; float& phi; + float& pt; + float& eta; + float& phi; XYZVector& unitVec; //! the proxy direction as a unit vector }; /// \brief The cache slots shared by forcePreviousJet and doMixedEventProxies, also bound by reference. /// \note Fallback skips this event using ProxyState::hasValidProxy. - /// \note forcePreviousJet updates these here for the next collision, in a simplistic event event mixing approach. + /// \note forcePreviousJet updates these here for the next collision, in a simplistic event event mixing approach. // doMixedEventProxies instead overwrites the caller's cache fields with this collision's mixed proxy right before the call. - struct ProxyCacheRef { bool& hadProxy; float& eta; float& phi; }; + struct ProxyCacheRef { + bool& hadProxy; + float& eta; + float& phi; + }; /// \brief Applies whichever fakePolSwitches distortion is active to a jet-proxy direction, in place. No-op if none are on. /// \param proxy input/edited in-place: the proxy's kinematics, overwritten by whichever distortion is active. @@ -1174,8 +1202,9 @@ struct lambdajetpolarizationionsderived { /// \note Shared across leadP/leadJet/subJet: the caller resolves which proxy-specific procedure (e.g., LUT for evtMixing) applies. // Helper to modify the jet direction for QA and for spurious signal baseline removal tests: inline void applyProxyDistortion(ProxyState proxy, float minPtThreshold, ProxyCacheRef cache, - std::discrete_distribution& etaDist, std::discrete_distribution& phiDist, std::mt19937& rng){ - if (!fakePolSwitches.forcePerpToJet && !fakePolSwitches.forceJetDirectionSmudge && !fakePolSwitches.forceRandJet && !fakePolSwitches.forcePreviousJet && !fakePolSwitches.forceDatalikeJet && !fakePolSwitches.doMixedEventProxies)[[likely]] { + std::discrete_distribution& etaDist, std::discrete_distribution& phiDist, std::mt19937& rng) + { + if (!fakePolSwitches.forcePerpToJet && !fakePolSwitches.forceJetDirectionSmudge && !fakePolSwitches.forceRandJet && !fakePolSwitches.forcePreviousJet && !fakePolSwitches.forceDatalikeJet && !fakePolSwitches.doMixedEventProxies) [[likely]] { return; // Skip this function if none of the modifications are actually being executed! } @@ -1188,7 +1217,7 @@ struct lambdajetpolarizationionsderived { } else { perpVec = XYZVector(0., proxy.unitVec.Z(), -proxy.unitVec.Y()).Unit(); // Cross product with X-axis (1, 0, 0) } - + // Now we rotate around the jet axis by a random angle, just to make sure we are not introducing a bias in the QA: // We will use Rodrigues' rotation formula (v_rot = v*cos(randomAngle) + (Jet \cross v)*sin(randomAngle)) const double randomAngle = randomGen.Uniform(0., o2::constants::math::TwoPI); @@ -1208,12 +1237,12 @@ struct lambdajetpolarizationionsderived { } else { perpVec = XYZVector(0., proxy.unitVec.Z(), -proxy.unitVec.Y()).Unit(); // Cross product with X-axis (1, 0, 0) } - + // Rotate perpVec around the jet axis by a uniform random azimuth to get // a uniformly distributed random perpendicular direction (the smear axis): const double smearAzimuth = randomGen.Uniform(0., o2::constants::math::TwoPI); XYZVector smearAxis = perpVec * std::cos(smearAzimuth) + proxy.unitVec.Cross(perpVec) * std::sin(smearAzimuth); - + // 2) draw the smearing polar angle from a Gaussian: // sigma = 0.05 * R --> ~68% of events smeared within 5% of R, // ~95% of events smeared within 10% of R, @@ -1222,7 +1251,7 @@ struct lambdajetpolarizationionsderived { // -- R is not really an angle: just gives me a scale for the angular shift I am performing. // -- This may pose problems for forward jets: a small displacement in \theta becomes a large displacement in \eta space const double smearAngle = std::abs(randomGen.Gaus(0., smearSigma)); - + // 3) rotate the jet axis by smearAngle around smearAxis. // Rodrigues is v_rot = v*cos(theta) + (k \cross v)*sin(theta) + k*(k \cdot v)*(1-cos(theta)) // But the last term vanishes because smearAxis is perpendicular to unitVec: @@ -1231,7 +1260,7 @@ struct lambdajetpolarizationionsderived { } else if (fakePolSwitches.forceRandJet) { // This randomization was made different for each proxy (LeadP, LeadJet, SubLeadJet): bear that in mind! // 1) Uniformly sample cos(theta) and phi to ensure an isotropic distribution (could also use TRandom::Sphere as well, but may be slower) - // Notice that uniformly sampling theta would make the distribution non-isotropic, thus we use cos(theta)! + // Notice that uniformly sampling theta would make the distribution non-isotropic, thus we use cos(theta)! const double cosTheta = randomGen.Uniform(-1., 1.); const double sinTheta = std::sqrt(1. - cosTheta * cosTheta); const double randPhi = randomGen.Uniform(0., o2::constants::math::TwoPI); @@ -1246,16 +1275,16 @@ struct lambdajetpolarizationionsderived { const double cosPhi = std::cos(cache.phi); proxy.unitVec = XYZVector(cosPhi * inverseCoshEta, sinPhi * inverseCoshEta, std::tanh(cache.eta)); } - + // Update cache with the current event data: cache.hadProxy = proxy.hasValidProxy; - cache.eta = proxy.eta; + cache.eta = proxy.eta; cache.phi = proxy.phi; - + // Current event cannot use previous-jet mixing if previous event lacked a proxy if (!usableProxy) proxy.hasValidProxy = false; - } else if (fakePolSwitches.forceDatalikeJet){ // A compromise between forceRandJet and forcePreviousJet, using data-like weights for sampling jets + } else if (fakePolSwitches.forceDatalikeJet) { // A compromise between forceRandJet and forcePreviousJet, using data-like weights for sampling jets const float etaMin = -0.92f; const float etaBinWidth = 0.04f; constexpr float phiBinWidth = constants::math::TwoPI / 50.f; @@ -1290,21 +1319,23 @@ struct lambdajetpolarizationionsderived { // Recalculating pT, phi and eta after distortions: // (without this, later kinematic selections make no sense at all! In the forceRandJet case, the ring observable would always sum zero) if (proxy.hasValidProxy) { // If you don't check this flag here, the "if (!usableProxy)" change would be silently overwritten - if (!fakePolSwitches.forcePreviousJet && !fakePolSwitches.forceDatalikeJet){ // In these two cases, we already know the eta and phi variables. No need to recompute + if (!fakePolSwitches.forcePreviousJet && !fakePolSwitches.forceDatalikeJet) { + // (In these two cases, we already know the eta and phi variables. No need to recompute) // Calculating total jet momentum, which should be preserved, just to recalculate the new jet Pt: const double mag = proxy.pt * std::cosh(proxy.eta); - + // For stability (Rho is the projection on the transverse plane, badly behaved for high |eta|): const double transverseNorm = std::max(proxy.unitVec.Rho(), 1e-12); // Stability guard - + // Recalculate pT: proxy.pt = mag * transverseNorm; - + // Recalculate phi: - proxy.phi = std::atan2(proxy.unitVec.Y(), proxy.unitVec.X()); - if (proxy.phi < 0.0f) - proxy.phi += o2::constants::math::TwoPI; // Notice that atan2 outputs values from -PI to PI, and DataModel convention was from 0 to 2PI as per FastJet's phi() getter - + proxy.phi = RecoDecay::constrainAngle(std::atan2(proxy.unitVec.Y(), proxy.unitVec.X()), 0.0f); // atan2 outputs [-PI, PI), and DataModel convention was [0,2PI) as per FastJet's phi() getter + // proxy.phi = std::atan2(proxy.unitVec.Y(), proxy.unitVec.X()); + // if (proxy.phi < 0.0f) + // proxy.phi += o2::constants::math::TwoPI; + // Stable eta computation: // Stabler than 0.5 * std::log((1. + cosTheta) / (1. - cosTheta)) proxy.eta = std::asinh(proxy.unitVec.Z() / transverseNorm); @@ -1317,26 +1348,45 @@ struct lambdajetpolarizationionsderived { // Caching the previous collision's jet directions -- An optional feature for forcePreviousJet QA: struct PrevJetCache { - bool hadLeadJet; float leadJetEta; float leadJetPhi; // Leading jet - bool hadSubJet; float subJetEta; float subJetPhi; // Subleading jet - bool hadLeadP; float leadPEta; float leadPPhi; // Leading particle + // Leading jet + bool hadLeadJet; + float leadJetEta; + float leadJetPhi; + // Subleading jet + bool hadSubJet; + float subJetEta; + float subJetPhi; + // Leading particle + bool hadLeadP; + float leadPEta; + float leadPPhi; }; // Per-collision leading/subleading jet, computed once per dataframe and shared by both the main loop below // (instead of re-scanning RingJets once per resampling pass) and the leadJet/subJet event mixing functions: struct JetProxyCache { - bool hasValidLeadingJet = false; float leadingJetPt = -1.f; float leadingJetEta = 0.f; float leadingJetPhi = 0.f; - bool hasValidSubJet = false; float subleadingJetPt = -1.f; float subleadingJetEta = 0.f; float subleadingJetPhi = 0.f; + bool hasValidLeadingJet = false; + float leadingJetPt = -1.f; + float leadingJetEta = 0.f; + float leadingJetPhi = 0.f; + bool hasValidSubJet = false; + float subleadingJetPt = -1.f; + float subleadingJetEta = 0.f; + float subleadingJetPhi = 0.f; }; // A simple struct for doMixedEventProxies. Each proxy (leadP/leadJet/subJet) gets its own independent cache. - struct MixedProxyInfo { float eta; float phi; int64_t sourceCollisionId; }; + struct MixedProxyInfo { + float eta; + float phi; + int64_t sourceCollisionId; + }; /// \brief Uniform random pick among each target's candidate window, via reservoir sampling /// (cannibalization of proxies by neighbouring collisions in Continuous Readout is not a worry as ITS hits are being demanded) /// \note Reuses the task's own rng member rather than a separate generator per proxy. void reservoirInsert(std::unordered_map& candidateCount, std::unordered_map& lut, - int64_t targetId, const MixedProxyInfo& candidate) + int64_t targetId, const MixedProxyInfo& candidate) { int& nSeen = candidateCount[targetId]; ++nSeen; @@ -1357,20 +1407,20 @@ struct lambdajetpolarizationionsderived { // Defining filters for events: Filter zvtxFilter = (nabs(o2::aod::lambdajetpol::zvtx) < maxZVtxPosition); - + // Preslices for correct collisions association: // (tested custom grouping and performs worse here) Preslice perColJets = o2::aod::lambdajetpol::ringCollisionId; Preslice perColV0s = o2::aod::lambdajetpol::ringCollisionId; Preslice perColLeadPs = o2::aod::lambdajetpol::ringCollisionId; - // For doMixedEventProxies: + // For doMixedEventProxies: SliceCache mixCache; /// \brief Main analysis loop: for each collision, rebuilds the leading jet/particle proxies (with optional fakePolSwitches distortions), then loops over V0s computing the ring observable and polarization-vector profiles for every enabled kinematic-cut family. void processPolarizationData(soa::Filtered const& collisions, o2::aod::RingJets const& jets, o2::aod::RingLaV0s const& v0s, o2::aod::RingLeadPs const& leadPs) { // Event mixing, built once per dataframe - // Simplistic event mixing using previous collision: + // Simplistic event mixing using previous collision: PrevJetCache prevJetCache{}; // Initializing struct before the collisions loop. This zero-initializes, so bools start as false // This should not be used along nProxyResamples > 1, as it does not apply to that case. @@ -1428,22 +1478,30 @@ struct lambdajetpolarizationionsderived { return rows.size() > 0 ? rows.begin().leadParticlePt() : -999.f; }; using LeadPBinningType = FlexibleBinningPolicy, - o2::aod::lambdajetpol::Zvtx, decltype(getMixLeadPPt), decltype(getMixCentrality)>; + 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}, + true}; // Ignore overflows true // SameKindPair defaults to CombinationsBlockStrictlyUpperSameIndexPolicy, so no same-event mixing should happen: SameKindPair leadPPair{ - leadPBinning, fakePolSwitches.mixedEventWindowSize, -1, collisions, std::make_tuple(leadPs), &mixCache}; + leadPBinning, fakePolSwitches.mixedEventWindowSize, -1, collisions, std::make_tuple(leadPs), &mixCache}; std::unordered_map leadPCandidateCount; for (auto it = leadPPair.begin(); it != leadPPair.end(); ++it) { - auto& [c1, leadP1, c2, leadP2] = *it; // Iterates over collision pairs and leading particle pairs (structured binding) + auto& [c1, leadP1, c2, leadP2] = *it; // Iterates over collision pairs and leading particle pairs (structured binding) if (leadP1.size() > 0 && leadP2.size() > 0) { // There should always be at least one leadP, given the overflow exclusion above float eta1 = 0.f, phi1 = 0.f, eta2 = 0.f, phi2 = 0.f; - for (auto const& lp : leadP1) { eta1 = lp.leadParticleEta(); phi1 = lp.leadParticlePhi(); break; } // Retrieves the first entry - for (auto const& lp : leadP2) { eta2 = lp.leadParticleEta(); phi2 = lp.leadParticlePhi(); break; } + for (auto const& lp : leadP1) { + eta1 = lp.leadParticleEta(); + phi1 = lp.leadParticlePhi(); + break; + } // Retrieves the first entry + for (auto const& lp : leadP2) { + eta2 = lp.leadParticleEta(); + phi2 = lp.leadParticlePhi(); + break; + } // Each side of the pair is one more candidate for the other collision's reservoir: reservoirInsert(leadPCandidateCount, mixedLeadPByCollision, c1.globalIndex(), {eta2, phi2, c2.globalIndex()}); reservoirInsert(leadPCandidateCount, mixedLeadPByCollision, c2.globalIndex(), {eta1, phi1, c1.globalIndex()}); @@ -1464,15 +1522,15 @@ struct lambdajetpolarizationionsderived { return cacheIt->second.leadingJetPt; }; using LeadJetBinningType = FlexibleBinningPolicy, - o2::aod::lambdajetpol::Zvtx, decltype(getMixLeadJetPt), decltype(getMixCentrality)>; + 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}, + true}; // 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. SameKindPair leadJetPair{ - leadJetBinning, fakePolSwitches.mixedEventWindowSize, -1, collisions, std::make_tuple(jets), &mixCache}; + leadJetBinning, fakePolSwitches.mixedEventWindowSize, -1, collisions, std::make_tuple(jets), &mixCache}; std::unordered_map leadJetCandidateCount; for (auto it = leadJetPair.begin(); it != leadJetPair.end(); ++it) { @@ -1500,13 +1558,13 @@ struct lambdajetpolarizationionsderived { return cacheIt->second.subleadingJetPt; }; using SubJetBinningType = FlexibleBinningPolicy, - o2::aod::lambdajetpol::Zvtx, decltype(getMixSubJetPt), decltype(getMixCentrality)>; + 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}, + true}; SameKindPair subJetPair{ - subJetBinning, fakePolSwitches.mixedEventWindowSize, -1, collisions, std::make_tuple(jets), &mixCache}; + subJetBinning, fakePolSwitches.mixedEventWindowSize, -1, collisions, std::make_tuple(jets), &mixCache}; std::unordered_map subJetCandidateCount; for (auto it = subJetPair.begin(); it != subJetPair.end(); ++it) { @@ -1574,776 +1632,775 @@ struct lambdajetpolarizationionsderived { } } - for (int idxResampling = 0; idxResampling < fakePolSwitches.nProxyResamples; idxResampling++){ // resampling loop for forceRandJet and forceDatalikeJet - for (auto const& collision : collisions) { - const float collisionPVz = collision.zvtx(); - - const auto collId = collision.globalIndex(); // The self-index accessor - const float centrality = getCentrality(collision); - - // Used this dummy for backwards compatibility, under the reasonable assumption that the field points always in the same *direction* in the used runs - // (it is not worth it to fetch and store the magnetic field in the datamodel) - float magField = 1.f; // Purely geometric. - - // Slice jets, V0s and leading particle belonging to this collision: - // (global collision indices repeat a lot, but they are unique to a same TimeFrame (TF) subfolder in the derived data) - auto v0sInColl = v0s.sliceBy(perColV0s, collId); - auto leadPsInColl = leadPs.sliceBy(perColLeadPs, collId); - - // Check if there is at least one V0 and one jet in the collision: - // (in the way I fill the table, there is always at least one V0 in - // the stored collision, but the jets table can not be filled for - // that collision, and a collision may not be filled when the jets - // table is. Be mindful of that!) - // 1) Require at least one V0: - const int nLambdaLikeV0s = v0sInColl.size(); // Caching this variable, as it will be reused in the loop - // In the latest datamodel format, only unambiguous V0s (Lambda XOR antiLambda) are saved, - // so the number of V0s in the collision table is the number of Lambdas/antiLambda identified - // by the table producer. - if (!nLambdaLikeV0s) - continue; - - // 2) We require at least one leading particle: - // (The goal is to see how diluted the signal gets with events which don't even have a loose FastJet jet) - // (The leading particle is built from all tracks that passed the pseudojet - // selection, so it exists whenever FastJet was run on this collision. - // Events that have a leading jet always have a leading particle too, but - // the converse is not true: events can have a leading particle with no jet - // if no jet survives the pT threshold/the background subtraction) - // (At least that is the case when minLeadParticlePt = 0) - float leadPPt = -1.; // pT = -1 means "table entry not found for this collision". - float leadPEta = 0.; - float leadPPhi = 0.; - float leadPPx = 0., leadPPy = 0., leadPPz = 0.; - for (auto const& lp : leadPsInColl) { - // Table should contain exactly one entry per collision, but we break immediately to be safe: - leadPPt = lp.leadParticlePt(); - leadPEta = lp.leadParticleEta(); - leadPPhi = lp.leadParticlePhi(); - // Using dynamic columns to make code cleaner: - leadPPx = lp.leadParticlePx(); - leadPPy = lp.leadParticlePy(); - leadPPz = lp.leadParticlePz(); - } - // // Discard events with no leading particle (FastJet didn't even run in these cases!): - // if (leadPPt < 0.) - // continue; - - // Apply minimum pT selection for the leading particle (not necessarily the same as in derived data builder. Can be a stricter cut!): - bool hasValidLeadingP = leadPPt > minLeadParticlePt; - - // Build leading particle unit vector, outside the V0 loop for performance. - XYZVector leadPUnitVec(1., 0., 0.); // dummy (overwritten below when hasValidLeadingP) - if (hasValidLeadingP) { - leadPUnitVec = XYZVector(leadPPx, leadPPy, leadPPz).Unit(); - - // Apply distortion logic: - const bool hadPreviousProxy = prevJetCache.hadLeadP; - - // doMixedEventProxies: get this collision's mixing partner (if any) from the LUT built above: - // (this check is performed only if hasValidLeadingP is true for performance, as the LeadPPt matching for the event mixing would also demand a minimum jet pT) - if (fakePolSwitches.doMixedEventProxies) { - auto itMix = mixedLeadPByCollision.find(collId); - prevJetCache.hadLeadP = (itMix != mixedLeadPByCollision.end()); // Check if this event had a valid mixing target. Reusing prevJetCache - if (prevJetCache.hadLeadP) { - prevJetCache.leadPEta = itMix->second.eta; - prevJetCache.leadPPhi = itMix->second.phi; - } - histos.fill(HIST("EventMixingQA/hMixedEventLeadPOutcome"), prevJetCache.hadLeadP ? 1 : 0); - if (prevJetCache.hadLeadP) { // Only fill the comparison histograms on an actual hit: - histos.fill(HIST("EventMixingQA/h2dMixedLeadPEtaVsLeadPEta"), prevJetCache.leadPEta, leadPEta); - histos.fill(HIST("EventMixingQA/h2dMixedLeadPPhiVsLeadPPhi"), prevJetCache.leadPPhi, leadPPhi); - } - } - - applyProxyDistortion({hasValidLeadingP, leadPPt, leadPEta, leadPPhi, leadPUnitVec}, - minLeadParticlePt, {prevJetCache.hadLeadP, prevJetCache.leadPEta, prevJetCache.leadPPhi}, - etaLeadPDist, phiLeadPDist, rng); - - // Fill distorted-proxy QA histograms: - // Do not gate on the post-distortion hasValidLeadingP (a pT cut) value here! - if (!fakePolSwitches.forcePreviousJet || hadPreviousProxy){ - histos.fill(HIST("JetKinematicsQA/hLeadPEta"), leadPEta); - histos.fill(HIST("JetKinematicsQA/hLeadPPhi"), leadPPhi); - histos.fill(HIST("JetKinematicsQA/hJetCounterPtLeadP"), leadPPt); + for (int idxResampling = 0; idxResampling < fakePolSwitches.nProxyResamples; idxResampling++) { // resampling loop for forceRandJet and forceDatalikeJet + for (auto const& collision : collisions) { + const float collisionPVz = collision.zvtx(); + + const auto collId = collision.globalIndex(); // The self-index accessor + const float centrality = getCentrality(collision); + + // Used this dummy for backwards compatibility, under the reasonable assumption that the field points always in the same *direction* in the used runs + // (it is not worth it to fetch and store the magnetic field in the datamodel) + float magField = 1.f; // Purely geometric. + + // Slice jets, V0s and leading particle belonging to this collision: + // (global collision indices repeat a lot, but they are unique to a same TimeFrame (TF) subfolder in the derived data) + auto v0sInColl = v0s.sliceBy(perColV0s, collId); + auto leadPsInColl = leadPs.sliceBy(perColLeadPs, collId); + + // Check if there is at least one V0 and one jet in the collision: + // (in the way I fill the table, there is always at least one V0 in + // the stored collision, but the jets table can not be filled for + // that collision, and a collision may not be filled when the jets + // table is. Be mindful of that!) + // 1) Require at least one V0: + const int nLambdaLikeV0s = v0sInColl.size(); // Caching this variable, as it will be reused in the loop + // In the latest datamodel format, only unambiguous V0s (Lambda XOR antiLambda) are saved, + // so the number of V0s in the collision table is the number of Lambdas/antiLambda identified + // by the table producer. + if (!nLambdaLikeV0s) + continue; - histos.fill(HIST("JetKinematicsQA/h2dLeadPEtaVsPVz"), leadPEta, collisionPVz); - if (doJetProxy5dQA) - histos.fill(HIST("JetKinematicsQA/h5dLeadPEtaPhiPtPVzCent"), leadPEta, leadPPhi, leadPPt, collisionPVz, centrality); + // 2) We require at least one leading particle: + // (The goal is to see how diluted the signal gets with events which don't even have a loose FastJet jet) + // (The leading particle is built from all tracks that passed the pseudojet + // selection, so it exists whenever FastJet was run on this collision. + // Events that have a leading jet always have a leading particle too, but + // the converse is not true: events can have a leading particle with no jet + // if no jet survives the pT threshold/the background subtraction) + // (At least that is the case when minLeadParticlePt = 0) + float leadPPt = -1.; // pT = -1 means "table entry not found for this collision". + float leadPEta = 0.; + float leadPPhi = 0.; + float leadPPx = 0., leadPPy = 0., leadPPz = 0.; + for (auto const& lp : leadPsInColl) { + // Table should contain exactly one entry per collision, but we break immediately to be safe: + leadPPt = lp.leadParticlePt(); + leadPEta = lp.leadParticleEta(); + leadPPhi = lp.leadParticlePhi(); + // Using dynamic columns to make code cleaner: + leadPPx = lp.leadParticlePx(); + leadPPy = lp.leadParticlePy(); + leadPPz = lp.leadParticlePz(); } - } + // // Discard events with no leading particle (FastJet didn't even run in these cases!): + // if (leadPPt < 0.) + // continue; - // 3) Fetching leading jet and subleading jet -- Resolved once per collision in the jetProxyByCollision pre-pass: - const JetProxyCache& jetProxies = jetProxyByCollision.at(collId); // .at() makes it sure we don't create a new key in the map - float leadingJetPt = jetProxies.leadingJetPt; - float subleadingJetPt = jetProxies.subleadingJetPt; - - // Defining local bools that may be changed by applyProxyDistortion: - bool hasValidLeadingJet = jetProxies.hasValidLeadingJet; - bool hasValidSubJet = jetProxies.hasValidSubJet; - - // Build jet vectors (only when the corresponding jet exists): - // Dummy initialisations are safe: all jet-dependent fills are gated on hasValidLeadingJet / hasValidSubJet. - float leadingJetEta = 0.; - float leadingJetPhi = 0.; - XYZVector leadingJetUnitVec(1., 0., 0.); // dummy (overwritten below) - if (hasValidLeadingJet) { - leadingJetEta = jetProxies.leadingJetEta; - leadingJetPhi = jetProxies.leadingJetPhi; - // Rebuild the direction from the cached eta/phi (cheaper than calling jetPx() internal getters and then normalizing with .Unit()): - const double inverseCoshEta = 1.0 / std::cosh(leadingJetEta); - leadingJetUnitVec = XYZVector(std::cos(leadingJetPhi) * inverseCoshEta, std::sin(leadingJetPhi) * inverseCoshEta, std::tanh(leadingJetEta)); - - // Apply distortion logic: - const bool hadPreviousProxy = prevJetCache.hadLeadJet; - if (fakePolSwitches.doMixedEventProxies) { - // Get this collision's leading-jet mixing partner (if any) from the LUT built above: - auto itMix = mixedLeadJetByCollision.find(collId); - prevJetCache.hadLeadJet = (itMix != mixedLeadJetByCollision.end()); - if (prevJetCache.hadLeadJet) { - prevJetCache.leadJetEta = itMix->second.eta; - prevJetCache.leadJetPhi = itMix->second.phi; - } - histos.fill(HIST("EventMixingQA/hMixedEventLeadJetOutcome"), prevJetCache.hadLeadJet ? 1 : 0); - if (prevJetCache.hadLeadJet) { - histos.fill(HIST("EventMixingQA/h2dMixedLeadJetEtaVsLeadJetEta"), prevJetCache.leadJetEta, leadingJetEta); - histos.fill(HIST("EventMixingQA/h2dMixedLeadJetPhiVsLeadJetPhi"), prevJetCache.leadJetPhi, leadingJetPhi); - } - } - applyProxyDistortion({hasValidLeadingJet, leadingJetPt, leadingJetEta, leadingJetPhi, leadingJetUnitVec}, - minLeadJetPt, {prevJetCache.hadLeadJet, prevJetCache.leadJetEta, prevJetCache.leadJetPhi}, - etaLeadPDist, phiLeadPDist, rng); - - // Fill distorted-proxy QA histograms: - // Do not gate on the post-distortion hasValidLeadingJet (a pT cut) value here! - if (!fakePolSwitches.forcePreviousJet || hadPreviousProxy) { - histos.fill(HIST("JetKinematicsQA/hLeadJetEta"), leadingJetEta); - histos.fill(HIST("JetKinematicsQA/hLeadJetPhi"), leadingJetPhi); - histos.fill(HIST("JetKinematicsQA/hJetCounterPtJet"), leadingJetPt); - - histos.fill(HIST("JetKinematicsQA/h2dLeadJetEtaVsPVz"), leadingJetEta, collisionPVz); - if (doJetProxy5dQA) - histos.fill(HIST("JetKinematicsQA/h5dLeadJetEtaPhiPtPVzCent"), leadingJetEta, leadingJetPhi, leadingJetPt, collisionPVz, centrality); - } - } + // Apply minimum pT selection for the leading particle (not necessarily the same as in derived data builder. Can be a stricter cut!): + bool hasValidLeadingP = leadPPt > minLeadParticlePt; - float subleadingJetEta = 0.; - float subleadingJetPhi = 0.; - XYZVector subJetUnitVec(1., 0., 0.); - if (hasValidSubJet) { - subleadingJetEta = jetProxies.subleadingJetEta; - subleadingJetPhi = jetProxies.subleadingJetPhi; - const double inverseCoshEtaSub = 1.0 / std::cosh(subleadingJetEta); - subJetUnitVec = XYZVector(std::cos(subleadingJetPhi) * inverseCoshEtaSub, std::sin(subleadingJetPhi) * inverseCoshEtaSub, std::tanh(subleadingJetEta)); - - // Apply distortion logic: - const bool hadPreviousProxy = prevJetCache.hadSubJet; - if (fakePolSwitches.doMixedEventProxies) { - // Get this collision's subleading-jet mixing partner (if any) from the LUT built above: - auto itMix = mixedSubJetByCollision.find(collId); - prevJetCache.hadSubJet = (itMix != mixedSubJetByCollision.end()); - if (prevJetCache.hadSubJet) { - prevJetCache.subJetEta = itMix->second.eta; - prevJetCache.subJetPhi = itMix->second.phi; - } - histos.fill(HIST("EventMixingQA/hMixedEventSubJetOutcome"), prevJetCache.hadSubJet ? 1 : 0); - if (prevJetCache.hadSubJet) { - histos.fill(HIST("EventMixingQA/h2dMixedSubJetEtaVsSubJetEta"), prevJetCache.subJetEta, subleadingJetEta); - histos.fill(HIST("EventMixingQA/h2dMixedSubJetPhiVsSubJetPhi"), prevJetCache.subJetPhi, subleadingJetPhi); + // Build leading particle unit vector, outside the V0 loop for performance. + XYZVector leadPUnitVec(1., 0., 0.); // dummy (overwritten below when hasValidLeadingP) + if (hasValidLeadingP) { + leadPUnitVec = XYZVector(leadPPx, leadPPy, leadPPz).Unit(); + + // Apply distortion logic: + const bool hadPreviousProxy = prevJetCache.hadLeadP; + + // doMixedEventProxies: get this collision's mixing partner (if any) from the LUT built above: + // (this check is performed only if hasValidLeadingP is true for performance, as the LeadPPt matching for the event mixing would also demand a minimum jet pT) + if (fakePolSwitches.doMixedEventProxies) { + auto itMix = mixedLeadPByCollision.find(collId); + prevJetCache.hadLeadP = (itMix != mixedLeadPByCollision.end()); // Check if this event had a valid mixing target. Reusing prevJetCache + if (prevJetCache.hadLeadP) { + prevJetCache.leadPEta = itMix->second.eta; + prevJetCache.leadPPhi = itMix->second.phi; + } + histos.fill(HIST("EventMixingQA/hMixedEventLeadPOutcome"), prevJetCache.hadLeadP ? 1 : 0); + if (prevJetCache.hadLeadP) { // Only fill the comparison histograms on an actual hit: + histos.fill(HIST("EventMixingQA/h2dMixedLeadPEtaVsLeadPEta"), prevJetCache.leadPEta, leadPEta); + histos.fill(HIST("EventMixingQA/h2dMixedLeadPPhiVsLeadPPhi"), prevJetCache.leadPPhi, leadPPhi); + } } - } - applyProxyDistortion({hasValidSubJet, subleadingJetPt, subleadingJetEta, subleadingJetPhi, subJetUnitVec}, - minSubLeadJetPt, {prevJetCache.hadSubJet, prevJetCache.subJetEta, prevJetCache.subJetPhi}, - etaLeadPDist, phiLeadPDist, rng); - - - // Fill distorted-proxy QA histograms: - // Do not gate on the post-distortion hasValidSubJet (a pT cut) value here! - if (!fakePolSwitches.forcePreviousJet || hadPreviousProxy) { - histos.fill(HIST("JetKinematicsQA/hSubLeadJetEta"), subleadingJetEta); - histos.fill(HIST("JetKinematicsQA/hSubLeadJetPhi"), subleadingJetPhi); - histos.fill(HIST("JetKinematicsQA/hJetCounterPt2ndJet"), subleadingJetPt); - - histos.fill(HIST("JetKinematicsQA/h2dSubLeadJetEtaVsPVz"), subleadingJetEta, collisionPVz); - if (doJetProxy5dQA) - histos.fill(HIST("JetKinematicsQA/h5dSubLeadJetEtaPhiPtPVzCent"), subleadingJetEta, subleadingJetPhi, subleadingJetPt, collisionPVz, centrality); - } - } - - // (jet eta cuts only meaningful when the jet actually exists) - const bool kinematicJetCheck = hasValidLeadingJet && (std::abs(leadingJetEta) < 0.5); - const bool kinematic2ndJetCheck = hasValidSubJet && (std::abs(subleadingJetEta) < 0.5); - const bool kinematicLeadPCheck = hasValidLeadingP && (std::abs(leadPEta) < 0.5); - - // Quick bools that are useful for detector asymmetry QA: - const bool jetEtaPos = hasValidLeadingJet && (leadingJetEta >= 0.); // Only perform >= check if has validJet - const bool subJetEtaPos = hasValidSubJet && (subleadingJetEta >= 0.); - const bool leadPEtaPos = hasValidLeadingP && (leadPEta >= 0.); - - // Stricter QA version of the bools -- Jets have a radius that makes it possible eta_{jet} > 0, yet half its tracks are in eta < 0 - // (This does not apply to leading particles, obviously. They have no substructure in eta) - const bool jetEtaStrict = hasValidLeadingJet && (std::abs(leadingJetEta) >= jetR); - const bool subJetEtaStrict = hasValidSubJet && (std::abs(subleadingJetEta) >= jetR); - // If one was to define bools for each side of the detector (not needed in the current if-else structure on TProfile fills) - // const bool jetEtaStrictPos = jetEtaPos && jetEtaStrict; - // const bool jetEtaStrictNeg = !jetEtaPos && jetEtaStrict; - // const bool subJetEtaStrictPos = subJetEtaPos && subJetEtaStrict; - // const bool subJetEtaStrictNeg = !subJetEtaPos && subJetEtaStrict; - - // Fetching number of Lambda-like V0s in collision (must be known before full loop, to fill "pRingVsNV0s"): - // int nLambdaLikeV0s = 0; - // for (auto const& v0 : v0sInColl) { - // if (v0.isLambda() ^ v0.isAntiLambda()){ // XOR (only the non-ambiguous candidates) - // nLambdaLikeV0s++; - // } - // } - // Code above was superseeded: new datamodel does not store ambiguous candidates! - // The new getter comes at the very start of processPolarizationData() now. - - // Initialize delta method accumulators (reset for new dataframe): - for (EventDeltaTracker* tracker : {&trackRing, &trackRingKinCuts, &trackJetKinCuts, &trackJetLambdaKinCuts}) - tracker->reset(); - for (auto const& v0 : v0sInColl) { - const bool isLambda = v0.isLambda(); // true: is a Lambda. false: is an antiLambda. - // For now, removing the ambiguous candidates from the analysis. New datamodel does NOT save ambiguous candidates. - // (From Podolanski-Armenteros plots, the population of ambiguous is ~3.8% without TOF, and without - // competing mass rejection. From those, ~99% seem to be K0s, so no real gain in considering the - // ambiguous candidates in the analysis) - // const bool isAntiLambda = v0.isAntiLambda(); // No longer used! - // if (isLambda && isAntiLambda) continue; - const float v0pt = v0.v0Pt(); - const float v0eta = v0.v0Eta(); - const float v0phi = v0.v0Phi(); - const float dcaDau = v0.dcaV0Daughters(); - - float v0LambdaLikeMass = v0.massV0(); - float protonLikePt = 0; - float protonLikeEta = 0; - float protonLikePhi = 0; - float protonLikeDCADauToPV = 0; - float pionLikeDCADauToPV = 0; - if (isLambda) { - if (!analyseLambda) - continue; - protonLikePt = v0.posPt(); - protonLikeEta = v0.posEta(); - protonLikePhi = v0.posPhi(); - protonLikeDCADauToPV = v0.dcaPosToPV(); - pionLikeDCADauToPV = v0.dcaNegToPV(); - } else { // Guaranteed to be an antiLambda candidate, not an ambiguous candidate - if (!analyseAntiLambda) - continue; - protonLikePt = v0.negPt(); - protonLikeEta = v0.negEta(); - protonLikePhi = v0.negPhi(); - protonLikeDCADauToPV = v0.dcaNegToPV(); - pionLikeDCADauToPV = v0.dcaPosToPV(); - } - PtEtaPhiMVector lambdaLike4Vec(v0pt, v0eta, v0phi, v0LambdaLikeMass); - PtEtaPhiMVector protonLike4Vec(protonLikePt, protonLikeEta, protonLikePhi, protonMass); - const float lambdaRapidity = lambdaLike4Vec.Rapidity(); // For further kinematic selections - const int v0InMassPeak = (v0LambdaLikeMass >= 1.1020593 && v0LambdaLikeMass <= 1.1288811); // Very naive estimator. Based on signal extractions from outside this code + applyProxyDistortion({hasValidLeadingP, leadPPt, leadPEta, leadPPhi, leadPUnitVec}, + minLeadParticlePt, {prevJetCache.hadLeadP, prevJetCache.leadPEta, prevJetCache.leadPPhi}, + etaLeadPDist, phiLeadPDist, rng); - // Inexpensive estimates of signal extraction effects on the observable: - if (excludeOutOfPeakQA && !v0InMassPeak) - continue; - else if (excludeInPeakQA && v0InMassPeak) - continue; + // Fill distorted-proxy QA histograms: + // Do not gate on the post-distortion hasValidLeadingP (a pT cut) value here! + if (!fakePolSwitches.forcePreviousJet || hadPreviousProxy) { + histos.fill(HIST("JetKinematicsQA/hLeadPEta"), leadPEta); + histos.fill(HIST("JetKinematicsQA/hLeadPPhi"), leadPPhi); + histos.fill(HIST("JetKinematicsQA/hJetCounterPtLeadP"), leadPPt); - // Boosting proton into lambda frame: - XYZVector beta = lambdaLike4Vec.BoostToCM(); // Boost trivector that goes from laboratory frame to Lambda's rest frame (convenient new function, different from TLorentzVector's BoostVector()) - auto protonLike4VecStar = ROOT::Math::VectorUtil::boost(protonLike4Vec, beta); - - // Getting unit vectors and 3-components: - XYZVector lambdaLike3Vec = lambdaLike4Vec.Vect(); - auto lambdaLikeUnit3Vec = lambdaLike3Vec.Unit(); - XYZVector protonLikeStarUnit3Vec = protonLike4VecStar.Vect().Unit(); - - // Lab-frame Lambda momentum components -- Not for polarization, but for actual momenta plotting in XY and ZX planes: - // (Used for the (px,py) and (pz,px) polarization vector-field / ring 2D profiles) - const float v0px = lambdaLike3Vec.X(); - const float v0py = lambdaLike3Vec.Y(); - const float v0pz = lambdaLike3Vec.Z(); - - // Calculating fake polarization ("negative helicity problem") estimator: - // (this estimator is calculated outside of any gate, as it does not depend on jet proxy used) - float cosFakePol = protonLikeStarUnit3Vec.Dot(lambdaLikeUnit3Vec); - - // Calculating the azimuthal angle between the Lambda and the proton: - float deltaPhiLambdaProtonStar = wrapToPiFast(lambdaLikeUnit3Vec.Phi() - protonLikeStarUnit3Vec.Phi()); // Phi is defined from -PI to PI in ROOT::Math::Cartesian3D, thus kept the wrapping - - // Calculating the phi* angle: - // e_z = p_Lambda_hat; // e_x = normalize(z_hat cross p_Lambda); // e_y = e_z cross e_x; - // // phi_star = atan2(p_p_star dot e_y, p_p_star dot e_x); - // XYZVector e_x(-lambdaLikeUnit3Vec.Y(), lambdaLikeUnit3Vec.X(), 0.); // Same as e_x = zHat.Cross(lambdaLikeUnit3Vec); - // XYZVector e_y = lambdaLikeUnit3Vec.Cross(e_x); // e_y completes the right-handed coordinate system (e_z is lambdaLikeUnit3Vec) - // float pX = protonLikeStarUnit3Vec.Dot(e_x); - // float pY = protonLikeStarUnit3Vec.Dot(e_y); - // float phiStar = std::atan2(pY, pX); - // Faster implementation: - // pX = p_y * L_x - p_x * L_y - float pX = protonLikeStarUnit3Vec.Y() * lambdaLikeUnit3Vec.X() - protonLikeStarUnit3Vec.X() * lambdaLikeUnit3Vec.Y(); - // pY = p_z - L_z * (p_proton_star dot p_lambda_hat) - float pY = protonLikeStarUnit3Vec.Z() - lambdaLikeUnit3Vec.Z() * cosFakePol; // (Reusing cosFakePol calculated earlier!) - float phiStar = std::atan2(pY, pX); // This will give an output from -PI to PI - - if (qaSwitches.doFakePolDiagnosticsQA) { - // Another reconstruction efficiency measure: - // (Formula is: p_{Lambda} \cross p_{Daughter}^{*} \cdot B, and B points in Z) - if (analyseMagField) { - auto crossGeom = lambdaLike3Vec.Cross(protonLikeStarUnit3Vec); - const bool positiveGeom = crossGeom.Z() * magField > 0; - - if (isLambda && positiveGeom) - histos.fill(HIST("HelicityEfficiencyQA/hLambdaMassDecayGeomRight"), v0LambdaLikeMass); - else if (isLambda && !positiveGeom) - histos.fill(HIST("HelicityEfficiencyQA/hLambdaMassDecayGeomLeft"), v0LambdaLikeMass); - else if (!isLambda && positiveGeom) - histos.fill(HIST("HelicityEfficiencyQA/hAntiLambdaMassDecayGeomRight"), v0LambdaLikeMass); - else - histos.fill(HIST("HelicityEfficiencyQA/hAntiLambdaMassDecayGeomLeft"), v0LambdaLikeMass); + histos.fill(HIST("JetKinematicsQA/h2dLeadPEtaVsPVz"), leadPEta, collisionPVz); + if (doJetProxy5dQA) + histos.fill(HIST("JetKinematicsQA/h5dLeadPEtaPhiPtPVzCent"), leadPEta, leadPPhi, leadPPt, collisionPVz, centrality); } - - // Measuring the AEE effect differentially (azimuthal efficiency effect, which causes different V0 topologies to be enhanced/suppressed): - if (isLambda) - histos.fill(HIST("HelicityEfficiencyQA/hLambdaMassVsPhiLambdaMinusPhiProtonStar"), v0LambdaLikeMass, deltaPhiLambdaProtonStar); - else - histos.fill(HIST("HelicityEfficiencyQA/hAntiLambdaMassVsPhiLambdaMinusPhiProtonStar"), v0LambdaLikeMass, deltaPhiLambdaProtonStar); - // AEE and HEE correlation: - histos.fill(HIST("HelicityEfficiencyQA/hFakePolCounts_CosThetaVsPhiStar"), cosFakePol, phiStar); - } // end doFakePolDiagnosticsQA (per-V0 AEE/HEE) - - // Useful kinematic bools: - const bool lambdaEtaPos = v0eta >= 0.; - const bool pTLambdaCheck = v0pt > 0.5 && v0pt < 1.5; - const bool rapidityLambdaCheck = std::abs(lambdaRapidity) < 0.5; - const bool kinematicLambdaCheck = pTLambdaCheck && rapidityLambdaCheck; - - //////////////////////////////////////////// - // Ring observable: Leading particle proxy - // Only computed when a valid leading particle exists (pT > minLeadParticlePt) - //////////////////////////////////////////// - float ringObservableLeadP = 0.; - float deltaPhiLeadP = 0.; - float deltaThetaLeadP = 0.; - float cosDeltaThetaLeadP = 0.; - if (hasValidLeadingP) { - // Cross product - XYZVector crossLeadP = leadPUnitVec.Cross(lambdaLike3Vec); - ringObservableLeadP = protonLikeStarUnit3Vec.Dot(crossLeadP) / crossLeadP.R(); - // Adding the prefactor related to the CP-violating decay (decay constants have different signs) - if (!fakePolSwitches.forcePolSignQA) - ringObservableLeadP *= (isLambda) ? polPrefactorLambda : polPrefactorAntiLambda; - else - ringObservableLeadP *= (isLambda) ? polPrefactorLambda : -1.0 * polPrefactorAntiLambda; - // Angular variables - deltaPhiLeadP = wrapToPiFast(v0phi - leadPPhi); // Wrapped to [-PI, PI), for convenience - - cosDeltaThetaLeadP = leadPUnitVec.Dot(lambdaLikeUnit3Vec); // Uses the pre-calculated unit vectors to avoid recomputation - deltaThetaLeadP = std::acos(cosDeltaThetaLeadP); // 3D angular separation. Same as ROOT::Math::VectorUtil::Angle(leadPUnitVec, lambdaLike3Vec); } - ////////////////////////////////////////// - // Ring observable: Leading jet proxy - // Only computed when a leading jet exists in this collision. - ////////////////////////////////////////// - float ringObservable = 0.; - float deltaPhiJet = 0.; - float deltaEtaJet = 0.; - float deltaThetaJet = 0.; - float cosDeltaThetaJet = 0.; - float Jz = 0.; - float ringObservableOverJetZ = 0.; - if (hasValidLeadingJet) { - // Cross product - XYZVector cross = leadingJetUnitVec.Cross(lambdaLike3Vec); - ringObservable = protonLikeStarUnit3Vec.Dot(cross) / cross.R(); - // Adding prefactor - if (!fakePolSwitches.forcePolSignQA) - ringObservable *= (isLambda) ? polPrefactorLambda : polPrefactorAntiLambda; - else - ringObservable *= (isLambda) ? polPrefactorLambda : -1.0 * polPrefactorAntiLambda; - // Angular variables - deltaPhiJet = wrapToPiFast(v0phi - leadingJetPhi); - deltaEtaJet = v0eta - leadingJetEta; - - cosDeltaThetaJet = leadingJetUnitVec.Dot(lambdaLikeUnit3Vec); - deltaThetaJet = std::acos(cosDeltaThetaJet); - - // Testing an invariance -- /\hat{t}_z -- Possible source of an artificial (trivial) sign flip in the observable: - Jz = leadingJetUnitVec.Z(); - if (std::abs(Jz) > 1e-4) - ringObservableOverJetZ = ringObservable/Jz; - else - ringObservableOverJetZ = 0.0; // A simple guard. May not be the best, but works - - // // A second projection schema, where e_x = normalize(t_hat cross p_Lambda), using t_hat instead of z_hat (different from phi*): - // // (this decomposes in an orthogonal basis related to the jet coordinates) - // XYZVector ez = lambdaLikeUnit3Vec; - // XYZVector ex = leadingJetUnitVec.Cross(lambdaLike3Vec); - // XYZVector ey = ez.Cross(ex); - - // ringObservableExProjection = ringObservable; - // ringObservableEyProjection = - // // Assuming that energy can get inside the average in: - // // P_Lambda \cdot p_Lambda = E_Lambda/m_Lambda * P_Lambda^* \cdot p_Lambda = * p_Lambda - // ringObservableEzProjection = cosFakePol * lambdaLike4Vec.E()/v0LambdaLikeMass; - } + // 3) Fetching leading jet and subleading jet -- Resolved once per collision in the jetProxyByCollision pre-pass: + const JetProxyCache& jetProxies = jetProxyByCollision.at(collId); // .at() makes it sure we don't create a new key in the map + float leadingJetPt = jetProxies.leadingJetPt; + float subleadingJetPt = jetProxies.subleadingJetPt; - ////////////////////////////////////////// - // Ring observable: Subleading jet proxy - // Only computed when a subleading jet exists in this collision. - ////////////////////////////////////////// - float ringObservable2ndJet = 0.; - float deltaPhi2ndJet = 0.; - float deltaTheta2ndJet = 0.; - float cosDeltaTheta2ndJet = 0.; - if (hasValidSubJet) { - XYZVector cross2ndJet = subJetUnitVec.Cross(lambdaLike3Vec); - ringObservable2ndJet = protonLikeStarUnit3Vec.Dot(cross2ndJet) / cross2ndJet.R(); - // Adding prefactor - if (!fakePolSwitches.forcePolSignQA) - ringObservable2ndJet *= (isLambda) ? polPrefactorLambda : polPrefactorAntiLambda; - else - ringObservable2ndJet *= (isLambda) ? polPrefactorLambda : -1.0 * polPrefactorAntiLambda; - // Angular variables - deltaPhi2ndJet = wrapToPiFast(v0phi - subleadingJetPhi); - cosDeltaTheta2ndJet = subJetUnitVec.Dot(lambdaLikeUnit3Vec); - deltaTheta2ndJet = std::acos(cosDeltaTheta2ndJet); - } + // Defining local bools that may be changed by applyProxyDistortion: + bool hasValidLeadingJet = jetProxies.hasValidLeadingJet; + bool hasValidSubJet = jetProxies.hasValidSubJet; - // Calculating polarization observables (in the Lambda frame, because that is easier -- does not require boosts): - // To be precise, not actually the polarization, but a part of the summand in P^*_Lambda = (3/\alpha_Lambda) * - float polStarX = 0, polStarY = 0, polStarZ = 0; // Dummy initialization: avoid warnings in compile time - if (isLambda) { // Notice there is no need to check analyseLambda again due to previous checks. - polStarX = polPrefactorLambda * protonLikeStarUnit3Vec.X(); - polStarY = polPrefactorLambda * protonLikeStarUnit3Vec.Y(); - polStarZ = polPrefactorLambda * protonLikeStarUnit3Vec.Z(); - } else { - polStarX = polPrefactorAntiLambda * protonLikeStarUnit3Vec.X(); - polStarY = polPrefactorAntiLambda * protonLikeStarUnit3Vec.Y(); - polStarZ = polPrefactorAntiLambda * protonLikeStarUnit3Vec.Z(); + // Build jet vectors (only when the corresponding jet exists): + // Dummy initialisations are safe: all jet-dependent fills are gated on hasValidLeadingJet / hasValidSubJet. + float leadingJetEta = 0.; + float leadingJetPhi = 0.; + XYZVector leadingJetUnitVec(1., 0., 0.); // dummy (overwritten below) + if (hasValidLeadingJet) { + leadingJetEta = jetProxies.leadingJetEta; + leadingJetPhi = jetProxies.leadingJetPhi; + // Rebuild the direction from the cached eta/phi (cheaper than calling jetPx() internal getters and then normalizing with .Unit()): + const double inverseCoshEta = 1.0 / std::cosh(leadingJetEta); + leadingJetUnitVec = XYZVector(std::cos(leadingJetPhi) * inverseCoshEta, std::sin(leadingJetPhi) * inverseCoshEta, std::tanh(leadingJetEta)); + + // Apply distortion logic: + const bool hadPreviousProxy = prevJetCache.hadLeadJet; + if (fakePolSwitches.doMixedEventProxies) { + // Get this collision's leading-jet mixing partner (if any) from the LUT built above: + auto itMix = mixedLeadJetByCollision.find(collId); + prevJetCache.hadLeadJet = (itMix != mixedLeadJetByCollision.end()); + if (prevJetCache.hadLeadJet) { + prevJetCache.leadJetEta = itMix->second.eta; + prevJetCache.leadJetPhi = itMix->second.phi; + } + histos.fill(HIST("EventMixingQA/hMixedEventLeadJetOutcome"), prevJetCache.hadLeadJet ? 1 : 0); + if (prevJetCache.hadLeadJet) { + histos.fill(HIST("EventMixingQA/h2dMixedLeadJetEtaVsLeadJetEta"), prevJetCache.leadJetEta, leadingJetEta); + histos.fill(HIST("EventMixingQA/h2dMixedLeadJetPhiVsLeadJetPhi"), prevJetCache.leadJetPhi, leadingJetPhi); + } + } + applyProxyDistortion({hasValidLeadingJet, leadingJetPt, leadingJetEta, leadingJetPhi, leadingJetUnitVec}, + minLeadJetPt, {prevJetCache.hadLeadJet, prevJetCache.leadJetEta, prevJetCache.leadJetPhi}, + etaLeadPDist, phiLeadPDist, rng); + + // Fill distorted-proxy QA histograms: + // Do not gate on the post-distortion hasValidLeadingJet (a pT cut) value here! + if (!fakePolSwitches.forcePreviousJet || hadPreviousProxy) { + histos.fill(HIST("JetKinematicsQA/hLeadJetEta"), leadingJetEta); + histos.fill(HIST("JetKinematicsQA/hLeadJetPhi"), leadingJetPhi); + histos.fill(HIST("JetKinematicsQA/hJetCounterPtJet"), leadingJetPt); + + histos.fill(HIST("JetKinematicsQA/h2dLeadJetEtaVsPVz"), leadingJetEta, collisionPVz); + if (doJetProxy5dQA) + histos.fill(HIST("JetKinematicsQA/h5dLeadJetEtaPhiPtPVzCent"), leadingJetEta, leadingJetPhi, leadingJetPt, collisionPVz, centrality); + } } - float v0phiToFillHists = wrapToPiFast(v0phi); // A short wrap to reuse some predefined axes - - // Fill ring histograms: (1D, lambda 2D correlations and jet 2D correlations): - if (hasValidLeadingP) { - if (familySwitches.doFamilyRing) { - RING_OBSERVABLE_LEADP_FILL_LIST(APPLY_HISTO_FILL, "Ring") // Notice the usage of macros! If you change the variable names, this WILL break the code! - // No, there should NOT be any ";" here! Read the macro definition for an explanation + float subleadingJetEta = 0.; + float subleadingJetPhi = 0.; + XYZVector subJetUnitVec(1., 0., 0.); + if (hasValidSubJet) { + subleadingJetEta = jetProxies.subleadingJetEta; + subleadingJetPhi = jetProxies.subleadingJetPhi; + const double inverseCoshEtaSub = 1.0 / std::cosh(subleadingJetEta); + subJetUnitVec = XYZVector(std::cos(subleadingJetPhi) * inverseCoshEtaSub, std::sin(subleadingJetPhi) * inverseCoshEtaSub, std::tanh(subleadingJetEta)); + + // Apply distortion logic: + const bool hadPreviousProxy = prevJetCache.hadSubJet; + if (fakePolSwitches.doMixedEventProxies) { + // Get this collision's subleading-jet mixing partner (if any) from the LUT built above: + auto itMix = mixedSubJetByCollision.find(collId); + prevJetCache.hadSubJet = (itMix != mixedSubJetByCollision.end()); + if (prevJetCache.hadSubJet) { + prevJetCache.subJetEta = itMix->second.eta; + prevJetCache.subJetPhi = itMix->second.phi; + } + histos.fill(HIST("EventMixingQA/hMixedEventSubJetOutcome"), prevJetCache.hadSubJet ? 1 : 0); + if (prevJetCache.hadSubJet) { + histos.fill(HIST("EventMixingQA/h2dMixedSubJetEtaVsSubJetEta"), prevJetCache.subJetEta, subleadingJetEta); + histos.fill(HIST("EventMixingQA/h2dMixedSubJetPhiVsSubJetPhi"), prevJetCache.subJetPhi, subleadingJetPhi); + } } - histos.fill(HIST("IntegratedCuts/pRingCutsLeadingP"), 0, ringObservableLeadP); // First bin of comparison - histos.fill(HIST("IntegratedCuts/hCountCutsLeadingP"), 0); - - // Filling checks that rely on Eta>0 or Eta<0 checks for V0 and LeadingP eta: - if (familySwitches.doFamilyRing) { - RING_OBSERVABLE_LEADP_ETA_SPLIT_FILL_LIST("Ring", leadPEtaPos, lambdaEtaPos); + applyProxyDistortion({hasValidSubJet, subleadingJetPt, subleadingJetEta, subleadingJetPhi, subJetUnitVec}, + minSubLeadJetPt, {prevJetCache.hadSubJet, prevJetCache.subJetEta, prevJetCache.subJetPhi}, + etaLeadPDist, phiLeadPDist, rng); + + // Fill distorted-proxy QA histograms: + // Do not gate on the post-distortion hasValidSubJet (a pT cut) value here! + if (!fakePolSwitches.forcePreviousJet || hadPreviousProxy) { + histos.fill(HIST("JetKinematicsQA/hSubLeadJetEta"), subleadingJetEta); + histos.fill(HIST("JetKinematicsQA/hSubLeadJetPhi"), subleadingJetPhi); + histos.fill(HIST("JetKinematicsQA/hJetCounterPt2ndJet"), subleadingJetPt); + + histos.fill(HIST("JetKinematicsQA/h2dSubLeadJetEtaVsPVz"), subleadingJetEta, collisionPVz); + if (doJetProxy5dQA) + histos.fill(HIST("JetKinematicsQA/h5dSubLeadJetEtaPhiPtPVzCent"), subleadingJetEta, subleadingJetPhi, subleadingJetPt, collisionPVz, centrality); } } - if (familySwitches.doFamilyRing) { - POLARIZATION_PROFILE_FILL_LIST(APPLY_HISTO_FILL, "Ring") - } - // Binary search using the pre-fetched axes for delta method of error bar estimation: - int binPt = 0; // Dummy declarations - int binMass = 0; - int binDTheta = 0; - if (hasValidLeadingJet) { - if (familySwitches.doFamilyRing) { - RING_OBSERVABLE_FILL_LIST(APPLY_HISTO_FILL, "Ring") - } - histos.fill(HIST("IntegratedCuts/pRingCuts"), 0, ringObservable); - histos.fill(HIST("IntegratedCuts/hCountCuts"), 0); - histos.fill(HIST("IntegratedCuts/pRingVsNV0s"), nLambdaLikeV0s, ringObservable); - histos.fill(HIST("hNV0sVsCentrality"), nLambdaLikeV0s, centrality); - - // Properly fetching values as they are needed: - binPt = mAxisPt->FindBin(v0pt); - binMass = mAxisMass->FindBin(v0LambdaLikeMass); - binDTheta = mAxisDTheta->FindBin(deltaThetaJet); - if (familySwitches.doFamilyRing) { - trackRing.addV0(ringObservable, binPt, binMass, binDTheta); + // (jet eta cuts only meaningful when the jet actually exists) + const bool kinematicJetCheck = hasValidLeadingJet && (std::abs(leadingJetEta) < 0.5); + const bool kinematic2ndJetCheck = hasValidSubJet && (std::abs(subleadingJetEta) < 0.5); + const bool kinematicLeadPCheck = hasValidLeadingP && (std::abs(leadPEta) < 0.5); + + // Quick bools that are useful for detector asymmetry QA: + const bool jetEtaPos = hasValidLeadingJet && (leadingJetEta >= 0.); // Only perform >= check if has validJet + const bool subJetEtaPos = hasValidSubJet && (subleadingJetEta >= 0.); + const bool leadPEtaPos = hasValidLeadingP && (leadPEta >= 0.); + + // Stricter QA version of the bools -- Jets have a radius that makes it possible eta_{jet} > 0, yet half its tracks are in eta < 0 + // (This does not apply to leading particles, obviously. They have no substructure in eta) + const bool jetEtaStrict = hasValidLeadingJet && (std::abs(leadingJetEta) >= jetR); + const bool subJetEtaStrict = hasValidSubJet && (std::abs(subleadingJetEta) >= jetR); + // If one was to define bools for each side of the detector (not needed in the current if-else structure on TProfile fills) + // const bool jetEtaStrictPos = jetEtaPos && jetEtaStrict; + // const bool jetEtaStrictNeg = !jetEtaPos && jetEtaStrict; + // const bool subJetEtaStrictPos = subJetEtaPos && subJetEtaStrict; + // const bool subJetEtaStrictNeg = !subJetEtaPos && subJetEtaStrict; + + // Fetching number of Lambda-like V0s in collision (must be known before full loop, to fill "pRingVsNV0s"): + // int nLambdaLikeV0s = 0; + // for (auto const& v0 : v0sInColl) { + // if (v0.isLambda() ^ v0.isAntiLambda()){ // XOR (only the non-ambiguous candidates) + // nLambdaLikeV0s++; + // } + // } + // Code above was superseeded: new datamodel does not store ambiguous candidates! + // The new getter comes at the very start of processPolarizationData() now. + + // Initialize delta method accumulators (reset for new dataframe): + for (auto const& tracker : {&trackRing, &trackRingKinCuts, &trackJetKinCuts, &trackJetLambdaKinCuts}) + tracker->reset(); + for (auto const& v0 : v0sInColl) { + const bool isLambda = v0.isLambda(); // true: is a Lambda. false: is an antiLambda. + // For now, removing the ambiguous candidates from the analysis. New datamodel does NOT save ambiguous candidates. + // (From Podolanski-Armenteros plots, the population of ambiguous is ~3.8% without TOF, and without + // competing mass rejection. From those, ~99% seem to be K0s, so no real gain in considering the + // ambiguous candidates in the analysis) + // const bool isAntiLambda = v0.isAntiLambda(); // No longer used! + // if (isLambda && isAntiLambda) continue; + const float v0pt = v0.v0Pt(); + const float v0eta = v0.v0Eta(); + const float v0phi = v0.v0Phi(); + const float dcaDau = v0.dcaV0Daughters(); + + float v0LambdaLikeMass = v0.massV0(); + float protonLikePt = 0; + float protonLikeEta = 0; + float protonLikePhi = 0; + float protonLikeDCADauToPV = 0; + float pionLikeDCADauToPV = 0; + if (isLambda) { + if (!analyseLambda) + continue; + protonLikePt = v0.posPt(); + protonLikeEta = v0.posEta(); + protonLikePhi = v0.posPhi(); + protonLikeDCADauToPV = v0.dcaPosToPV(); + pionLikeDCADauToPV = v0.dcaNegToPV(); + } else { // Guaranteed to be an antiLambda candidate, not an ambiguous candidate + if (!analyseAntiLambda) + continue; + protonLikePt = v0.negPt(); + protonLikeEta = v0.negEta(); + protonLikePhi = v0.negPhi(); + protonLikeDCADauToPV = v0.dcaNegToPV(); + pionLikeDCADauToPV = v0.dcaPosToPV(); } + PtEtaPhiMVector lambdaLike4Vec(v0pt, v0eta, v0phi, v0LambdaLikeMass); + PtEtaPhiMVector protonLike4Vec(protonLikePt, protonLikeEta, protonLikePhi, ProtonMass); + const float lambdaRapidity = lambdaLike4Vec.Rapidity(); // For further kinematic selections + const int v0InMassPeak = (v0LambdaLikeMass >= 1.1020593 && v0LambdaLikeMass <= 1.1288811); // Very naive estimator. Based on signal extractions from outside this code + + // Inexpensive estimates of signal extraction effects on the observable: + if (excludeOutOfPeakQA && !v0InMassPeak) + continue; + else if (excludeInPeakQA && v0InMassPeak) + continue; + + // Boosting proton into lambda frame: + XYZVector beta = lambdaLike4Vec.BoostToCM(); // Boost trivector that goes from laboratory frame to Lambda's rest frame (convenient new function, different from TLorentzVector's BoostVector()) + auto protonLike4VecStar = ROOT::Math::VectorUtil::boost(protonLike4Vec, beta); + + // Getting unit vectors and 3-components: + XYZVector lambdaLike3Vec = lambdaLike4Vec.Vect(); + auto lambdaLikeUnit3Vec = lambdaLike3Vec.Unit(); + XYZVector protonLikeStarUnit3Vec = protonLike4VecStar.Vect().Unit(); + + // Lab-frame Lambda momentum components -- Not for polarization, but for actual momenta plotting in XY and ZX planes: + // (Used for the (px,py) and (pz,px) polarization vector-field / ring 2D profiles) + const float v0px = lambdaLike3Vec.X(); + const float v0py = lambdaLike3Vec.Y(); + const float v0pz = lambdaLike3Vec.Z(); + + // Calculating fake polarization ("negative helicity problem") estimator: + // (this estimator is calculated outside of any gate, as it does not depend on jet proxy used) + float cosFakePol = protonLikeStarUnit3Vec.Dot(lambdaLikeUnit3Vec); + + // Calculating the azimuthal angle between the Lambda and the proton: + float deltaPhiLambdaProtonStar = wrapToPiFast(lambdaLikeUnit3Vec.Phi() - protonLikeStarUnit3Vec.Phi()); // Phi is defined from -PI to PI in ROOT::Math::Cartesian3D, thus kept the wrapping + + // Calculating the phi* angle: + // e_z = p_Lambda_hat; // e_x = normalize(z_hat cross p_Lambda); // e_y = e_z cross e_x; + // // phi_star = atan2(p_p_star dot e_y, p_p_star dot e_x); + // XYZVector e_x(-lambdaLikeUnit3Vec.Y(), lambdaLikeUnit3Vec.X(), 0.); // Same as e_x = zHat.Cross(lambdaLikeUnit3Vec); + // XYZVector e_y = lambdaLikeUnit3Vec.Cross(e_x); // e_y completes the right-handed coordinate system (e_z is lambdaLikeUnit3Vec) + // float pX = protonLikeStarUnit3Vec.Dot(e_x); + // float pY = protonLikeStarUnit3Vec.Dot(e_y); + // float phiStar = std::atan2(pY, pX); + // Faster implementation: + // pX = p_y * L_x - p_x * L_y + float pX = protonLikeStarUnit3Vec.Y() * lambdaLikeUnit3Vec.X() - protonLikeStarUnit3Vec.X() * lambdaLikeUnit3Vec.Y(); + // pY = p_z - L_z * (p_proton_star dot p_lambda_hat) + float pY = protonLikeStarUnit3Vec.Z() - lambdaLikeUnit3Vec.Z() * cosFakePol; // (Reusing cosFakePol calculated earlier!) + float phiStar = std::atan2(pY, pX); // This will give an output from -PI to PI + if (qaSwitches.doFakePolDiagnosticsQA) { - // Measuring the AEE differentially (azimuthal efficiency effect, which causes different V0 topologies to be enhanced/suppressed): + // Another reconstruction efficiency measure: + // (Formula is: p_{Lambda} \cross p_{Daughter}^{*} \cdot B, and B points in Z) + if (analyseMagField) { + auto crossGeom = lambdaLike3Vec.Cross(protonLikeStarUnit3Vec); + const bool positiveGeom = crossGeom.Z() * magField > 0; + + if (isLambda && positiveGeom) + histos.fill(HIST("HelicityEfficiencyQA/hLambdaMassDecayGeomRight"), v0LambdaLikeMass); + else if (isLambda && !positiveGeom) + histos.fill(HIST("HelicityEfficiencyQA/hLambdaMassDecayGeomLeft"), v0LambdaLikeMass); + else if (!isLambda && positiveGeom) + histos.fill(HIST("HelicityEfficiencyQA/hAntiLambdaMassDecayGeomRight"), v0LambdaLikeMass); + else + histos.fill(HIST("HelicityEfficiencyQA/hAntiLambdaMassDecayGeomLeft"), v0LambdaLikeMass); + } + + // Measuring the AEE effect differentially (azimuthal efficiency effect, which causes different V0 topologies to be enhanced/suppressed): if (isLambda) - histos.fill(HIST("HelicityEfficiencyQA/p2dRing_LambdaMassVsPhiLambdaMinusPhiProtonStar"), v0LambdaLikeMass, deltaPhiLambdaProtonStar, ringObservable); + histos.fill(HIST("HelicityEfficiencyQA/hLambdaMassVsPhiLambdaMinusPhiProtonStar"), v0LambdaLikeMass, deltaPhiLambdaProtonStar); else - histos.fill(HIST("HelicityEfficiencyQA/p2dRing_AntiLambdaMassVsPhiLambdaMinusPhiProtonStar"), v0LambdaLikeMass, deltaPhiLambdaProtonStar, ringObservable); + histos.fill(HIST("HelicityEfficiencyQA/hAntiLambdaMassVsPhiLambdaMinusPhiProtonStar"), v0LambdaLikeMass, deltaPhiLambdaProtonStar); // AEE and HEE correlation: - histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsJet_CosThetaVsPhiStar"), cosFakePol, phiStar); - histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignal_CosThetaVsPhiStar"), cosFakePol, phiStar, ringObservable); - - // AEE and DCA between daughters correlation: - histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsJet_PhiStarVsDCAdau"), phiStar, dcaDau); - histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAdau"), phiStar, dcaDau, ringObservable); - histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAdauVsEtaJet"), phiStar, dcaDau, leadingJetEta, ringObservable); - histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAdauVsEtaLambda"), phiStar, dcaDau, v0eta, ringObservable); + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCounts_CosThetaVsPhiStar"), cosFakePol, phiStar); + } // end doFakePolDiagnosticsQA (per-V0 AEE/HEE) + + // Useful kinematic bools: + const bool lambdaEtaPos = v0eta >= 0.; + const bool pTLambdaCheck = v0pt > 0.5 && v0pt < 1.5; + const bool rapidityLambdaCheck = std::abs(lambdaRapidity) < 0.5; + const bool kinematicLambdaCheck = pTLambdaCheck && rapidityLambdaCheck; + + //////////////////////////////////////////// + // Ring observable: Leading particle proxy + // Only computed when a valid leading particle exists (pT > minLeadParticlePt) + //////////////////////////////////////////// + float ringObservableLeadP = 0.; + float deltaPhiLeadP = 0.; + float deltaThetaLeadP = 0.; + float cosDeltaThetaLeadP = 0.; + if (hasValidLeadingP) { + // Cross product + XYZVector crossLeadP = leadPUnitVec.Cross(lambdaLike3Vec); + ringObservableLeadP = protonLikeStarUnit3Vec.Dot(crossLeadP) / crossLeadP.R(); + // Adding the prefactor related to the CP-violating decay (decay constants have different signs) + if (!fakePolSwitches.forcePolSignQA) + ringObservableLeadP *= (isLambda) ? PolPrefactorLambda : PolPrefactorAntiLambda; + else + ringObservableLeadP *= (isLambda) ? PolPrefactorLambda : -1.0 * PolPrefactorAntiLambda; + // Angular variables + deltaPhiLeadP = wrapToPiFast(v0phi - leadPPhi); // Wrapped to [-PI, PI), for convenience - // DCA dau to PV correlation: - // For proton-like daughter: - histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsJet_PhiStarVsDCAProLike"), phiStar, protonLikeDCADauToPV); - histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAProLike"), phiStar, protonLikeDCADauToPV, ringObservable); - histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAProLikeVsEtaJet"), phiStar, protonLikeDCADauToPV, leadingJetEta, ringObservable); - histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAProLikeVsEtaLambda"), phiStar, protonLikeDCADauToPV, v0eta, ringObservable); - // For pion-like daughter: - histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsJet_PhiStarVsDCAPiLike"), phiStar, pionLikeDCADauToPV); - histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAPiLike"), phiStar, pionLikeDCADauToPV, ringObservable); - histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAPiLikeVsEtaJet"), phiStar, pionLikeDCADauToPV, leadingJetEta, ringObservable); - histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAPiLikeVsEtaLambda"), phiStar, pionLikeDCADauToPV, v0eta, ringObservable); - - histos.fill(HIST("HelicityEfficiencyQA/pRingVsJetZcomponent"), Jz, ringObservable); - histos.fill(HIST("HelicityEfficiencyQA/pRingOverJetZcomponent_VsJetEta"), leadingJetEta, ringObservableOverJetZ); - histos.fill(HIST("HelicityEfficiencyQA/pRingOverJetZcomponent_VsCosThetaHEE"), cosFakePol, ringObservableOverJetZ); - histos.fill(HIST("HelicityEfficiencyQA/pRingOverJetZcomponent_VsPhiStar"), phiStar, ringObservableOverJetZ); - histos.fill(HIST("HelicityEfficiencyQA/pRingOverJetZcomponent_VsJetEtaVsCosThetaHEE"), leadingJetEta, cosFakePol, ringObservableOverJetZ); - histos.fill(HIST("HelicityEfficiencyQA/pRingOverJetZcomponent_VsJetEtaVsPhiStar"), leadingJetEta, phiStar, ringObservableOverJetZ); - } // end doFakePolDiagnosticsQA (leading-jet AEE/HEE) - } - if (hasValidSubJet) { - if (familySwitches.doFamilyRing) { - RING_OBSERVABLE_2NDJET_FILL_LIST(APPLY_HISTO_FILL, "Ring") + cosDeltaThetaLeadP = leadPUnitVec.Dot(lambdaLikeUnit3Vec); // Uses the pre-calculated unit vectors to avoid recomputation + deltaThetaLeadP = std::acos(cosDeltaThetaLeadP); // 3D angular separation. Same as ROOT::Math::VectorUtil::Angle(leadPUnitVec, lambdaLike3Vec); } - histos.fill(HIST("IntegratedCuts/pRingCutsSubLeadingJet"), 0, ringObservable2ndJet); - histos.fill(HIST("IntegratedCuts/hCountCutsSubLeadingJet"), 0); - } - if (qaSwitches.doFakePolDiagnosticsQA) { - // Filling eta dependence QAs of the result (both for V0 and jet proxy): - // Defining shared binning which depend on the V0 only: - const int etaLambdaBin = lambdaEtaPos ? 3 : 4; + ////////////////////////////////////////// + // Ring observable: Leading jet proxy + // Only computed when a leading jet exists in this collision. + ////////////////////////////////////////// + float ringObservable = 0.; + float deltaPhiJet = 0.; + float deltaEtaJet = 0.; + float deltaThetaJet = 0.; + float cosDeltaThetaJet = 0.; + float Jz = 0.; + float ringObservableOverJetZ = 0.; if (hasValidLeadingJet) { - histos.fill(HIST("EtaStudy/pRingEtaCuts"), 0, ringObservable); - histos.fill(HIST("EtaStudy/pRingEtaCuts"), etaLambdaBin, ringObservable); - - // Bin indices for this proxy: - // Bin 0: all, 1/2: proxy #eta sign, 3/4: #Lambda #eta sign, 5-8: joint, - // 9/10: |#eta_{proxy}| >= R, 11-14: strict joint. - const int etaProxyBin = jetEtaPos ? 1 : 2; - const int etaProxyLambdaBin = (jetEtaPos ? 5 : 7) + (lambdaEtaPos ? 0 : 1); - const int etaProxyStrictBin = jetEtaPos ? 9 : 10; - const int etaProxyStrictLambdaBin = (jetEtaPos ? 11 : 13) + (lambdaEtaPos ? 0 : 1); - - histos.fill(HIST("EtaStudy/pRingEtaCuts"), etaProxyBin, ringObservable); - histos.fill(HIST("EtaStudy/pRingEtaCuts"), etaProxyLambdaBin, ringObservable); - - // HEE study (helicity efficiency effect): - histos.fill(HIST("EtaStudy/hFakePolCounts"), cosFakePol, 0); - histos.fill(HIST("EtaStudy/hFakePolCounts"), cosFakePol, etaLambdaBin); - histos.fill(HIST("EtaStudy/hFakePolCounts"), cosFakePol, etaProxyBin); - histos.fill(HIST("EtaStudy/hFakePolCounts"), cosFakePol, etaProxyLambdaBin); - // Same for signal: - histos.fill(HIST("EtaStudy/pFakePolSignalVsCosTheta"), cosFakePol, 0, ringObservable); - histos.fill(HIST("EtaStudy/pFakePolSignalVsCosTheta"), cosFakePol, etaLambdaBin, ringObservable); - histos.fill(HIST("EtaStudy/pFakePolSignalVsCosTheta"), cosFakePol, etaProxyBin, ringObservable); - histos.fill(HIST("EtaStudy/pFakePolSignalVsCosTheta"), cosFakePol, etaProxyLambdaBin, ringObservable); - // Counter and ring accumulators for AEE study: - histos.fill(HIST("EtaStudy/hCountsVsPhiStar"), phiStar, 0); - histos.fill(HIST("EtaStudy/hCountsVsPhiStar"), phiStar, etaLambdaBin); - histos.fill(HIST("EtaStudy/hCountsVsPhiStar"), phiStar, etaProxyBin); - histos.fill(HIST("EtaStudy/hCountsVsPhiStar"), phiStar, etaProxyLambdaBin); - histos.fill(HIST("EtaStudy/pFakePolSignalvsPhiStar"), phiStar, 0, ringObservable); - histos.fill(HIST("EtaStudy/pFakePolSignalvsPhiStar"), phiStar, etaLambdaBin, ringObservable); - histos.fill(HIST("EtaStudy/pFakePolSignalvsPhiStar"), phiStar, etaProxyBin, ringObservable); - histos.fill(HIST("EtaStudy/pFakePolSignalvsPhiStar"), phiStar, etaProxyLambdaBin, ringObservable); - - histos.fill(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"), deltaPhiLambdaProtonStar, 0, ringObservable); - histos.fill(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"), deltaPhiLambdaProtonStar, etaLambdaBin, ringObservable); - histos.fill(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"), deltaPhiLambdaProtonStar, etaProxyBin, ringObservable); - histos.fill(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"), deltaPhiLambdaProtonStar, etaProxyLambdaBin, ringObservable); - - // Extra correlations test: - histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaJet"), cosFakePol, deltaThetaJet); - histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsCosThetaVsPtForJets"), cosFakePol, v0pt); - // Split by proxy #eta sign: - if (jetEtaPos) - histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaJetPosEta"), cosFakePol, deltaThetaJet); + // Cross product + XYZVector cross = leadingJetUnitVec.Cross(lambdaLike3Vec); + ringObservable = protonLikeStarUnit3Vec.Dot(cross) / cross.R(); + // Adding prefactor + if (!fakePolSwitches.forcePolSignQA) + ringObservable *= (isLambda) ? PolPrefactorLambda : PolPrefactorAntiLambda; else - histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaJetNegEta"), cosFakePol, deltaThetaJet); - - if (pTLambdaCheck) { - histos.fill(HIST("EtaStudy/hFakePolCountsLambdaPtCut"), cosFakePol, 0); - histos.fill(HIST("EtaStudy/hFakePolCountsLambdaPtCut"), cosFakePol, etaLambdaBin); - histos.fill(HIST("EtaStudy/hFakePolCountsLambdaPtCut"), cosFakePol, etaProxyBin); - histos.fill(HIST("EtaStudy/hFakePolCountsLambdaPtCut"), cosFakePol, etaProxyLambdaBin); - if (rapidityLambdaCheck){ // Stricter check - histos.fill(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"), cosFakePol, 0); - histos.fill(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"), cosFakePol, etaLambdaBin); - histos.fill(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"), cosFakePol, etaProxyBin); - histos.fill(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"), cosFakePol, etaProxyLambdaBin); - } - } - if (jetEtaStrict) { // |eta_{Jet}| >= R - histos.fill(HIST("EtaStudy/pRingEtaCuts"), etaProxyStrictBin, ringObservable); - histos.fill(HIST("EtaStudy/pRingEtaCuts"), etaProxyStrictLambdaBin, ringObservable); - } + ringObservable *= (isLambda) ? PolPrefactorLambda : -1.0 * PolPrefactorAntiLambda; + // Angular variables + deltaPhiJet = wrapToPiFast(v0phi - leadingJetPhi); + deltaEtaJet = v0eta - leadingJetEta; + + cosDeltaThetaJet = leadingJetUnitVec.Dot(lambdaLikeUnit3Vec); + deltaThetaJet = std::acos(cosDeltaThetaJet); + + // Testing an invariance -- /\hat{t}_z -- Possible source of an artificial (trivial) sign flip in the observable: + Jz = leadingJetUnitVec.Z(); + if (std::abs(Jz) > 1e-4) + ringObservableOverJetZ = ringObservable / Jz; + else + ringObservableOverJetZ = 0.0; // A simple guard. May not be the best, but works + + // // A second projection schema, where e_x = normalize(t_hat cross p_Lambda), using t_hat instead of z_hat (different from phi*): + // // (this decomposes in an orthogonal basis related to the jet coordinates) + // XYZVector ez = lambdaLikeUnit3Vec; + // XYZVector ex = leadingJetUnitVec.Cross(lambdaLike3Vec); + // XYZVector ey = ez.Cross(ex); + + // ringObservableExProjection = ringObservable; + // ringObservableEyProjection = + // // Assuming that energy can get inside the average in: + // // P_Lambda \cdot p_Lambda = E_Lambda/m_Lambda * P_Lambda^* \cdot p_Lambda = * p_Lambda + // ringObservableEzProjection = cosFakePol * lambdaLike4Vec.E()/v0LambdaLikeMass; } + + ////////////////////////////////////////// + // Ring observable: Subleading jet proxy + // Only computed when a subleading jet exists in this collision. + ////////////////////////////////////////// + float ringObservable2ndJet = 0.; + float deltaPhi2ndJet = 0.; + float deltaTheta2ndJet = 0.; + float cosDeltaTheta2ndJet = 0.; if (hasValidSubJet) { - // Same bin scheme as the leading jet above: - const int etaProxyBin = subJetEtaPos ? 1 : 2; - const int etaProxyLambdaBin = (subJetEtaPos ? 5 : 7) + (lambdaEtaPos ? 0 : 1); - const int etaProxyStrictBin = subJetEtaPos ? 9 : 10; - const int etaProxyStrictLambdaBin = (subJetEtaPos ? 11 : 13) + (lambdaEtaPos ? 0 : 1); - - histos.fill(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"), 0, ringObservable2ndJet); - histos.fill(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"), etaLambdaBin, ringObservable2ndJet); - histos.fill(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"), etaProxyBin, ringObservable2ndJet); - histos.fill(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"), etaProxyLambdaBin, ringObservable2ndJet); - if (subJetEtaStrict) { // |eta_{SubJet}| >= R - histos.fill(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"), etaProxyStrictBin, ringObservable2ndJet); - histos.fill(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"), etaProxyStrictLambdaBin, ringObservable2ndJet); - } - } - if (hasValidLeadingP) { - // Same bin scheme, without the strict variant (this axis stops at 9 bins): - const int etaProxyBin = leadPEtaPos ? 1 : 2; - const int etaProxyLambdaBin = (leadPEtaPos ? 5 : 7) + (lambdaEtaPos ? 0 : 1); - - histos.fill(HIST("EtaStudy/pRingEtaCutsLeadingP"), 0, ringObservableLeadP); - histos.fill(HIST("EtaStudy/pRingEtaCutsLeadingP"), etaLambdaBin, ringObservableLeadP); - histos.fill(HIST("EtaStudy/pRingEtaCutsLeadingP"), etaProxyBin, ringObservableLeadP); - histos.fill(HIST("EtaStudy/pRingEtaCutsLeadingP"), etaProxyLambdaBin, ringObservableLeadP); - histos.fill(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"), 0, v0InMassPeak, ringObservableLeadP); - histos.fill(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"), etaLambdaBin, v0InMassPeak, ringObservableLeadP); - histos.fill(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"), etaProxyBin, v0InMassPeak, ringObservableLeadP); - histos.fill(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"), etaProxyLambdaBin, v0InMassPeak, ringObservableLeadP); - - histos.fill(HIST("EtaStudy/hFakePolCountsLeadP"), cosFakePol, 0); - histos.fill(HIST("EtaStudy/hFakePolCountsLeadP"), cosFakePol, etaLambdaBin); - histos.fill(HIST("EtaStudy/hFakePolCountsLeadP"), cosFakePol, etaProxyBin); - histos.fill(HIST("EtaStudy/hFakePolCountsLeadP"), cosFakePol, etaProxyLambdaBin); - - histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsCosThetaVsPtForLeadP"), cosFakePol, v0pt); // Understanding the population of events that has a leading particle (even though this does not need one to be calculated!) - histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaLeadP"), cosFakePol, deltaThetaLeadP); - if (leadPEtaPos) - histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaLeadPPosEta"), cosFakePol, deltaThetaLeadP); + XYZVector cross2ndJet = subJetUnitVec.Cross(lambdaLike3Vec); + ringObservable2ndJet = protonLikeStarUnit3Vec.Dot(cross2ndJet) / cross2ndJet.R(); + // Adding prefactor + if (!fakePolSwitches.forcePolSignQA) + ringObservable2ndJet *= (isLambda) ? PolPrefactorLambda : PolPrefactorAntiLambda; else - histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaLeadPNegEta"), cosFakePol, deltaThetaLeadP); + ringObservable2ndJet *= (isLambda) ? PolPrefactorLambda : -1.0 * PolPrefactorAntiLambda; + // Angular variables + deltaPhi2ndJet = wrapToPiFast(v0phi - subleadingJetPhi); + cosDeltaTheta2ndJet = subJetUnitVec.Dot(lambdaLikeUnit3Vec); + deltaTheta2ndJet = std::acos(cosDeltaTheta2ndJet); + } + + // Calculating polarization observables (in the Lambda frame, because that is easier -- does not require boosts): + // To be precise, not actually the polarization, but a part of the summand in P^*_Lambda = (3/\alpha_Lambda) * + float polStarX = 0, polStarY = 0, polStarZ = 0; // Dummy initialization: avoid warnings in compile time + if (isLambda) { // Notice there is no need to check analyseLambda again due to previous checks. + polStarX = PolPrefactorLambda * protonLikeStarUnit3Vec.X(); + polStarY = PolPrefactorLambda * protonLikeStarUnit3Vec.Y(); + polStarZ = PolPrefactorLambda * protonLikeStarUnit3Vec.Z(); + } else { + polStarX = PolPrefactorAntiLambda * protonLikeStarUnit3Vec.X(); + polStarY = PolPrefactorAntiLambda * protonLikeStarUnit3Vec.Y(); + polStarZ = PolPrefactorAntiLambda * protonLikeStarUnit3Vec.Z(); } - } // end doFakePolDiagnosticsQA (eta-dependence block) - // Extra kinematic criteria for Lambda candidates (removes polarization background): - if (kinematicLambdaCheck) { + float v0phiToFillHists = wrapToPiFast(v0phi); // A short wrap to reuse some predefined axes + + // Fill ring histograms: (1D, lambda 2D correlations and jet 2D correlations): if (hasValidLeadingP) { - if (familySwitches.doFamilyRingKinematicCuts) { - RING_OBSERVABLE_LEADP_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts") + if (familySwitches.doFamilyRing) { + RING_OBSERVABLE_LEADP_FILL_LIST(APPLY_HISTO_FILL, "Ring") // Notice the usage of macros! If you change the variable names, this WILL break the code! + // No, there should NOT be any ";" here! Read the macro definition for an explanation } - histos.fill(HIST("IntegratedCuts/pRingCutsLeadingP"), 1, ringObservableLeadP); - histos.fill(HIST("IntegratedCuts/hCountCutsLeadingP"), 1); + histos.fill(HIST("IntegratedCuts/pRingCutsLeadingP"), 0, ringObservableLeadP); // First bin of comparison + histos.fill(HIST("IntegratedCuts/hCountCutsLeadingP"), 0); // Filling checks that rely on Eta>0 or Eta<0 checks for V0 and LeadingP eta: - if (familySwitches.doFamilyRingKinematicCuts) { - RING_OBSERVABLE_LEADP_ETA_SPLIT_FILL_LIST("RingKinematicCuts", leadPEtaPos, lambdaEtaPos); + if (familySwitches.doFamilyRing) { + RING_OBSERVABLE_LEADP_ETA_SPLIT_FILL_LIST("Ring", leadPEtaPos, lambdaEtaPos); } } - if (familySwitches.doFamilyRingKinematicCuts) { - POLARIZATION_PROFILE_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts") + if (familySwitches.doFamilyRing) { + POLARIZATION_PROFILE_FILL_LIST(APPLY_HISTO_FILL, "Ring") } + + // Binary search using the pre-fetched axes for delta method of error bar estimation: + int binPt = 0; // Dummy declarations + int binMass = 0; + int binDTheta = 0; if (hasValidLeadingJet) { - if (familySwitches.doFamilyRingKinematicCuts) { - RING_OBSERVABLE_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts") + if (familySwitches.doFamilyRing) { + RING_OBSERVABLE_FILL_LIST(APPLY_HISTO_FILL, "Ring") } - histos.fill(HIST("IntegratedCuts/pRingCuts"), 1, ringObservable); - histos.fill(HIST("IntegratedCuts/hCountCuts"), 1); - if (familySwitches.doFamilyRingKinematicCuts) { - trackRingKinCuts.addV0(ringObservable, binPt, binMass, binDTheta); + histos.fill(HIST("IntegratedCuts/pRingCuts"), 0, ringObservable); + histos.fill(HIST("IntegratedCuts/hCountCuts"), 0); + histos.fill(HIST("IntegratedCuts/pRingVsNV0s"), nLambdaLikeV0s, ringObservable); + histos.fill(HIST("hNV0sVsCentrality"), nLambdaLikeV0s, centrality); + + // Properly fetching values as they are needed: + binPt = mAxisPt->FindBin(v0pt); + binMass = mAxisMass->FindBin(v0LambdaLikeMass); + binDTheta = mAxisDTheta->FindBin(deltaThetaJet); + if (familySwitches.doFamilyRing) { + trackRing.addV0(ringObservable, binPt, binMass, binDTheta); } + + if (qaSwitches.doFakePolDiagnosticsQA) { + // Measuring the AEE differentially (azimuthal efficiency effect, which causes different V0 topologies to be enhanced/suppressed): + if (isLambda) + histos.fill(HIST("HelicityEfficiencyQA/p2dRing_LambdaMassVsPhiLambdaMinusPhiProtonStar"), v0LambdaLikeMass, deltaPhiLambdaProtonStar, ringObservable); + else + histos.fill(HIST("HelicityEfficiencyQA/p2dRing_AntiLambdaMassVsPhiLambdaMinusPhiProtonStar"), v0LambdaLikeMass, deltaPhiLambdaProtonStar, ringObservable); + // AEE and HEE correlation: + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsJet_CosThetaVsPhiStar"), cosFakePol, phiStar); + histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignal_CosThetaVsPhiStar"), cosFakePol, phiStar, ringObservable); + + // AEE and DCA between daughters correlation: + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsJet_PhiStarVsDCAdau"), phiStar, dcaDau); + histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAdau"), phiStar, dcaDau, ringObservable); + histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAdauVsEtaJet"), phiStar, dcaDau, leadingJetEta, ringObservable); + histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAdauVsEtaLambda"), phiStar, dcaDau, v0eta, ringObservable); + + // DCA dau to PV correlation: + // For proton-like daughter: + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsJet_PhiStarVsDCAProLike"), phiStar, protonLikeDCADauToPV); + histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAProLike"), phiStar, protonLikeDCADauToPV, ringObservable); + histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAProLikeVsEtaJet"), phiStar, protonLikeDCADauToPV, leadingJetEta, ringObservable); + histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAProLikeVsEtaLambda"), phiStar, protonLikeDCADauToPV, v0eta, ringObservable); + // For pion-like daughter: + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsJet_PhiStarVsDCAPiLike"), phiStar, pionLikeDCADauToPV); + histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAPiLike"), phiStar, pionLikeDCADauToPV, ringObservable); + histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAPiLikeVsEtaJet"), phiStar, pionLikeDCADauToPV, leadingJetEta, ringObservable); + histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAPiLikeVsEtaLambda"), phiStar, pionLikeDCADauToPV, v0eta, ringObservable); + + histos.fill(HIST("HelicityEfficiencyQA/pRingVsJetZcomponent"), Jz, ringObservable); + histos.fill(HIST("HelicityEfficiencyQA/pRingOverJetZcomponent_VsJetEta"), leadingJetEta, ringObservableOverJetZ); + histos.fill(HIST("HelicityEfficiencyQA/pRingOverJetZcomponent_VsCosThetaHEE"), cosFakePol, ringObservableOverJetZ); + histos.fill(HIST("HelicityEfficiencyQA/pRingOverJetZcomponent_VsPhiStar"), phiStar, ringObservableOverJetZ); + histos.fill(HIST("HelicityEfficiencyQA/pRingOverJetZcomponent_VsJetEtaVsCosThetaHEE"), leadingJetEta, cosFakePol, ringObservableOverJetZ); + histos.fill(HIST("HelicityEfficiencyQA/pRingOverJetZcomponent_VsJetEtaVsPhiStar"), leadingJetEta, phiStar, ringObservableOverJetZ); + } // end doFakePolDiagnosticsQA (leading-jet AEE/HEE) } if (hasValidSubJet) { + if (familySwitches.doFamilyRing) { + RING_OBSERVABLE_2NDJET_FILL_LIST(APPLY_HISTO_FILL, "Ring") + } + histos.fill(HIST("IntegratedCuts/pRingCutsSubLeadingJet"), 0, ringObservable2ndJet); + histos.fill(HIST("IntegratedCuts/hCountCutsSubLeadingJet"), 0); + } + + if (qaSwitches.doFakePolDiagnosticsQA) { + // Filling eta dependence QAs of the result (both for V0 and jet proxy): + // Defining shared binning which depend on the V0 only: + const int etaLambdaBin = lambdaEtaPos ? 3 : 4; + if (hasValidLeadingJet) { + histos.fill(HIST("EtaStudy/pRingEtaCuts"), 0, ringObservable); + histos.fill(HIST("EtaStudy/pRingEtaCuts"), etaLambdaBin, ringObservable); + + // Bin indices for this proxy: + // Bin 0: all, 1/2: proxy #eta sign, 3/4: #Lambda #eta sign, 5-8: joint, + // 9/10: |#eta_{proxy}| >= R, 11-14: strict joint. + const int etaProxyBin = jetEtaPos ? 1 : 2; + const int etaProxyLambdaBin = (jetEtaPos ? 5 : 7) + (lambdaEtaPos ? 0 : 1); + const int etaProxyStrictBin = jetEtaPos ? 9 : 10; + const int etaProxyStrictLambdaBin = (jetEtaPos ? 11 : 13) + (lambdaEtaPos ? 0 : 1); + + histos.fill(HIST("EtaStudy/pRingEtaCuts"), etaProxyBin, ringObservable); + histos.fill(HIST("EtaStudy/pRingEtaCuts"), etaProxyLambdaBin, ringObservable); + + // HEE study (helicity efficiency effect): + histos.fill(HIST("EtaStudy/hFakePolCounts"), cosFakePol, 0); + histos.fill(HIST("EtaStudy/hFakePolCounts"), cosFakePol, etaLambdaBin); + histos.fill(HIST("EtaStudy/hFakePolCounts"), cosFakePol, etaProxyBin); + histos.fill(HIST("EtaStudy/hFakePolCounts"), cosFakePol, etaProxyLambdaBin); + // Same for signal: + histos.fill(HIST("EtaStudy/pFakePolSignalVsCosTheta"), cosFakePol, 0, ringObservable); + histos.fill(HIST("EtaStudy/pFakePolSignalVsCosTheta"), cosFakePol, etaLambdaBin, ringObservable); + histos.fill(HIST("EtaStudy/pFakePolSignalVsCosTheta"), cosFakePol, etaProxyBin, ringObservable); + histos.fill(HIST("EtaStudy/pFakePolSignalVsCosTheta"), cosFakePol, etaProxyLambdaBin, ringObservable); + // Counter and ring accumulators for AEE study: + histos.fill(HIST("EtaStudy/hCountsVsPhiStar"), phiStar, 0); + histos.fill(HIST("EtaStudy/hCountsVsPhiStar"), phiStar, etaLambdaBin); + histos.fill(HIST("EtaStudy/hCountsVsPhiStar"), phiStar, etaProxyBin); + histos.fill(HIST("EtaStudy/hCountsVsPhiStar"), phiStar, etaProxyLambdaBin); + histos.fill(HIST("EtaStudy/pFakePolSignalvsPhiStar"), phiStar, 0, ringObservable); + histos.fill(HIST("EtaStudy/pFakePolSignalvsPhiStar"), phiStar, etaLambdaBin, ringObservable); + histos.fill(HIST("EtaStudy/pFakePolSignalvsPhiStar"), phiStar, etaProxyBin, ringObservable); + histos.fill(HIST("EtaStudy/pFakePolSignalvsPhiStar"), phiStar, etaProxyLambdaBin, ringObservable); + + histos.fill(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"), deltaPhiLambdaProtonStar, 0, ringObservable); + histos.fill(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"), deltaPhiLambdaProtonStar, etaLambdaBin, ringObservable); + histos.fill(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"), deltaPhiLambdaProtonStar, etaProxyBin, ringObservable); + histos.fill(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"), deltaPhiLambdaProtonStar, etaProxyLambdaBin, ringObservable); + + // Extra correlations test: + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaJet"), cosFakePol, deltaThetaJet); + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsCosThetaVsPtForJets"), cosFakePol, v0pt); + // Split by proxy #eta sign: + if (jetEtaPos) + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaJetPosEta"), cosFakePol, deltaThetaJet); + else + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaJetNegEta"), cosFakePol, deltaThetaJet); + + if (pTLambdaCheck) { + histos.fill(HIST("EtaStudy/hFakePolCountsLambdaPtCut"), cosFakePol, 0); + histos.fill(HIST("EtaStudy/hFakePolCountsLambdaPtCut"), cosFakePol, etaLambdaBin); + histos.fill(HIST("EtaStudy/hFakePolCountsLambdaPtCut"), cosFakePol, etaProxyBin); + histos.fill(HIST("EtaStudy/hFakePolCountsLambdaPtCut"), cosFakePol, etaProxyLambdaBin); + if (rapidityLambdaCheck) { // Stricter check + histos.fill(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"), cosFakePol, 0); + histos.fill(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"), cosFakePol, etaLambdaBin); + histos.fill(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"), cosFakePol, etaProxyBin); + histos.fill(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"), cosFakePol, etaProxyLambdaBin); + } + } + if (jetEtaStrict) { // |eta_{Jet}| >= R + histos.fill(HIST("EtaStudy/pRingEtaCuts"), etaProxyStrictBin, ringObservable); + histos.fill(HIST("EtaStudy/pRingEtaCuts"), etaProxyStrictLambdaBin, ringObservable); + } + } + if (hasValidSubJet) { + // Same bin scheme as the leading jet above: + const int etaProxyBin = subJetEtaPos ? 1 : 2; + const int etaProxyLambdaBin = (subJetEtaPos ? 5 : 7) + (lambdaEtaPos ? 0 : 1); + const int etaProxyStrictBin = subJetEtaPos ? 9 : 10; + const int etaProxyStrictLambdaBin = (subJetEtaPos ? 11 : 13) + (lambdaEtaPos ? 0 : 1); + + histos.fill(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"), 0, ringObservable2ndJet); + histos.fill(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"), etaLambdaBin, ringObservable2ndJet); + histos.fill(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"), etaProxyBin, ringObservable2ndJet); + histos.fill(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"), etaProxyLambdaBin, ringObservable2ndJet); + if (subJetEtaStrict) { // |eta_{SubJet}| >= R + histos.fill(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"), etaProxyStrictBin, ringObservable2ndJet); + histos.fill(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"), etaProxyStrictLambdaBin, ringObservable2ndJet); + } + } + if (hasValidLeadingP) { + // Same bin scheme, without the strict variant (this axis stops at 9 bins): + const int etaProxyBin = leadPEtaPos ? 1 : 2; + const int etaProxyLambdaBin = (leadPEtaPos ? 5 : 7) + (lambdaEtaPos ? 0 : 1); + + histos.fill(HIST("EtaStudy/pRingEtaCutsLeadingP"), 0, ringObservableLeadP); + histos.fill(HIST("EtaStudy/pRingEtaCutsLeadingP"), etaLambdaBin, ringObservableLeadP); + histos.fill(HIST("EtaStudy/pRingEtaCutsLeadingP"), etaProxyBin, ringObservableLeadP); + histos.fill(HIST("EtaStudy/pRingEtaCutsLeadingP"), etaProxyLambdaBin, ringObservableLeadP); + histos.fill(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"), 0, v0InMassPeak, ringObservableLeadP); + histos.fill(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"), etaLambdaBin, v0InMassPeak, ringObservableLeadP); + histos.fill(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"), etaProxyBin, v0InMassPeak, ringObservableLeadP); + histos.fill(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"), etaProxyLambdaBin, v0InMassPeak, ringObservableLeadP); + + histos.fill(HIST("EtaStudy/hFakePolCountsLeadP"), cosFakePol, 0); + histos.fill(HIST("EtaStudy/hFakePolCountsLeadP"), cosFakePol, etaLambdaBin); + histos.fill(HIST("EtaStudy/hFakePolCountsLeadP"), cosFakePol, etaProxyBin); + histos.fill(HIST("EtaStudy/hFakePolCountsLeadP"), cosFakePol, etaProxyLambdaBin); + + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsCosThetaVsPtForLeadP"), cosFakePol, v0pt); // Understanding the population of events that has a leading particle (even though this does not need one to be calculated!) + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaLeadP"), cosFakePol, deltaThetaLeadP); + if (leadPEtaPos) + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaLeadPPosEta"), cosFakePol, deltaThetaLeadP); + else + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaLeadPNegEta"), cosFakePol, deltaThetaLeadP); + } + } // end doFakePolDiagnosticsQA (eta-dependence block) + + // Extra kinematic criteria for Lambda candidates (removes polarization background): + if (kinematicLambdaCheck) { + if (hasValidLeadingP) { + if (familySwitches.doFamilyRingKinematicCuts) { + RING_OBSERVABLE_LEADP_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts") + } + histos.fill(HIST("IntegratedCuts/pRingCutsLeadingP"), 1, ringObservableLeadP); + histos.fill(HIST("IntegratedCuts/hCountCutsLeadingP"), 1); + + // Filling checks that rely on Eta>0 or Eta<0 checks for V0 and LeadingP eta: + if (familySwitches.doFamilyRingKinematicCuts) { + RING_OBSERVABLE_LEADP_ETA_SPLIT_FILL_LIST("RingKinematicCuts", leadPEtaPos, lambdaEtaPos); + } + } if (familySwitches.doFamilyRingKinematicCuts) { - RING_OBSERVABLE_2NDJET_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts") + POLARIZATION_PROFILE_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts") + } + if (hasValidLeadingJet) { + if (familySwitches.doFamilyRingKinematicCuts) { + RING_OBSERVABLE_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts") + } + histos.fill(HIST("IntegratedCuts/pRingCuts"), 1, ringObservable); + histos.fill(HIST("IntegratedCuts/hCountCuts"), 1); + if (familySwitches.doFamilyRingKinematicCuts) { + trackRingKinCuts.addV0(ringObservable, binPt, binMass, binDTheta); + } + } + if (hasValidSubJet) { + if (familySwitches.doFamilyRingKinematicCuts) { + RING_OBSERVABLE_2NDJET_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts") + } + histos.fill(HIST("IntegratedCuts/pRingCutsSubLeadingJet"), 1, ringObservable2ndJet); + histos.fill(HIST("IntegratedCuts/hCountCutsSubLeadingJet"), 1); } - histos.fill(HIST("IntegratedCuts/pRingCutsSubLeadingJet"), 1, ringObservable2ndJet); - histos.fill(HIST("IntegratedCuts/hCountCutsSubLeadingJet"), 1); } - } - // Extra selection criteria on jet candidates: - // (redundant for jets with R=0.4, but for jets with R<0.4 the leading jet may be farther in eta) - if (kinematicJetCheck) { // Already includes hasValidLeadingJet in the bool! (no need to check again) - if (familySwitches.doFamilyJetKinematicCuts) { - RING_OBSERVABLE_FILL_LIST(APPLY_HISTO_FILL, "JetKinematicCuts") + // Extra selection criteria on jet candidates: + // (redundant for jets with R=0.4, but for jets with R<0.4 the leading jet may be farther in eta) + if (kinematicJetCheck) { // Already includes hasValidLeadingJet in the bool! (no need to check again) + if (familySwitches.doFamilyJetKinematicCuts) { + RING_OBSERVABLE_FILL_LIST(APPLY_HISTO_FILL, "JetKinematicCuts") + } + histos.fill(HIST("IntegratedCuts/pRingCuts"), 2, ringObservable); + histos.fill(HIST("IntegratedCuts/hCountCuts"), 2); + if (familySwitches.doFamilyJetKinematicCuts) { + POLARIZATION_PROFILE_FILL_LIST(APPLY_HISTO_FILL, "JetKinematicCuts") + trackJetKinCuts.addV0(ringObservable, binPt, binMass, binDTheta); + } } - histos.fill(HIST("IntegratedCuts/pRingCuts"), 2, ringObservable); - histos.fill(HIST("IntegratedCuts/hCountCuts"), 2); - if (familySwitches.doFamilyJetKinematicCuts) { - POLARIZATION_PROFILE_FILL_LIST(APPLY_HISTO_FILL, "JetKinematicCuts") - trackJetKinCuts.addV0(ringObservable, binPt, binMass, binDTheta); + + // Extra selection criteria on both Lambda and jet candidates: + if (kinematicLambdaCheck && kinematicJetCheck) { + if (familySwitches.doFamilyJetAndLambdaKinematicCuts) { + RING_OBSERVABLE_FILL_LIST(APPLY_HISTO_FILL, "JetAndLambdaKinematicCuts") + } + histos.fill(HIST("IntegratedCuts/pRingCuts"), 3, ringObservable); + histos.fill(HIST("IntegratedCuts/hCountCuts"), 3); + if (familySwitches.doFamilyJetAndLambdaKinematicCuts) { + POLARIZATION_PROFILE_FILL_LIST(APPLY_HISTO_FILL, "JetAndLambdaKinematicCuts") + trackJetLambdaKinCuts.addV0(ringObservable, binPt, binMass, binDTheta); + } } - } - // Extra selection criteria on both Lambda and jet candidates: - if (kinematicLambdaCheck && kinematicJetCheck) { - if (familySwitches.doFamilyJetAndLambdaKinematicCuts) { - RING_OBSERVABLE_FILL_LIST(APPLY_HISTO_FILL, "JetAndLambdaKinematicCuts") + // Same variations for the leading particle and for the subleading jet: + // (kinematicLeadPCheck already encodes hasValidLeadingP, so no extra gate needed here) + if (kinematicLeadPCheck) { + if (familySwitches.doFamilyJetKinematicCuts) { + RING_OBSERVABLE_LEADP_FILL_LIST(APPLY_HISTO_FILL, "JetKinematicCuts") + } + histos.fill(HIST("IntegratedCuts/pRingCutsLeadingP"), 2, ringObservableLeadP); + histos.fill(HIST("IntegratedCuts/hCountCutsLeadingP"), 2); + + // Filling checks that rely on Eta>0 or Eta<0 checks for V0 and LeadingP eta: + if (familySwitches.doFamilyJetKinematicCuts) { + RING_OBSERVABLE_LEADP_ETA_SPLIT_FILL_LIST("JetKinematicCuts", leadPEtaPos, lambdaEtaPos); + } } - histos.fill(HIST("IntegratedCuts/pRingCuts"), 3, ringObservable); - histos.fill(HIST("IntegratedCuts/hCountCuts"), 3); - if (familySwitches.doFamilyJetAndLambdaKinematicCuts) { - POLARIZATION_PROFILE_FILL_LIST(APPLY_HISTO_FILL, "JetAndLambdaKinematicCuts") - trackJetLambdaKinCuts.addV0(ringObservable, binPt, binMass, binDTheta); + if (kinematic2ndJetCheck) { + if (familySwitches.doFamilyJetKinematicCuts) { + RING_OBSERVABLE_2NDJET_FILL_LIST(APPLY_HISTO_FILL, "JetKinematicCuts") + } + histos.fill(HIST("IntegratedCuts/pRingCutsSubLeadingJet"), 2, ringObservable2ndJet); + histos.fill(HIST("IntegratedCuts/hCountCutsSubLeadingJet"), 2); } - } + if (kinematicLambdaCheck && kinematicLeadPCheck) { + if (familySwitches.doFamilyJetAndLambdaKinematicCuts) { + RING_OBSERVABLE_LEADP_FILL_LIST(APPLY_HISTO_FILL, "JetAndLambdaKinematicCuts") + } + histos.fill(HIST("IntegratedCuts/pRingCutsLeadingP"), 3, ringObservableLeadP); + histos.fill(HIST("IntegratedCuts/hCountCutsLeadingP"), 3); - // Same variations for the leading particle and for the subleading jet: - // (kinematicLeadPCheck already encodes hasValidLeadingP, so no extra gate needed here) - if (kinematicLeadPCheck) { - if (familySwitches.doFamilyJetKinematicCuts) { - RING_OBSERVABLE_LEADP_FILL_LIST(APPLY_HISTO_FILL, "JetKinematicCuts") + // Filling checks that rely on Eta>0 or Eta<0 checks for V0 and LeadingP eta: + if (familySwitches.doFamilyJetAndLambdaKinematicCuts) { + RING_OBSERVABLE_LEADP_ETA_SPLIT_FILL_LIST("JetAndLambdaKinematicCuts", leadPEtaPos, lambdaEtaPos); + } } - histos.fill(HIST("IntegratedCuts/pRingCutsLeadingP"), 2, ringObservableLeadP); - histos.fill(HIST("IntegratedCuts/hCountCutsLeadingP"), 2); - - // Filling checks that rely on Eta>0 or Eta<0 checks for V0 and LeadingP eta: - if (familySwitches.doFamilyJetKinematicCuts) { - RING_OBSERVABLE_LEADP_ETA_SPLIT_FILL_LIST("JetKinematicCuts", leadPEtaPos, lambdaEtaPos); + if (kinematicLambdaCheck && kinematic2ndJetCheck) { + if (familySwitches.doFamilyJetAndLambdaKinematicCuts) { + RING_OBSERVABLE_2NDJET_FILL_LIST(APPLY_HISTO_FILL, "JetAndLambdaKinematicCuts") + } + histos.fill(HIST("IntegratedCuts/pRingCutsSubLeadingJet"), 3, ringObservable2ndJet); + histos.fill(HIST("IntegratedCuts/hCountCutsSubLeadingJet"), 3); } + } // end v0s loop + + // Flush trackers to the actual O2 histograms (via macros, so that O2 compiles properly): + if (familySwitches.doFamilyRing) { + FLUSH_DELTA_TRACKER("Ring", trackRing, mAxisPt, mAxisMass, mAxisDTheta) } - if (kinematic2ndJetCheck) { - if (familySwitches.doFamilyJetKinematicCuts) { - RING_OBSERVABLE_2NDJET_FILL_LIST(APPLY_HISTO_FILL, "JetKinematicCuts") - } - histos.fill(HIST("IntegratedCuts/pRingCutsSubLeadingJet"), 2, ringObservable2ndJet); - histos.fill(HIST("IntegratedCuts/hCountCutsSubLeadingJet"), 2); + if (familySwitches.doFamilyRingKinematicCuts) { + FLUSH_DELTA_TRACKER("RingKinematicCuts", trackRingKinCuts, mAxisPt, mAxisMass, mAxisDTheta) } - if (kinematicLambdaCheck && kinematicLeadPCheck) { - if (familySwitches.doFamilyJetAndLambdaKinematicCuts) { - RING_OBSERVABLE_LEADP_FILL_LIST(APPLY_HISTO_FILL, "JetAndLambdaKinematicCuts") - } - histos.fill(HIST("IntegratedCuts/pRingCutsLeadingP"), 3, ringObservableLeadP); - histos.fill(HIST("IntegratedCuts/hCountCutsLeadingP"), 3); - - // Filling checks that rely on Eta>0 or Eta<0 checks for V0 and LeadingP eta: - if (familySwitches.doFamilyJetAndLambdaKinematicCuts) { - RING_OBSERVABLE_LEADP_ETA_SPLIT_FILL_LIST("JetAndLambdaKinematicCuts", leadPEtaPos, lambdaEtaPos); - } + if (familySwitches.doFamilyJetKinematicCuts) { + FLUSH_DELTA_TRACKER("JetKinematicCuts", trackJetKinCuts, mAxisPt, mAxisMass, mAxisDTheta) } - if (kinematicLambdaCheck && kinematic2ndJetCheck) { - if (familySwitches.doFamilyJetAndLambdaKinematicCuts) { - RING_OBSERVABLE_2NDJET_FILL_LIST(APPLY_HISTO_FILL, "JetAndLambdaKinematicCuts") - } - histos.fill(HIST("IntegratedCuts/pRingCutsSubLeadingJet"), 3, ringObservable2ndJet); - histos.fill(HIST("IntegratedCuts/hCountCutsSubLeadingJet"), 3); + if (familySwitches.doFamilyJetAndLambdaKinematicCuts) { + FLUSH_DELTA_TRACKER("JetAndLambdaKinematicCuts", trackJetLambdaKinCuts, mAxisPt, mAxisMass, mAxisDTheta) } - } // end v0s loop - - // Flush trackers to the actual O2 histograms (via macros, so that O2 compiles properly): - if (familySwitches.doFamilyRing) { - FLUSH_DELTA_TRACKER("Ring", trackRing, mAxisPt, mAxisMass, mAxisDTheta) - } - if (familySwitches.doFamilyRingKinematicCuts) { - FLUSH_DELTA_TRACKER("RingKinematicCuts", trackRingKinCuts, mAxisPt, mAxisMass, mAxisDTheta) - } - if (familySwitches.doFamilyJetKinematicCuts) { - FLUSH_DELTA_TRACKER("JetKinematicCuts", trackJetKinCuts, mAxisPt, mAxisMass, mAxisDTheta) - } - if (familySwitches.doFamilyJetAndLambdaKinematicCuts) { - FLUSH_DELTA_TRACKER("JetAndLambdaKinematicCuts", trackJetLambdaKinCuts, mAxisPt, mAxisMass, mAxisDTheta) - } - } // end collisions + } // end collisions } // end of resampling loop for forceRandJet and forceDatalikeJet } @@ -2357,4 +2414,4 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) } // Avoid macro leakage! -#undef APPLY_HISTO_FILL \ No newline at end of file +#undef APPLY_HISTO_FILL From 4356704f63b6c08f976a513aecbec8f1cb133b16 Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Wed, 19 Aug 2026 22:57:24 +0000 Subject: [PATCH 26/31] Final fill guard corrections and comment removals --- .../lambdaJetPolarizationIonsDerived.cxx | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx index 65b353e0ec2..aa56694d445 100644 --- a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx @@ -70,7 +70,6 @@ using namespace o2::framework; using namespace o2::framework::expressions; using ROOT::Math::PtEtaPhiMVector; using ROOT::Math::XYZVector; -// using namespace o2::aod::lambdajetpol; // Used it explicitly along the code for clarity // Declaring constants: constexpr double ProtonMass = o2::constants::physics::MassProton; // Assumes particle identification for daughter is perfect @@ -530,6 +529,8 @@ struct lambdajetpolarizationionsderived { << "Previous-jet/Mixed-Event proxies do not change between resamplings, so every extra pass would double-count the same proxy."; if (excludeOutOfPeakQA && excludeInPeakQA) // Complementary selections LOG(fatal) << "excludeOutOfPeakQA and excludeInPeakQA are complementary: enabling both rejects every V0."; + if (!familySwitches.doFamilyRing) // Todo: think of a smarter way of handling the axis getters for the DeltaMethod + LOG(fatal) << "doFamilyRing must be on: the Delta Method accumulators take their binning from the Ring/ histograms."; // Ring observable histograms: // Helper to register one full histogram family (kinematic cut variation of ring observable) @@ -1469,7 +1470,7 @@ struct lambdajetpolarizationionsderived { // First we build lookup tables based on current dataframe's collisions (connects pairs of jet proxies from similar collisions): // (these proxies may come from collisions with no valid Lambdas, by construction, enabling more mixes) if (fakePolSwitches.doMixedEventProxies) { // This is performed out of the resampling loop, so nProxyResamples will not resample event mixing candidates - auto getMixCentrality = [this](o2::aod::RingCollision const& col) { return this->getCentrality(col); }; + auto getMixCentrality = [this](o2::aod::RingCollision const& col) { return this->getCentrality(col); }; // Already filtered even though referencing only RingCollision, not an iterator to Filtered table // For the leading particle: // Pattern follows MixedEventsLambdaBinning tutorial (captures leadPs table and cache define in task's struct): @@ -1484,7 +1485,7 @@ struct lambdajetpolarizationionsderived { true}; // Ignore overflows true // SameKindPair defaults to CombinationsBlockStrictlyUpperSameIndexPolicy, so no same-event mixing should happen: - SameKindPair leadPPair{ + SameKindPair leadPPair{ // Already filtered even though referencing only RingCollision, so no need to access the filtered table leadPBinning, fakePolSwitches.mixedEventWindowSize, -1, collisions, std::make_tuple(leadPs), &mixCache}; std::unordered_map leadPCandidateCount; @@ -1641,7 +1642,7 @@ struct lambdajetpolarizationionsderived { // Used this dummy for backwards compatibility, under the reasonable assumption that the field points always in the same *direction* in the used runs // (it is not worth it to fetch and store the magnetic field in the datamodel) - float magField = 1.f; // Purely geometric. + const float magField = 1.f; // Purely geometric. // Slice jets, V0s and leading particle belonging to this collision: // (global collision indices repeat a lot, but they are unique to a same TimeFrame (TF) subfolder in the derived data) @@ -1857,7 +1858,7 @@ struct lambdajetpolarizationionsderived { // Code above was superseeded: new datamodel does not store ambiguous candidates! // The new getter comes at the very start of processPolarizationData() now. - // Initialize delta method accumulators (reset for new dataframe): + // Initialize delta method accumulators (reset for new collision): for (auto const& tracker : {&trackRing, &trackRingKinCuts, &trackJetKinCuts, &trackJetLambdaKinCuts}) tracker->reset(); for (auto const& v0 : v0sInColl) { @@ -1986,7 +1987,6 @@ struct lambdajetpolarizationionsderived { float deltaThetaLeadP = 0.; float cosDeltaThetaLeadP = 0.; if (hasValidLeadingP) { - // Cross product XYZVector crossLeadP = leadPUnitVec.Cross(lambdaLike3Vec); ringObservableLeadP = protonLikeStarUnit3Vec.Dot(crossLeadP) / crossLeadP.R(); // Adding the prefactor related to the CP-violating decay (decay constants have different signs) @@ -2013,7 +2013,6 @@ struct lambdajetpolarizationionsderived { float Jz = 0.; float ringObservableOverJetZ = 0.; if (hasValidLeadingJet) { - // Cross product XYZVector cross = leadingJetUnitVec.Cross(lambdaLike3Vec); ringObservable = protonLikeStarUnit3Vec.Dot(cross) / cross.R(); // Adding prefactor From 602e36fbfe1f04f94ca4dc25fd961947c04bd72d Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Thu, 20 Aug 2026 17:07:56 +0000 Subject: [PATCH 27/31] Some more clang-formatting changes before PR --- .../Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx index aa56694d445..3375b227ecd 100644 --- a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx @@ -1469,7 +1469,8 @@ struct lambdajetpolarizationionsderived { std::unordered_map mixedSubJetByCollision; // First we build lookup tables based on current dataframe's collisions (connects pairs of jet proxies from similar collisions): // (these proxies may come from collisions with no valid Lambdas, by construction, enabling more mixes) - if (fakePolSwitches.doMixedEventProxies) { // This is performed out of the resampling loop, so nProxyResamples will not resample event mixing candidates + // (This is performed out of the resampling loop, so nProxyResamples will not resample event mixing candidates) + if (fakePolSwitches.doMixedEventProxies) { auto getMixCentrality = [this](o2::aod::RingCollision const& col) { return this->getCentrality(col); }; // Already filtered even though referencing only RingCollision, not an iterator to Filtered table // For the leading particle: @@ -1485,7 +1486,8 @@ struct lambdajetpolarizationionsderived { true}; // Ignore overflows true // SameKindPair defaults to CombinationsBlockStrictlyUpperSameIndexPolicy, so no same-event mixing should happen: - SameKindPair leadPPair{ // Already filtered even though referencing only RingCollision, so no need to access the filtered table + //(Already filtered by Zvtx even though we call by aod::RingCollisions, so no need to access the filtered table) + SameKindPair leadPPair{ leadPBinning, fakePolSwitches.mixedEventWindowSize, -1, collisions, std::make_tuple(leadPs), &mixCache}; std::unordered_map leadPCandidateCount; From 961d4f59618014e7f2956faa17d65346921620b9 Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Thu, 20 Aug 2026 17:47:30 +0000 Subject: [PATCH 28/31] IWYU fixes and cppcheck fixes --- PWGLF/DataModel/lambdaJetPolarizationIons.h | 1 + .../Strangeness/lambdaJetPolarizationIons.cxx | 8 +++----- .../Strangeness/lambdaJetPolarizationIonsDerived.cxx | 9 +++++++-- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/PWGLF/DataModel/lambdaJetPolarizationIons.h b/PWGLF/DataModel/lambdaJetPolarizationIons.h index e17c0250f62..cbb6c0d0abc 100644 --- a/PWGLF/DataModel/lambdaJetPolarizationIons.h +++ b/PWGLF/DataModel/lambdaJetPolarizationIons.h @@ -24,6 +24,7 @@ #include #include +#include namespace o2::aod { diff --git a/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx b/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx index 968eadeae8e..f9253f3469c 100644 --- a/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx +++ b/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx @@ -72,10 +72,10 @@ #include #include #include -#include #include #include +#include #include #include #include @@ -303,7 +303,6 @@ struct lambdajetpolarizationions { // For some binning variables: int mBinHasRingJet; int mBinHasRingV0; - std::map metadata; o2::parameters::GRPMagField* grpmag = nullptr; // Histogram axes configuration: @@ -1241,11 +1240,10 @@ struct lambdajetpolarizationions { fillEventSelectionQA(++selectionIdx, centrality); if (!rctConfigurations.cfgRCTLabel.value.empty() && !rctFlagsChecker(collision)) return false; - if (fillHists) + if (fillHists) { fillEventSelectionQA(++selectionIdx, centrality); - // Filling histograms previously filled in fillReconstructedEventProperties here, to avoid re-accessing data: - if (fillHists) { + // Filling histograms previously filled in fillReconstructedEventProperties here, to avoid re-accessing data: histos.fill(HIST("hEventOccupancy"), collisionOccupancy); histos.fill(HIST("hCentralityVsOccupancy"), centrality, collisionOccupancy); histos.fill(HIST("hInteractionRate"), interactionRate); diff --git a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx index 3375b227ecd..fa370d7eb71 100644 --- a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx @@ -36,7 +36,6 @@ #include #include #include -#include #include #include #include @@ -53,12 +52,18 @@ #include #include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) #include +#include +#include +#include #include +#include #include // For perpendicular jet direction QAs #include // std::fill, for resetting the Delta Method accumulators #include #include +#include +#include #include #include #include @@ -1203,7 +1208,7 @@ struct lambdajetpolarizationionsderived { /// \note Shared across leadP/leadJet/subJet: the caller resolves which proxy-specific procedure (e.g., LUT for evtMixing) applies. // Helper to modify the jet direction for QA and for spurious signal baseline removal tests: inline void applyProxyDistortion(ProxyState proxy, float minPtThreshold, ProxyCacheRef cache, - std::discrete_distribution& etaDist, std::discrete_distribution& phiDist, std::mt19937& rng) + const std::discrete_distribution& etaDist, const std::discrete_distribution& phiDist, std::mt19937& rng) { if (!fakePolSwitches.forcePerpToJet && !fakePolSwitches.forceJetDirectionSmudge && !fakePolSwitches.forceRandJet && !fakePolSwitches.forcePreviousJet && !fakePolSwitches.forceDatalikeJet && !fakePolSwitches.doMixedEventProxies) [[likely]] { return; // Skip this function if none of the modifications are actually being executed! From 0d009f639a8b79b006af151fee08ee71a7482609 Mon Sep 17 00:00:00 2001 From: ALICE Action Bot Date: Thu, 20 Aug 2026 18:10:14 +0000 Subject: [PATCH 29/31] Please consider the following formatting changes --- PWGLF/DataModel/lambdaJetPolarizationIons.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PWGLF/DataModel/lambdaJetPolarizationIons.h b/PWGLF/DataModel/lambdaJetPolarizationIons.h index cbb6c0d0abc..0a336ecf385 100644 --- a/PWGLF/DataModel/lambdaJetPolarizationIons.h +++ b/PWGLF/DataModel/lambdaJetPolarizationIons.h @@ -44,7 +44,7 @@ DECLARE_SOA_COLUMN(JetPt, jetPt, float); DECLARE_SOA_COLUMN(JetEta, jetEta, float); DECLARE_SOA_COLUMN(JetPhi, jetPhi, float); // DECLARE_SOA_COLUMN(JetNConstituents, jetNConstituents, uint16_t); // Currently removed from datamodel. - // Other variables can better reveal jet quenching and help identify good selection criteria for quenched jets proxies +// Other variables can better reveal jet quenching and help identify good selection criteria for quenched jets proxies DECLARE_SOA_COLUMN(LeadParticlePt, leadParticlePt, float); DECLARE_SOA_COLUMN(LeadParticleEta, leadParticleEta, float); From 3f17087c258c9df620613576224e65e74818e21a Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Thu, 20 Aug 2026 18:29:11 +0000 Subject: [PATCH 30/31] Fixing possibly wrong flagged usage by cppcheck --- PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx index fa370d7eb71..d333ceb694f 100644 --- a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx @@ -1208,7 +1208,7 @@ struct lambdajetpolarizationionsderived { /// \note Shared across leadP/leadJet/subJet: the caller resolves which proxy-specific procedure (e.g., LUT for evtMixing) applies. // Helper to modify the jet direction for QA and for spurious signal baseline removal tests: inline void applyProxyDistortion(ProxyState proxy, float minPtThreshold, ProxyCacheRef cache, - const std::discrete_distribution& etaDist, const std::discrete_distribution& phiDist, std::mt19937& rng) + std::discrete_distribution& etaDist, std::discrete_distribution& phiDist, std::mt19937& rng) { if (!fakePolSwitches.forcePerpToJet && !fakePolSwitches.forceJetDirectionSmudge && !fakePolSwitches.forceRandJet && !fakePolSwitches.forcePreviousJet && !fakePolSwitches.forceDatalikeJet && !fakePolSwitches.doMixedEventProxies) [[likely]] { return; // Skip this function if none of the modifications are actually being executed! From 6c7094a7dedaaf201f394d07f9e2011811e93cb0 Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Thu, 20 Aug 2026 20:35:20 +0000 Subject: [PATCH 31/31] Small fix on lambda function variable capturings --- PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx index d333ceb694f..72a6465ced8 100644 --- a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx @@ -1523,7 +1523,7 @@ struct lambdajetpolarizationionsderived { // Leading jet: // pt comes from the jetProxyByCollision pre-pass above. // (Collisions whose leading jet failed minLeadJetPt get the -999 sentinel, so they never enter this mixing) - auto getMixLeadJetPt = [this, &jetProxyByCollision](o2::aod::RingCollision const& col) { + auto getMixLeadJetPt = [&jetProxyByCollision](o2::aod::RingCollision const& col) { auto cacheIt = jetProxyByCollision.find(col.globalIndex()); if (cacheIt == jetProxyByCollision.end() || !cacheIt->second.hasValidLeadingJet) return -999.f; @@ -1559,7 +1559,7 @@ struct lambdajetpolarizationionsderived { } // Subleading jet: - auto getMixSubJetPt = [this, &jetProxyByCollision](o2::aod::RingCollision const& col) { + auto getMixSubJetPt = [&jetProxyByCollision](o2::aod::RingCollision const& col) { auto cacheIt = jetProxyByCollision.find(col.globalIndex()); if (cacheIt == jetProxyByCollision.end() || !cacheIt->second.hasValidSubJet) return -999.f;