From 2819b3c78cf684f3385c2a05aa82f7e0108093fd Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Thu, 16 Oct 2025 15:53:04 -0300 Subject: [PATCH 01/40] Testing Lambda invariant mass reconstruction from the derivedlambdakzeroanalysis.cxx code, locally --- PWGLF/Tasks/Strangeness/lambdaInvMassTest.cxx | 1311 +++++++++++++++++ 1 file changed, 1311 insertions(+) create mode 100644 PWGLF/Tasks/Strangeness/lambdaInvMassTest.cxx diff --git a/PWGLF/Tasks/Strangeness/lambdaInvMassTest.cxx b/PWGLF/Tasks/Strangeness/lambdaInvMassTest.cxx new file mode 100644 index 00000000000..e7fa162c2f4 --- /dev/null +++ b/PWGLF/Tasks/Strangeness/lambdaInvMassTest.cxx @@ -0,0 +1,1311 @@ +/// \file lambdaInvMassTest.cxx +/// \brief Lambda analysis task using derived data as a test +/// +/// \author Cicero Domenico Muncinelli , Campinas State University, Brazil +// +// Lambda Invariant Mass test task +// ================ +// +// This code loops over a V0Cores table and produces some +// standard analysis output. It is meant to be run over +// derived data. +// This is NOT meant to be in the ALICE O2 repository, +// ever! It is just a small test for me to get up to +// speed with O2 analyses! +// +// Comments, questions, complaints, suggestions? +// Please write to: +// cicero.domenico.muncinelli@cern.ch +// +// This code is heavily based on the derivedlambdakzeroanalysis.cxx code! + +#include "PWGLF/DataModel/LFStrangenessMLTables.h" +#include "PWGLF/DataModel/LFStrangenessPIDTables.h" +#include "PWGLF/DataModel/LFStrangenessTables.h" +#include "PWGUD/Core/SGSelector.h" + +#include "Common/CCDB/ctpRateFetcher.h" +#include "Common/Core/TrackSelection.h" +#include "Common/Core/trackUtilities.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponse.h" +#include "Common/DataModel/TrackSelectionTables.h" +#include "Tools/ML/MlResponse.h" +#include "Tools/ML/model.h" + +#include "CommonConstants/MathConstants.h" +#include "CommonConstants/PhysicsConstants.h" +#include "DataFormatsParameters/GRPMagField.h" +#include "Framework/ASoAHelpers.h" +#include "Framework/AnalysisDataModel.h" +#include "Framework/AnalysisTask.h" +#include "Framework/runDataProcessing.h" +#include "ReconstructionDataFormats/Track.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using std::array; + +using namespace o2::aod::rctsel; + +using DauTracks = soa::Join; +using DauMCTracks = soa::Join; +// using V0Candidates = soa::Join; +using V0Candidates = soa::Join; +// using V0McCandidates = soa::Join; +// using V0McCandidates = soa::Join; +using V0McCandidates = soa::Join; + +// simple checkers, but ensure 64 bit integers +#define BITSET(var, nbit) ((var) |= (static_cast(1) << static_cast(nbit))) +#define BITCHECK(var, nbit) ((var) & (static_cast(1) << static_cast(nbit))) + +enum CentEstimator { + kCentFT0C = 0, + kCentFT0M, + kCentFT0CVariant1, + kCentMFT, + kCentNGlobal, + kCentFV0A +}; + +// bool doPlainTopoQA = true; + +struct lambdaInvMassTest{ + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + bool isRun3 = true; + + // master analysis switches + Configurable analyseK0Short{"analyseK0Short", false, "process K0Short-like candidates"}; + Configurable analyseLambda{"analyseLambda", true, "process Lambda-like candidates"}; + Configurable analyseAntiLambda{"analyseAntiLambda", false, "process AntiLambda-like candidates"}; + Configurable calculateFeeddownMatrix{"calculateFeeddownMatrix", true, "fill feeddown matrix if MC"}; + + Configurable doPPAnalysis{"doPPAnalysis", true, "if in pp, set to true"}; + Configurable irSource{"irSource", "T0VTX", "Estimator of the interaction rate (Recommended: pp --> T0VTX, Pb-Pb --> ZNC hadronic)"}; + Configurable centralityEstimator{"centralityEstimator", kCentFT0C, "Run 3 centrality estimator (0:CentFT0C, 1:CentFT0M, 2:CentFT0CVariant1, 3:CentMFT, 4:CentNGlobal, 5:CentFV0A)"}; + + Configurable doEventQA{"doEventQA", false, "do event QA histograms"}; + Configurable doCompleteTopoQA{"doCompleteTopoQA", false, "do topological variable QA histograms"}; + Configurable doTPCQA{"doTPCQA", false, "do TPC QA histograms"}; + Configurable doTOFQA{"doTOFQA", false, "do TOF QA histograms"}; + Configurable doDetectPropQA{"doDetectPropQA", 0, "do Detector/ITS map QA: 0: no, 1: 4D, 2: 5D with mass; 3: plain in 3D"}; + Configurable doEtaPhiQA{"doEtaPhiQA", false, "do Eta/Phi QA histograms"}; + + Configurable doPlainTopoQA{"doPlainTopoQA", true, "do simple 1D QA of candidates"}; + Configurable qaMinPt{"qaMinPt", 0.0f, "minimum pT for QA plots"}; + Configurable qaMaxPt{"qaMaxPt", 1000.0f, "maximum pT for QA plots"}; + Configurable qaCentrality{"qaCentrality", false, "qa centrality flag: check base raw values"}; + + // for MC + Configurable doMCAssociation{"doMCAssociation", true, "if MC, do MC association"}; // Will not do MC, so can keep this on regardless + Configurable doTreatPiToMuon{"doTreatPiToMuon", false, "Take pi decay into muon into account in MC"}; + Configurable doCollisionAssociationQA{"doCollisionAssociationQA", true, "check collision association"}; + + // // Defining a configurable axis for easier manipulation later on: + // ConfigurableAxis axisPtQA{"axisPtQA", {VARIABLE_WIDTH, 0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, + // 0.6f, 0.7f, 0.8f, 0.9f, 1.0f, 1.1f, 1.2f, 1.3f, 1.4f, 1.5f, 1.6f, 1.7f, 1.8f, 1.9f, 2.0f, 2.2f, + // 2.4f, 2.6f, 2.8f, 3.0f, 3.2f, 3.4f, 3.6f, 3.8f, 4.0f, 4.4f, 4.8f, 5.2f, 5.6f, 6.0f, 6.5f, 7.0f, + // 7.5f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 17.0f, 19.0f, 21.0f, 23.0f, 25.0f, + // 30.0f, 35.0f, 40.0f, 50.0f}, "pt axis for QA histograms"}; + // // ConfigurableAxis axisInvMassLambda{"axisInvMassLambda", {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}, "Invariant mass axis for Lambda"}; + + // My own version of these axes: + // Configurable nBinsInvMass{"nBinsInvMass", 100, "Number of bins of invariant mass axis"}; + // Configurable minInvMass{"minInvMass", 0.7f, "Lower bound of invariant mass axis"}; + // Configurable maxInvMass{"maxInvMass", 1.3f, "Upper bound of invariant mass axis"}; + // From David's code: + ConfigurableAxis axisLambdaMass{"axisLambdaMass", {200, 1.101f, 1.131f}, ""}; + + // Defining a configurable object group for the event selections: + struct : ConfigurableGroup { + std::string prefix = "eventSelections"; // JSON group name + Configurable requireSel8{"requireSel8", true, "require sel8 event selection"}; + Configurable requireTriggerTVX{"requireTriggerTVX", true, "require FT0 vertex (acceptable FT0C-FT0A time difference) at trigger level"}; + Configurable rejectITSROFBorder{"rejectITSROFBorder", true, "reject events at ITS ROF border (Run 3 only)"}; + Configurable rejectTFBorder{"rejectTFBorder", true, "reject events at TF border (Run 3 only)"}; + 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)"}; + 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 rejectSameBunchPileup{"rejectSameBunchPileup", true, "reject collisions in case of pileup with another collision in the same foundBC (Run 3 only)"}; + 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)"}; + Configurable requireINEL0{"requireINEL0", true, "require INEL>0 event selection"}; + Configurable requireINEL1{"requireINEL1", false, "require INEL>1 event selection"}; + + Configurable maxZVtxPosition{"maxZVtxPosition", 10., "max Z vtx position"}; + + Configurable useEvtSelInDenomEff{"useEvtSelInDenomEff", false, "Consider event selections in the recoed <-> gen collision association for the denominator (or numerator) of the acc. x eff. (or signal loss)?"}; + Configurable applyZVtxSelOnMCPV{"applyZVtxSelOnMCPV", false, "Apply Z-vtx cut on the PV of the generated collision?"}; + Configurable useFT0CbasedOccupancy{"useFT0CbasedOccupancy", false, "Use sum of FT0-C amplitudes for estimating occupancy? (if not, use track-based definition)"}; + // 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 + Configurable minIR{"minIR", -1, "minimum IR collisions"}; + Configurable maxIR{"maxIR", -1, "maximum IR collisions"}; + + // Run 2 specific event selections + Configurable requireSel7{"requireSel7", true, "require sel7 event selection (Run 2 only: event selection decision based on V0A & V0C)"}; + Configurable requireINT7{"requireINT7", true, "require INT7 trigger selection (Run 2 only)"}; + Configurable rejectIncompleteDAQ{"rejectIncompleteDAQ", true, "reject events with incomplete DAQ (Run 2 only)"}; + Configurable requireConsistentSPDAndTrackVtx{"requireConsistentSPDAndTrackVtx", true, "reject events with inconsistent in SPD and Track vertices (Run 2 only)"}; + Configurable rejectPileupFromSPD{"rejectPileupFromSPD", true, "reject events with pileup according to SPD vertexer (Run 2 only)"}; + Configurable rejectV0PFPileup{"rejectV0PFPileup", false, "reject events tagged as OOB pileup according to V0 past-future info (Run 2 only)"}; + Configurable rejectPileupInMultBins{"rejectPileupInMultBins", true, "reject events tagged as pileup according to multiplicity-differential pileup checks (Run 2 only)"}; + Configurable rejectPileupMV{"rejectPileupMV", true, "reject events tagged as pileup according to according to multi-vertexer (Run 2 only)"}; + Configurable rejectTPCPileup{"rejectTPCPileup", false, "reject events tagged as pileup according to pileup in TPC (Run 2 only)"}; + Configurable requireNoV0MOnVsOffPileup{"requireNoV0MOnVsOffPileup", false, "reject events tagged as OOB pileup according to online-vs-offline VOM correlation (Run 2 only)"}; + Configurable requireNoSPDOnVsOffPileup{"requireNoSPDOnVsOffPileup", false, "reject events tagged as pileup according to online-vs-offline SPD correlation (Run 2 only)"}; + Configurable requireNoSPDClsVsTklBG{"requireNoSPDClsVsTklBG", true, "reject events tagged as beam-gas and pileup according to cluster-vs-tracklet correlation (Run 2 only)"}; + + Configurable useSPDTrackletsCent{"useSPDTrackletsCent", false, "Use SPD tracklets for estimating centrality? If not, use V0M-based centrality (Run 2 only)"}; + } eventSelections; + + // Defining the configurable object that is going to be used: v0Selections, for selections later on + struct : ConfigurableGroup { + std::string prefix = "v0Selections"; // JSON group name + Configurable v0TypeSelection{"v0TypeSelection", 1, "select on a certain V0 type (leave negative if no selection desired)"}; + + // Selection criteria: acceptance + Configurable rapidityCut{"rapidityCut", 0.5, "rapidity"}; + Configurable daughterEtaCut{"daughterEtaCut", 0.8, "max eta for daughters"}; + + // Standard 5 topological criteria + Configurable v0cospa{"v0cospa", 0.97, "min V0 CosPA"}; + Configurable dcav0dau{"dcav0dau", 1.0, "max DCA V0 Daughters (cm)"}; + Configurable dcanegtopv{"dcanegtopv", .05, "min DCA Neg To PV (cm)"}; + Configurable dcapostopv{"dcapostopv", .05, "min DCA Pos To PV (cm)"}; + Configurable v0radius{"v0radius", 1.2, "minimum V0 radius (cm)"}; + Configurable v0radiusMax{"v0radiusMax", 1E5, "maximum V0 radius (cm)"}; + Configurable> lifetimecut{"lifetimecut", {DefaultLifetimeCuts[0], 2, {"lifetimecutLambda", "lifetimecutK0S"}}, "lifetimecut"}; + + // // Additional selection on the AP plot (exclusive for K0Short) + // // original equation: lArmPt*5>TMath::Abs(lArmAlpha) + // Configurable armPodCut{"armPodCut", 5.0f, "pT * (cut) > |alpha|, AP cut. Negative: no cut"}; + + // Track quality + Configurable minTPCrows{"minTPCrows", 70, "minimum TPC crossed rows"}; + Configurable minITSclusters{"minITSclusters", -1, "minimum ITS clusters"}; + Configurable minTPCrowsOverFindableClusters{"minTPCrowsOverFindableClusters", -1, "minimum nbr of TPC crossed rows over findable clusters"}; + Configurable minTPCfoundOverFindableClusters{"minTPCfoundOverFindableClusters", -1, "minimum nbr of found over findable TPC clusters"}; + Configurable maxFractionTPCSharedClusters{"maxFractionTPCSharedClusters", 1e+09, "maximum fraction of TPC shared clusters"}; + Configurable maxITSchi2PerNcls{"maxITSchi2PerNcls", 1e+09, "maximum ITS chi2 per clusters"}; + Configurable maxTPCchi2PerNcls{"maxTPCchi2PerNcls", 1e+09, "maximum TPC chi2 per clusters"}; + Configurable skipTPConly{"skipTPConly", false, "skip V0s comprised of at least one TPC only prong"}; + Configurable requirePosITSonly{"requirePosITSonly", false, "require that positive track is ITSonly (overrides TPC quality)"}; + Configurable requireNegITSonly{"requireNegITSonly", false, "require that negative track is ITSonly (overrides TPC quality)"}; + Configurable rejectPosITSafterburner{"rejectPosITSafterburner", false, "reject positive track formed out of afterburner ITS tracks"}; + Configurable rejectNegITSafterburner{"rejectNegITSafterburner", false, "reject negative track formed out of afterburner ITS tracks"}; + Configurable requirePosITSafterburnerOnly{"requirePosITSafterburnerOnly", false, "require positive track formed out of afterburner ITS tracks"}; + Configurable requireNegITSafterburnerOnly{"requireNegITSafterburnerOnly", false, "require negative track formed out of afterburner ITS tracks"}; + Configurable rejectTPCsectorBoundary{"rejectTPCsectorBoundary", false, "reject tracks close to the TPC sector boundaries"}; + Configurable phiLowCut{"phiLowCut", "0.06/x+pi/18.0-0.06", "Low azimuth cut parametrisation"}; + Configurable phiHighCut{"phiHighCut", "0.1/x+pi/18.0+0.06", "High azimuth cut parametrisation"}; + + // PID (TPC/TOF) + Configurable tpcPidNsigmaCut{"tpcPidNsigmaCut", 5, "tpcPidNsigmaCut"}; + Configurable tofPidNsigmaCutLaPr{"tofPidNsigmaCutLaPr", 1e+6, "tofPidNsigmaCutLaPr"}; + Configurable tofPidNsigmaCutLaPi{"tofPidNsigmaCutLaPi", 1e+6, "tofPidNsigmaCutLaPi"}; + Configurable tofPidNsigmaCutK0Pi{"tofPidNsigmaCutK0Pi", 1e+6, "tofPidNsigmaCutK0Pi"}; + + // PID (TOF) + Configurable maxDeltaTimeProton{"maxDeltaTimeProton", 1e+9, "check maximum allowed time"}; + Configurable maxDeltaTimePion{"maxDeltaTimePion", 1e+9, "check maximum allowed time"}; + } v0Selections; + + // Machine learning evaluation for pre-selection and corresponding information generation + // o2::ml::OnnxModel mlCustomModelK0Short; + o2::ml::OnnxModel mlCustomModelLambda; + o2::ml::OnnxModel mlCustomModelAntiLambda; + o2::ml::OnnxModel mlCustomModelGamma; + + struct : ConfigurableGroup { + std::string prefix = "mlConfigurations"; // JSON group name + // ML classifiers: master flags to control whether we should use custom ML classifiers or the scores in the derived data + // Configurable useK0ShortScores{"useK0ShortScores", false, "use ML scores to select K0Short"}; + Configurable useLambdaScores{"useLambdaScores", false, "use ML scores to select Lambda"}; + // Configurable useAntiLambdaScores{"useAntiLambdaScores", false, "use ML scores to select AntiLambda"}; + + // Configurable calculateK0ShortScores{"calculateK0ShortScores", false, "calculate K0Short ML scores"}; + Configurable calculateLambdaScores{"calculateLambdaScores", false, "calculate Lambda ML scores"}; + // Configurable calculateAntiLambdaScores{"calculateAntiLambdaScores", false, "calculate AntiLambda ML scores"}; + + // ML input for ML calculation + Configurable customModelPathCCDB{"customModelPathCCDB", "", "Custom ML Model path in CCDB"}; + Configurable timestampCCDB{"timestampCCDB", -1, "timestamp of the ONNX file for ML model used to query in CCDB. Exceptions: > 0 for the specific timestamp, 0 gets the run dependent timestamp"}; + Configurable loadCustomModelsFromCCDB{"loadCustomModelsFromCCDB", false, "Flag to enable or disable the loading of custom models from CCDB"}; + Configurable enableOptimizations{"enableOptimizations", false, "Enables the ONNX extended model-optimization: sessionOptions.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_EXTENDED)"}; + + // Local paths for test purposes + Configurable localModelPathLambda{"localModelPathLambda", "Lambda_BDTModel.onnx", "(std::string) Path to the local .onnx file."}; + // Configurable localModelPathAntiLambda{"localModelPathAntiLambda", "AntiLambda_BDTModel.onnx", "(std::string) Path to the local .onnx file."}; + // Configurable localModelPathK0Short{"localModelPathK0Short", "KZeroShort_BDTModel.onnx", "(std::string) Path to the local .onnx file."}; + + // Thresholds for choosing to populate V0Cores tables with pre-selections + Configurable thresholdLambda{"thresholdLambda", -1.0f, "Threshold to keep Lambda candidates"}; + // Configurable thresholdAntiLambda{"thresholdAntiLambda", -1.0f, "Threshold to keep AntiLambda candidates"}; + // Configurable thresholdK0Short{"thresholdK0Short", -1.0f, "Threshold to keep K0Short candidates"}; + } mlConfigurations; + + // CCDB options + struct : ConfigurableGroup { + std::string prefix = "ccdbConfigurations"; // JSON group name + Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable grpPath{"grpPath", "GLO/GRP/GRP", "Path of the grp file"}; + Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; + Configurable lutPath{"lutPath", "GLO/Param/MatLUT", "Path of the Lut parametrization"}; + Configurable geoPath{"geoPath", "GLO/Config/GeometryAligned", "Path of the geometry file"}; + Configurable mVtxPath{"mVtxPath", "GLO/Calib/MeanVertex", "Path of the mean vertex file"}; + + // manual + Configurable useCustomMagField{"useCustomMagField", false, "Use custom magnetic field value"}; + Configurable customMagField{"customMagField", 5.0f, "Manually set magnetic field"}; + } ccdbConfigurations; + + // CCDB options + struct : ConfigurableGroup { + 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 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 axisK0Mass{"axisK0Mass", {200, 0.4f, 0.6f}, ""}; + ConfigurableAxis axisLambdaMass{"axisLambdaMass", {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)"}; + 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)"}; + ConfigurableAxis axisDCAdau{"axisDCAdau", {20, 0.0f, 2.0f}, "DCA (cm)"}; + 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"}; + ConfigurableAxis axisTPCsignal{"axisTPCsignal", {200, 0.0f, 200.0f}, "TPC signal"}; + ConfigurableAxis axisNsigmaTOF{"axisNsigmaTOF", {200, -10.0f, 10.0f}, "N sigma TOF"}; + ConfigurableAxis axisTOFdeltaT{"axisTOFdeltaT", {200, -5000.0f, 5000.0f}, "TOF Delta T (ps)"}; + ConfigurableAxis axisPhi{"axisPhi", {18, 0.0f, constants::math::TwoPI}, "Azimuth angle (rad)"}; + ConfigurableAxis axisPhiMod{"axisPhiMod", {100, 0.0f, constants::math::TwoPI / 18}, "Azimuth angle wrt TPC sector (rad.)"}; + ConfigurableAxis axisEta{"axisEta", {10, -1.0f, 1.0f}, "#eta"}; + 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"}; + + // UPC axes + ConfigurableAxis axisSelGap{"axisSelGap", {4, -1.5, 2.5}, "Gap side"}; + + // 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"}; + + // MC coll assoc QA axis + ConfigurableAxis axisMonteCarloNch{"axisMonteCarloNch", {300, 0.0f, 3000.0f}, "N_{ch} MC"}; + } axisConfigurations; + + + // For manual sliceBy + // Preslice> perMcCollision = aod::v0data::straMCCollisionId; + PresliceUnsorted> perMcCollision = aod::v0data::straMCCollisionId; + PresliceUnsorted> perMcCollisionRun2 = aod::v0data::straMCCollisionId; + + enum Selection : uint64_t { selCosPA = 0, + selRadius, + selRadiusMax, + selDCANegToPV, + selDCAPosToPV, + selDCAV0Dau, + selK0ShortRapidity, + selLambdaRapidity, + selTPCPIDPositivePion, + selTPCPIDNegativePion, + selTPCPIDPositiveProton, + selTPCPIDNegativeProton, + selTOFDeltaTPositiveProtonLambda, + selTOFDeltaTPositivePionLambda, + selTOFDeltaTPositivePionK0Short, + selTOFDeltaTNegativeProtonLambda, + selTOFDeltaTNegativePionLambda, + selTOFDeltaTNegativePionK0Short, + selTOFNSigmaPositiveProtonLambda, // Nsigma + selTOFNSigmaPositivePionLambda, // Nsigma + selTOFNSigmaPositivePionK0Short, // Nsigma + selTOFNSigmaNegativeProtonLambda, // Nsigma + selTOFNSigmaNegativePionLambda, // Nsigma + selTOFNSigmaNegativePionK0Short, // Nsigma + selK0ShortCTau, + selLambdaCTau, + selK0ShortArmenteros, + selPosGoodTPCTrack, // at least min # TPC rows + selNegGoodTPCTrack, // at least min # TPC rows + selPosGoodITSTrack, // at least min # ITS clusters + selNegGoodITSTrack, // at least min # ITS clusters + selPosItsOnly, + selNegItsOnly, + selPosNotTPCOnly, + selNegNotTPCOnly, + selConsiderK0Short, // for mc tagging + selConsiderLambda, // for mc tagging + selConsiderAntiLambda, // for mc tagging + selPhysPrimK0Short, // for mc tagging + selPhysPrimLambda, // for mc tagging + selPhysPrimAntiLambda, // for mc tagging + }; + + uint64_t maskTopological; + uint64_t maskTopoNoV0Radius; + uint64_t maskTopoNoDCANegToPV; + uint64_t maskTopoNoDCAPosToPV; + uint64_t maskTopoNoCosPA; + uint64_t maskTopoNoDCAV0Dau; + uint64_t maskTrackProperties; + + uint64_t maskK0ShortSpecific; + uint64_t maskLambdaSpecific; + uint64_t maskAntiLambdaSpecific; + + uint64_t maskSelectionK0Short; + uint64_t maskSelectionLambda; + uint64_t maskSelectionAntiLambda; + + uint64_t secondaryMaskSelectionLambda; + uint64_t secondaryMaskSelectionAntiLambda; + + + void init(InitContext const&){ + // const AxisSpec axisCounter{1, 0, +1, ""}; + // // invMassAx = AxisSpec{nBinsInvMass, minInvMass, maxInvMass}; + + // histos.add("eventCounter", "eventCounter", kTH1F, {axisCounter}); + // // histos.add("LambdaInvMass1D", "Test LambdaInvMass 1D", kTH1F, {axisLambdaMass}); + histos.add("hMassLambda", "hMassLambda", kTH1D, {axisConfigurations.axisLambdaMass}); + histos.add("ptQAHist", "ptQAHist", kTH1F, {axisPtQA}); + + // From the analyseLambda flag: + histos.add("h2dNbrOfLambdaVsCentrality", "h2dNbrOfLambdaVsCentrality", kTH2D, {axisConfigurations.axisCentrality, {10, -0.5f, 9.5f}}); + histos.add("h3dMassLambda", "h3dMassLambda", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtQA, axisConfigurations.axisLambdaMass}); + // Non-UPC info + histos.add("h3dMassLambdaHadronic", "h3dMassLambdaHadronic", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisLambdaMass}); + // Not doing ultra-peripheral! + // // UPC info + // histos.add("h3dMassLambdaSGA", "h3dMassLambdaSGA", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisLambdaMass}); + // histos.add("h3dMassLambdaSGC", "h3dMassLambdaSGC", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisLambdaMass}); + // histos.add("h3dMassLambdaDG", "h3dMassLambdaDG", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisLambdaMass}); + + // // For the doCompleteTopoQA analysis: + // 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}); + + // For the doPlainTopoQA analysis: + // All candidates received + histos.add("hPosDCAToPV", "hPosDCAToPV", kTH1D, {axisConfigurations.axisDCAtoPV}); + histos.add("hNegDCAToPV", "hNegDCAToPV", kTH1D, {axisConfigurations.axisDCAtoPV}); + histos.add("hDCADaughters", "hDCADaughters", kTH1D, {axisConfigurations.axisDCAdau}); + histos.add("hPointingAngle", "hPointingAngle", kTH1D, {axisConfigurations.axisPointingAngle}); + histos.add("hV0Radius", "hV0Radius", kTH1D, {axisConfigurations.axisV0Radius}); + histos.add("h2dPositiveITSvsTPCpts", "h2dPositiveITSvsTPCpts", kTH2D, {axisConfigurations.axisTPCrows, axisConfigurations.axisITSclus}); + histos.add("h2dNegativeITSvsTPCpts", "h2dNegativeITSvsTPCpts", kTH2D, {axisConfigurations.axisTPCrows, axisConfigurations.axisITSclus}); + histos.add("h2dPositivePtVsPhi", "h2dPositivePtVsPhi", kTH2D, {axisConfigurations.axisPtCoarse, axisConfigurations.axisPhiMod}); + histos.add("h2dNegativePtVsPhi", "h2dNegativePtVsPhi", kTH2D, {axisConfigurations.axisPtCoarse, axisConfigurations.axisPhiMod}); + + // Specifically for Lambda: + 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}); + + // Check if doing the right thing in AP space please + histos.add("GeneralQA/h2dArmenterosAll", "h2dArmenterosAll", kTH2D, {axisConfigurations.axisAPAlpha, axisConfigurations.axisAPQt}); + histos.add("GeneralQA/h2dArmenterosSelected", "h2dArmenterosSelected", kTH2D, {axisConfigurations.axisAPAlpha, axisConfigurations.axisAPQt}); + } + + template + void initCCDB(TCollision collision) + { + if (mRunNumber == collision.runNumber()) { + return; + } + + mRunNumber = collision.runNumber(); + + // machine learning initialization if requested + if (mlConfigurations.calculateLambdaScores) { + int64_t timeStampML = collision.timestamp(); + if (mlConfigurations.timestampCCDB.value != -1) + timeStampML = mlConfigurations.timestampCCDB.value; + loadMachines(timeStampML); + } + // Fetching magnetic field if requested + if (v0Selections.rejectTPCsectorBoundary) { + // In case override, don't proceed, please - no CCDB access required + if (ccdbConfigurations.useCustomMagField) { + magField = ccdbConfigurations.customMagField; + } else { + grpmag = ccdb->getForRun(ccdbConfigurations.grpmagPath, mRunNumber); + if (!grpmag) { + LOG(fatal) << "Got nullptr from CCDB for path " << ccdbConfigurations.grpmagPath << " of object GRPMagField and " << ccdbConfigurations.grpPath << " of object GRPObject for run " << mRunNumber; + } + // Fetch magnetic field from ccdb for current collision + magField = std::lround(5.f * grpmag->getL3Current() / 30000.f); + LOG(info) << "Retrieved GRP for run " << mRunNumber << " with magnetic field of " << magField << " kZG"; + } + } + } + + template + // uint64_t computeReconstructionBitmap(TV0 v0, TCollision collision, float rapidityLambda, float rapidityK0Short, float /*pT*/) + uint64_t computeReconstructionBitmap(TV0 v0, TCollision collision, float rapidityLambda, float /*pT*/) + // precalculate this information so that a check is one mask operation, not many + { + uint64_t bitMap = 0; + // Base topological variables + if (v0.v0radius() > v0Selections.v0radius) + BITSET(bitMap, selRadius); + if (v0.v0radius() < v0Selections.v0radiusMax) + BITSET(bitMap, selRadiusMax); + if (std::abs(v0.dcapostopv()) > v0Selections.dcapostopv) + BITSET(bitMap, selDCAPosToPV); + if (std::abs(v0.dcanegtopv()) > v0Selections.dcanegtopv) + BITSET(bitMap, selDCANegToPV); + if (v0.v0cosPA() > v0Selections.v0cospa) + BITSET(bitMap, selCosPA); + if (v0.dcaV0daughters() < v0Selections.dcav0dau) + BITSET(bitMap, selDCAV0Dau); + + // rapidity + if (std::abs(rapidityLambda) < v0Selections.rapidityCut) + BITSET(bitMap, selLambdaRapidity); + + auto posTrackExtra = v0.template posTrackExtra_as(); + auto negTrackExtra = v0.template negTrackExtra_as(); + + // ITS quality flags + bool posIsFromAfterburner = posTrackExtra.hasITSAfterburner(); + bool negIsFromAfterburner = negTrackExtra.hasITSAfterburner(); + + // check minimum number of ITS clusters + maximum ITS chi2 per clusters + reject or select ITS afterburner tracks if requested + if (posTrackExtra.itsNCls() >= v0Selections.minITSclusters && // check minium ITS clusters + posTrackExtra.itsChi2NCl() < v0Selections.maxITSchi2PerNcls && // check maximum ITS chi2 per clusters + (!v0Selections.rejectPosITSafterburner || !posIsFromAfterburner) && // reject afterburner track or not + (!v0Selections.requirePosITSafterburnerOnly || posIsFromAfterburner)) // keep afterburner track or not + BITSET(bitMap, selPosGoodITSTrack); + if (negTrackExtra.itsNCls() >= v0Selections.minITSclusters && // check minium ITS clusters + negTrackExtra.itsChi2NCl() < v0Selections.maxITSchi2PerNcls && // check maximum ITS chi2 per clusters + (!v0Selections.rejectNegITSafterburner || !negIsFromAfterburner) && // reject afterburner track or not + (!v0Selections.requireNegITSafterburnerOnly || negIsFromAfterburner)) // select only afterburner track or not + BITSET(bitMap, selNegGoodITSTrack); + + // TPC quality flags + if (posTrackExtra.tpcCrossedRows() >= v0Selections.minTPCrows && // check minimum TPC crossed rows + posTrackExtra.tpcChi2NCl() < v0Selections.maxTPCchi2PerNcls && // check maximum TPC chi2 per clusters + posTrackExtra.tpcCrossedRowsOverFindableCls() >= v0Selections.minTPCrowsOverFindableClusters && // check minimum fraction of TPC rows over findable + posTrackExtra.tpcFoundOverFindableCls() >= v0Selections.minTPCfoundOverFindableClusters && // check minimum fraction of found over findable TPC clusters + posTrackExtra.tpcFractionSharedCls() < v0Selections.maxFractionTPCSharedClusters && // check the maximum fraction of allowed shared TPC clusters + (!v0Selections.rejectTPCsectorBoundary || isTrackFarFromTPCBoundary(v0.positivept(), v0.positivephi(), 1))) // reject track far from TPC sector boundary or not + BITSET(bitMap, selPosGoodTPCTrack); + if (negTrackExtra.tpcCrossedRows() >= v0Selections.minTPCrows && // check minimum TPC crossed rows + negTrackExtra.tpcChi2NCl() < v0Selections.maxTPCchi2PerNcls && // check maximum TPC chi2 per clusters + negTrackExtra.tpcCrossedRowsOverFindableCls() >= v0Selections.minTPCrowsOverFindableClusters && // check minimum fraction of TPC rows over findable + negTrackExtra.tpcFoundOverFindableCls() >= v0Selections.minTPCfoundOverFindableClusters && // check minimum fraction of found over findable TPC clusters + negTrackExtra.tpcFractionSharedCls() < v0Selections.maxFractionTPCSharedClusters && // check the maximum fraction of allowed shared TPC clusters + (!v0Selections.rejectTPCsectorBoundary || isTrackFarFromTPCBoundary(v0.negativept(), v0.negativephi(), -1))) // reject track far from TPC sector boundary or not + BITSET(bitMap, selNegGoodTPCTrack); + + // TPC PID + if (std::fabs(posTrackExtra.tpcNSigmaPi()) < v0Selections.tpcPidNsigmaCut) + BITSET(bitMap, selTPCPIDPositivePion); + if (std::fabs(posTrackExtra.tpcNSigmaPr()) < v0Selections.tpcPidNsigmaCut) + BITSET(bitMap, selTPCPIDPositiveProton); + if (std::fabs(negTrackExtra.tpcNSigmaPi()) < v0Selections.tpcPidNsigmaCut) + BITSET(bitMap, selTPCPIDNegativePion); + if (std::fabs(negTrackExtra.tpcNSigmaPr()) < v0Selections.tpcPidNsigmaCut) + BITSET(bitMap, selTPCPIDNegativeProton); + + // TOF PID in DeltaT + // Positive track + if (!posTrackExtra.hasTOF() || std::fabs(v0.posTOFDeltaTLaPr()) < v0Selections.maxDeltaTimeProton) + BITSET(bitMap, selTOFDeltaTPositiveProtonLambda); + if (!posTrackExtra.hasTOF() || std::fabs(v0.posTOFDeltaTLaPi()) < v0Selections.maxDeltaTimePion) + BITSET(bitMap, selTOFDeltaTPositivePionLambda); + // Negative track + if (!negTrackExtra.hasTOF() || std::fabs(v0.negTOFDeltaTLaPr()) < v0Selections.maxDeltaTimeProton) + BITSET(bitMap, selTOFDeltaTNegativeProtonLambda); + if (!negTrackExtra.hasTOF() || std::fabs(v0.negTOFDeltaTLaPi()) < v0Selections.maxDeltaTimePion) + BITSET(bitMap, selTOFDeltaTNegativePionLambda); + + // TOF PID in NSigma + // Positive track + if (!posTrackExtra.hasTOF() || std::fabs(v0.tofNSigmaLaPr()) < v0Selections.tofPidNsigmaCutLaPr) + BITSET(bitMap, selTOFNSigmaPositiveProtonLambda); + if (!posTrackExtra.hasTOF() || std::fabs(v0.tofNSigmaALaPi()) < v0Selections.tofPidNsigmaCutLaPi) + BITSET(bitMap, selTOFNSigmaPositivePionLambda); + // Negative track + if (!negTrackExtra.hasTOF() || std::fabs(v0.tofNSigmaALaPr()) < v0Selections.tofPidNsigmaCutLaPr) + BITSET(bitMap, selTOFNSigmaNegativeProtonLambda); + if (!negTrackExtra.hasTOF() || std::fabs(v0.tofNSigmaLaPi()) < v0Selections.tofPidNsigmaCutLaPi) + BITSET(bitMap, selTOFNSigmaNegativePionLambda); + + // ITS only tag + if (posTrackExtra.tpcCrossedRows() < 1) + BITSET(bitMap, selPosItsOnly); + if (negTrackExtra.tpcCrossedRows() < 1) + BITSET(bitMap, selNegItsOnly); + + // TPC only tag + if (posTrackExtra.detectorMap() != o2::aod::track::TPC) + BITSET(bitMap, selPosNotTPCOnly); + if (negTrackExtra.detectorMap() != o2::aod::track::TPC) + BITSET(bitMap, selNegNotTPCOnly); + + // proper lifetime + if (v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * o2::constants::physics::MassLambda0 < v0Selections.lifetimecut->get("lifetimecutLambda")) + BITSET(bitMap, selLambdaCTau); + + return bitMap; + } + + // function to load models for ML-based classifiers + void loadMachines(int64_t timeStampML) + { + if (mlConfigurations.loadCustomModelsFromCCDB) { + ccdbApi.init(ccdbConfigurations.ccdbUrl); + LOG(info) << "Fetching models for timestamp: " << timeStampML; + + if (mlConfigurations.calculateLambdaScores) { + bool retrieveSuccessLambda = ccdbApi.retrieveBlob(mlConfigurations.customModelPathCCDB, ".", metadata, timeStampML, false, mlConfigurations.localModelPathLambda.value); + if (retrieveSuccessLambda) { + mlCustomModelLambda.initModel(mlConfigurations.localModelPathLambda.value, mlConfigurations.enableOptimizations.value); + } else { + LOG(fatal) << "Error encountered while fetching/loading the Lambda model from CCDB! Maybe the model doesn't exist yet for this runnumber/timestamp?"; + } + } + } + if (mlConfigurations.calculateLambdaScores) + mlCustomModelLambda.initModel(mlConfigurations.localModelPathLambda.value, mlConfigurations.enableOptimizations.value); + LOG(info) << "ML Models loaded."; + } + + template + // void analyseCandidate(TV0 v0, float pt, float centrality, uint64_t selMap, uint8_t gapSide, int& nK0Shorts, int& nLambdas, int& nAntiLambdas) + void analyseCandidate(TV0 v0, float pt, float centrality, uint64_t selMap, uint8_t gapSide, int& nLambdas) + // precalculate this information so that a check is one mask operation, not many + { + bool passLambdaSelections = false; + + // machine learning is on, go for calculation of thresholds + // FIXME THIS NEEDS ADJUSTING + std::vector inputFeatures{pt, 0.0f, 0.0f, v0.v0radius(), v0.v0cosPA(), v0.dcaV0daughters(), v0.dcapostopv(), v0.dcanegtopv()}; + + if (mlConfigurations.useLambdaScores) { + float lambdaScore = -1; + if (mlConfigurations.calculateLambdaScores) { + // evaluate machine-learning scores + float* lambdaProbability = mlCustomModelLambda.evalModel(inputFeatures); + lambdaScore = lambdaProbability[1]; + } else { + lambdaScore = v0.lambdaBDTScore(); + } + if (lambdaScore > mlConfigurations.thresholdK0Short.value) { + passLambdaSelections = true; + } + } else { + passLambdaSelections = verifyMask(selMap, maskSelectionLambda); + } + + auto posTrackExtra = v0.template posTrackExtra_as(); + auto negTrackExtra = v0.template negTrackExtra_as(); + + bool posIsFromAfterburner = posTrackExtra.itsChi2PerNcl() < 0; + bool negIsFromAfterburner = negTrackExtra.itsChi2PerNcl() < 0; + + uint posDetMap = computeDetBitmap(posTrackExtra.detectorMap()); + int posITSclusMap = computeITSclusBitmap(posTrackExtra.itsClusterMap(), posIsFromAfterburner); + uint negDetMap = computeDetBitmap(negTrackExtra.detectorMap()); + int negITSclusMap = computeITSclusBitmap(negTrackExtra.itsClusterMap(), negIsFromAfterburner); + + // __________________________________________ + // fill with no selection if plain QA requested + if (doPlainTopoQA) { + histos.fill(HIST("hPosDCAToPV"), v0.dcapostopv()); + histos.fill(HIST("hNegDCAToPV"), v0.dcanegtopv()); + histos.fill(HIST("hDCADaughters"), v0.dcaV0daughters()); + histos.fill(HIST("hPointingAngle"), std::acos(v0.v0cosPA())); + histos.fill(HIST("hV0Radius"), v0.v0radius()); + histos.fill(HIST("h2dPositiveITSvsTPCpts"), posTrackExtra.tpcCrossedRows(), posTrackExtra.itsNCls()); + histos.fill(HIST("h2dNegativeITSvsTPCpts"), negTrackExtra.tpcCrossedRows(), negTrackExtra.itsNCls()); + histos.fill(HIST("h2dPositivePtVsPhi"), v0.positivept(), computePhiMod(v0.positivephi(), 1)); + histos.fill(HIST("h2dNegativePtVsPhi"), v0.negativept(), computePhiMod(v0.negativephi(), -1)); + } + + // Fill first bin: all candidates + histos.fill(HIST("GeneralQA/hSelectionV0s"), 0); + // Loop over all bits in the enum and fill if passed + for (uint64_t i = 0; i <= selPhysPrimAntiLambda; i++) { + if (BITCHECK(selMap, i)) { + histos.fill(HIST("GeneralQA/hSelectionV0s"), i + 1); // +1 because bin 0 = "All" + } + } + + // __________________________________________ + // main analysis + if (passLambdaSelections && analyseLambda) { + histos.fill(HIST("GeneralQA/hSelectionV0s"), selPhysPrimAntiLambda + 2); // + histos.fill(HIST("h3dMassLambda"), centrality, pt, v0.mLambda()); + if (gapSide == 0) + histos.fill(HIST("h3dMassLambdaSGA"), centrality, pt, v0.mLambda()); + else if (gapSide == 1) + histos.fill(HIST("h3dMassLambdaSGC"), centrality, pt, v0.mLambda()); + else if (gapSide == 2) + histos.fill(HIST("h3dMassLambdaDG"), centrality, pt, v0.mLambda()); + else + histos.fill(HIST("h3dMassLambdaHadronic"), centrality, pt, v0.mLambda()); + histos.fill(HIST("hMassLambda"), v0.mLambda()); + if (doPlainTopoQA) { + histos.fill(HIST("Lambda/hPosDCAToPV"), v0.dcapostopv()); + histos.fill(HIST("Lambda/hNegDCAToPV"), v0.dcanegtopv()); + histos.fill(HIST("Lambda/hDCADaughters"), v0.dcaV0daughters()); + histos.fill(HIST("Lambda/hPointingAngle"), std::acos(v0.v0cosPA())); + histos.fill(HIST("Lambda/hV0Radius"), v0.v0radius()); + histos.fill(HIST("Lambda/h2dPositiveITSvsTPCpts"), posTrackExtra.tpcCrossedRows(), posTrackExtra.itsNCls()); + histos.fill(HIST("Lambda/h2dNegativeITSvsTPCpts"), negTrackExtra.tpcCrossedRows(), negTrackExtra.itsNCls()); + histos.fill(HIST("Lambda/h2dPositivePtVsPhi"), v0.positivept(), computePhiMod(v0.positivephi(), 1)); + histos.fill(HIST("Lambda/h2dNegativePtVsPhi"), v0.negativept(), computePhiMod(v0.negativephi(), -1)); + } + if (doDetectPropQA == 1) { + histos.fill(HIST("Lambda/h6dDetectPropVsCentrality"), centrality, posDetMap, posITSclusMap, negDetMap, negITSclusMap, pt); + histos.fill(HIST("Lambda/h4dPosDetectPropVsCentrality"), centrality, posTrackExtra.detectorMap(), posTrackExtra.itsClusterMap(), pt); + histos.fill(HIST("Lambda/h4dNegDetectPropVsCentrality"), centrality, negTrackExtra.detectorMap(), negTrackExtra.itsClusterMap(), pt); + } + if (doDetectPropQA == 2) { + histos.fill(HIST("Lambda/h7dDetectPropVsCentrality"), centrality, posDetMap, posITSclusMap, negDetMap, negITSclusMap, pt, v0.mLambda()); + histos.fill(HIST("Lambda/h5dPosDetectPropVsCentrality"), centrality, posTrackExtra.detectorMap(), posTrackExtra.itsClusterMap(), pt, v0.mLambda()); + histos.fill(HIST("Lambda/h5dNegDetectPropVsCentrality"), centrality, negTrackExtra.detectorMap(), negTrackExtra.itsClusterMap(), pt, v0.mLambda()); + } + if (doDetectPropQA == 3) { + histos.fill(HIST("Lambda/h3dITSchi2"), centrality, pt, std::max(posTrackExtra.itsChi2NCl(), negTrackExtra.itsChi2NCl())); + histos.fill(HIST("Lambda/h3dTPCchi2"), centrality, pt, std::max(posTrackExtra.tpcChi2NCl(), negTrackExtra.tpcChi2NCl())); + histos.fill(HIST("Lambda/h3dTPCFoundOverFindable"), centrality, pt, std::min(posTrackExtra.tpcFoundOverFindableCls(), negTrackExtra.tpcFoundOverFindableCls())); + histos.fill(HIST("Lambda/h3dTPCrowsOverFindable"), centrality, pt, std::min(posTrackExtra.tpcCrossedRowsOverFindableCls(), negTrackExtra.tpcCrossedRowsOverFindableCls())); + histos.fill(HIST("Lambda/h3dTPCsharedCls"), centrality, pt, std::max(posTrackExtra.tpcFractionSharedCls(), negTrackExtra.tpcFractionSharedCls())); + histos.fill(HIST("Lambda/h3dPositiveITSchi2"), centrality, pt, posTrackExtra.itsChi2NCl()); + histos.fill(HIST("Lambda/h3dNegativeITSchi2"), centrality, pt, negTrackExtra.itsChi2NCl()); + histos.fill(HIST("Lambda/h3dPositiveTPCchi2"), centrality, pt, posTrackExtra.tpcChi2NCl()); + histos.fill(HIST("Lambda/h3dNegativeTPCchi2"), centrality, pt, negTrackExtra.tpcChi2NCl()); + histos.fill(HIST("Lambda/h3dPositiveITSclusters"), centrality, pt, posTrackExtra.itsNCls()); + histos.fill(HIST("Lambda/h3dNegativeITSclusters"), centrality, pt, negTrackExtra.itsNCls()); + histos.fill(HIST("Lambda/h3dPositiveTPCcrossedRows"), centrality, pt, posTrackExtra.tpcCrossedRows()); + histos.fill(HIST("Lambda/h3dNegativeTPCcrossedRows"), centrality, pt, negTrackExtra.tpcCrossedRows()); + } + if (doTPCQA) { + histos.fill(HIST("Lambda/h3dPosNsigmaTPC"), centrality, pt, posTrackExtra.tpcNSigmaPr()); + histos.fill(HIST("Lambda/h3dNegNsigmaTPC"), centrality, pt, negTrackExtra.tpcNSigmaPi()); + histos.fill(HIST("Lambda/h3dPosTPCsignal"), centrality, pt, posTrackExtra.tpcSignal()); + histos.fill(HIST("Lambda/h3dNegTPCsignal"), centrality, pt, negTrackExtra.tpcSignal()); + histos.fill(HIST("Lambda/h3dPosNsigmaTPCvsTrackPtot"), centrality, v0.pfracpos() * v0.p(), posTrackExtra.tpcNSigmaPr()); + histos.fill(HIST("Lambda/h3dNegNsigmaTPCvsTrackPtot"), centrality, v0.pfracneg() * v0.p(), negTrackExtra.tpcNSigmaPi()); + histos.fill(HIST("Lambda/h3dPosTPCsignalVsTrackPtot"), centrality, v0.pfracpos() * v0.p(), posTrackExtra.tpcSignal()); + histos.fill(HIST("Lambda/h3dNegTPCsignalVsTrackPtot"), centrality, v0.pfracneg() * v0.p(), negTrackExtra.tpcSignal()); + histos.fill(HIST("Lambda/h3dPosNsigmaTPCvsTrackPt"), centrality, v0.positivept(), posTrackExtra.tpcNSigmaPr()); + histos.fill(HIST("Lambda/h3dNegNsigmaTPCvsTrackPt"), centrality, v0.negativept(), negTrackExtra.tpcNSigmaPi()); + histos.fill(HIST("Lambda/h3dPosTPCsignalVsTrackPt"), centrality, v0.positivept(), posTrackExtra.tpcSignal()); + histos.fill(HIST("Lambda/h3dNegTPCsignalVsTrackPt"), centrality, v0.negativept(), negTrackExtra.tpcSignal()); + } + if (doTOFQA) { + histos.fill(HIST("Lambda/h3dPosNsigmaTOF"), centrality, pt, v0.tofNSigmaLaPr()); + histos.fill(HIST("Lambda/h3dNegNsigmaTOF"), centrality, pt, v0.tofNSigmaLaPi()); + histos.fill(HIST("Lambda/h3dPosTOFdeltaT"), centrality, pt, v0.posTOFDeltaTLaPr()); + histos.fill(HIST("Lambda/h3dNegTOFdeltaT"), centrality, pt, v0.negTOFDeltaTLaPi()); + histos.fill(HIST("Lambda/h3dPosNsigmaTOFvsTrackPtot"), centrality, v0.pfracpos() * v0.p(), v0.tofNSigmaLaPr()); + histos.fill(HIST("Lambda/h3dNegNsigmaTOFvsTrackPtot"), centrality, v0.pfracneg() * v0.p(), v0.tofNSigmaLaPi()); + histos.fill(HIST("Lambda/h3dPosTOFdeltaTvsTrackPtot"), centrality, v0.pfracpos() * v0.p(), v0.posTOFDeltaTLaPr()); + histos.fill(HIST("Lambda/h3dNegTOFdeltaTvsTrackPtot"), centrality, v0.pfracneg() * v0.p(), v0.negTOFDeltaTLaPi()); + histos.fill(HIST("Lambda/h3dPosNsigmaTOFvsTrackPt"), centrality, v0.positivept(), v0.tofNSigmaLaPr()); + histos.fill(HIST("Lambda/h3dNegNsigmaTOFvsTrackPt"), centrality, v0.negativept(), v0.tofNSigmaLaPi()); + histos.fill(HIST("Lambda/h3dPosTOFdeltaTvsTrackPt"), centrality, v0.positivept(), v0.posTOFDeltaTLaPr()); + histos.fill(HIST("Lambda/h3dNegTOFdeltaTvsTrackPt"), centrality, v0.negativept(), v0.negTOFDeltaTLaPi()); + } + if (doEtaPhiQA) { + histos.fill(HIST("Lambda/h5dV0PhiVsEta"), centrality, pt, 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()); + } + nLambdas++; + } + + // // __________________________________________ + // // do systematics / qa plots + // if (doCompleteTopoQA) { + // if (analyseLambda) { + // if (verifyMask(selMap, maskTopoNoV0Radius | maskLambdaSpecific)) + // histos.fill(HIST("Lambda/h4dV0Radius"), centrality, pt, v0.mLambda(), v0.v0radius()); + // if (verifyMask(selMap, maskTopoNoDCAPosToPV | maskLambdaSpecific)) + // histos.fill(HIST("Lambda/h4dPosDCAToPV"), centrality, pt, v0.mLambda(), std::abs(v0.dcapostopv())); + // if (verifyMask(selMap, maskTopoNoDCANegToPV | maskLambdaSpecific)) + // histos.fill(HIST("Lambda/h4dNegDCAToPV"), centrality, pt, v0.mLambda(), std::abs(v0.dcanegtopv())); + // if (verifyMask(selMap, maskTopoNoCosPA | maskLambdaSpecific)) + // histos.fill(HIST("Lambda/h4dPointingAngle"), centrality, pt, v0.mLambda(), std::acos(v0.v0cosPA())); + // if (verifyMask(selMap, maskTopoNoDCAV0Dau | maskLambdaSpecific)) + // histos.fill(HIST("Lambda/h4dDCADaughters"), centrality, pt, v0.mLambda(), v0.dcaV0daughters()); + // } // end systematics / qa + // } + } + + // ______________________________________________________ + // Return slicing output + template + auto getCentralityRun3(TCollision const& collision) + { + if (centralityEstimator == kCentFT0C) + return collision.centFT0C(); + else if (centralityEstimator == kCentFT0M) + return collision.centFT0M(); + else if (centralityEstimator == kCentFT0CVariant1) + return collision.centFT0CVariant1(); + else if (centralityEstimator == kCentMFT) + return collision.centMFT(); + else if (centralityEstimator == kCentNGlobal) + return collision.centNGlobal(); + else if (centralityEstimator == kCentFV0A) + return collision.centFV0A(); + + return -1.f; + } + + + // ______________________________________________________ + // Reconstructed data processing + // Fill reconstructed event information + // Return centrality, occupancy, interaction rate, gap side and selGapside via reference-passing in arguments + template + void fillReconstructedEventProperties(TCollision const& collision, float& centrality, float& collisionOccupancy, double& interactionRate, int& gapSide, int& selGapSide) + { + if constexpr (requires { collision.centFT0C(); }) { // check if we are in Run 3 + centrality = getCentralityRun3(collision); + collisionOccupancy = eventSelections.useFT0CbasedOccupancy ? collision.ft0cOccupancyInTimeRange() : collision.trackOccupancyInTimeRange(); + // Fetch interaction rate only if required (in order to limit ccdb calls) + interactionRate = !irSource.value.empty() ? rateFetcher.fetch(ccdb.service, collision.timestamp(), collision.runNumber(), irSource) * 1.e-3 : -1; + + if (qaCentrality) { + auto hRawCentrality = histos.get(HIST("hRawCentrality")); + centrality = hRawCentrality->GetBinContent(hRawCentrality->FindBin(doPPAnalysis ? collision.multFT0A() + collision.multFT0C() : collision.multFT0C())); + } + + // gap side + gapSide = collision.gapSide(); + // -1 --> Hadronic + // 0 --> Single Gap - A side + // 1 --> Single Gap - C side + // 2 --> Double Gap - both A & C sides + selGapSide = sgSelector.trueGap(collision, upcCuts.fv0Cut, upcCuts.ft0Acut, upcCuts.ft0Ccut, upcCuts.zdcCut); + } else { // no, we are in Run 2 + centrality = eventSelections.useSPDTrackletsCent ? collision.centRun2SPDTracklets() : collision.centRun2V0M(); + } + + histos.fill(HIST("hGapSide"), gapSide); + histos.fill(HIST("hSelGapSide"), selGapSide); + histos.fill(HIST("hEventCentralityVsSelGapSide"), centrality, selGapSide <= 2 ? selGapSide : -1); + + histos.fill(HIST("hEventCentrality"), centrality); + + histos.fill(HIST("hCentralityVsNch"), centrality, collision.multNTracksPVeta1()); + if (doEventQA) { + if constexpr (requires { collision.centFT0C(); }) { // check if we are in Run 3 + histos.fill(HIST("hCentralityVsNGlobal"), centrality, collision.multNTracksGlobal()); + histos.fill(HIST("hEventCentVsMultFT0M"), collision.centFT0M(), collision.multFT0A() + collision.multFT0C()); + histos.fill(HIST("hEventCentVsMultFT0C"), collision.centFT0C(), collision.multFT0C()); + histos.fill(HIST("hEventCentVsMultNGlobal"), collision.centNGlobal(), collision.multNTracksGlobal()); + histos.fill(HIST("hEventCentVsMultFV0A"), collision.centFV0A(), collision.multFV0A()); + histos.fill(HIST("hEventMultFT0MvsMultNGlobal"), collision.multFT0A() + collision.multFT0C(), collision.multNTracksGlobal()); + histos.fill(HIST("hEventMultFT0CvsMultNGlobal"), collision.multFT0C(), collision.multNTracksGlobal()); + histos.fill(HIST("hEventMultFV0AvsMultNGlobal"), collision.multFV0A(), collision.multNTracksGlobal()); + histos.fill(HIST("hEventMultPVvsMultNGlobal"), collision.multNTracksPVeta1(), collision.multNTracksGlobal()); + histos.fill(HIST("hEventMultFT0CvsMultFV0A"), collision.multFT0C(), collision.multFV0A()); + } + } + + histos.fill(HIST("hCentralityVsPVz"), centrality, collision.posZ()); + histos.fill(HIST("hEventPVz"), collision.posZ()); + + histos.fill(HIST("hEventOccupancy"), collisionOccupancy); + histos.fill(HIST("hCentralityVsOccupancy"), centrality, collisionOccupancy); + + histos.fill(HIST("hInteractionRate"), interactionRate); + histos.fill(HIST("hCentralityVsInteractionRate"), centrality, interactionRate); + + histos.fill(HIST("hInteractionRateVsOccupancy"), interactionRate, collisionOccupancy); + return; + } + + template + bool isEventAccepted(TCollision collision, bool fillHists) + // check whether the collision passes our collision selections + { + float centrality = -1.0f; + if (fillHists) { + histos.fill(HIST("hEventSelection"), 0. /* all collisions */); + if (doEventQA) { + if constexpr (requires { collision.centFT0C(); }) { // check if we are in Run 3 + centrality = getCentralityRun3(collision); + } + histos.fill(HIST("hEventSelectionVsCentrality"), 0. /* all collisions */, centrality); + } + } + + if constexpr (requires { collision.centFT0C(); }) { // check if we are in Run 3 + if (eventSelections.requireSel8 && !collision.sel8()) { + return false; + } + if (fillHists) { + histos.fill(HIST("hEventSelection"), 1 /* sel8 collisions */); + if (doEventQA) { + histos.fill(HIST("hEventSelectionVsCentrality"), 1 /* sel8 collisions */, centrality); + } + } + + if (eventSelections.requireTriggerTVX && !collision.selection_bit(aod::evsel::kIsTriggerTVX)) { + return false; + } + if (fillHists) { + histos.fill(HIST("hEventSelection"), 2 /* FT0 vertex (acceptable FT0C-FT0A time difference) collisions */); + if (doEventQA) { + histos.fill(HIST("hEventSelectionVsCentrality"), 2 /* FT0 vertex (acceptable FT0C-FT0A time difference) collisions */, centrality); + } + } + + if (eventSelections.rejectITSROFBorder && !collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { + return false; + } + if (fillHists) { + histos.fill(HIST("hEventSelection"), 3 /* Not at ITS ROF border */); + if (doEventQA) { + histos.fill(HIST("hEventSelectionVsCentrality"), 3 /* Not at ITS ROF border */, centrality); + } + } + + if (eventSelections.rejectTFBorder && !collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { + return false; + } + if (fillHists) { + histos.fill(HIST("hEventSelection"), 4 /* Not at TF border */); + if (doEventQA) { + histos.fill(HIST("hEventSelectionVsCentrality"), 4 /* Not at TF border */, centrality); + } + } + + if (std::abs(collision.posZ()) > eventSelections.maxZVtxPosition) { + return false; + } + if (fillHists) { + histos.fill(HIST("hEventSelection"), 5 /* vertex-Z selected */); + if (doEventQA) { + histos.fill(HIST("hEventSelectionVsCentrality"), 5 /* vertex-Z selected */, centrality); + } + } + + if (eventSelections.requireIsVertexITSTPC && !collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) { + return false; + } + if (fillHists) { + histos.fill(HIST("hEventSelection"), 6 /* Contains at least one ITS-TPC track */); + if (doEventQA) { + histos.fill(HIST("hEventSelectionVsCentrality"), 6 /* Contains at least one ITS-TPC track */, centrality); + } + } + + if (eventSelections.requireIsGoodZvtxFT0VsPV && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + return false; + } + if (fillHists) { + histos.fill(HIST("hEventSelection"), 7 /* PV position consistency check */); + if (doEventQA) { + histos.fill(HIST("hEventSelectionVsCentrality"), 7 /* PV position consistency check */, centrality); + } + } + + if (eventSelections.requireIsVertexTOFmatched && !collision.selection_bit(o2::aod::evsel::kIsVertexTOFmatched)) { + return false; + } + if (fillHists) { + histos.fill(HIST("hEventSelection"), 8 /* PV with at least one contributor matched with TOF */); + if (doEventQA) { + histos.fill(HIST("hEventSelectionVsCentrality"), 8 /* PV with at least one contributor matched with TOF */, centrality); + } + } + + if (eventSelections.requireIsVertexTRDmatched && !collision.selection_bit(o2::aod::evsel::kIsVertexTRDmatched)) { + return false; + } + if (fillHists) { + histos.fill(HIST("hEventSelection"), 9 /* PV with at least one contributor matched with TRD */); + if (doEventQA) { + histos.fill(HIST("hEventSelectionVsCentrality"), 9 /* PV with at least one contributor matched with TRD */, centrality); + } + } + + if (eventSelections.rejectSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + return false; + } + if (fillHists) { + histos.fill(HIST("hEventSelection"), 10 /* Not at same bunch pile-up */); + if (doEventQA) { + histos.fill(HIST("hEventSelectionVsCentrality"), 10 /* Not at same bunch pile-up */, centrality); + } + } + + if (eventSelections.requireNoCollInTimeRangeStd && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { + return false; + } + if (fillHists) { + histos.fill(HIST("hEventSelection"), 11 /* No other collision within +/- 2 microseconds or mult above a certain threshold in -4 - -2 microseconds*/); + if (doEventQA) { + histos.fill(HIST("hEventSelectionVsCentrality"), 11 /* No other collision within +/- 2 microseconds or mult above a certain threshold in -4 - -2 microseconds*/, centrality); + } + } + + if (eventSelections.requireNoCollInTimeRangeStrict && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStrict)) { + return false; + } + if (fillHists) { + histos.fill(HIST("hEventSelection"), 12 /* No other collision within +/- 10 microseconds */); + if (doEventQA) { + histos.fill(HIST("hEventSelectionVsCentrality"), 12 /* No other collision within +/- 10 microseconds */, centrality); + } + } + + if (eventSelections.requireNoCollInTimeRangeNarrow && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeNarrow)) { + return false; + } + if (fillHists) { + histos.fill(HIST("hEventSelection"), 13 /* No other collision within +/- 2 microseconds */); + if (doEventQA) { + histos.fill(HIST("hEventSelectionVsCentrality"), 13 /* No other collision within +/- 2 microseconds */, centrality); + } + } + + if (eventSelections.requireNoCollInROFStd && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { + return false; + } + if (fillHists) { + histos.fill(HIST("hEventSelection"), 14 /* No other collision within the same ITS ROF with mult. above a certain threshold */); + if (doEventQA) { + histos.fill(HIST("hEventSelectionVsCentrality"), 14 /* No other collision within the same ITS ROF with mult. above a certain threshold */, centrality); + } + } + + if (eventSelections.requireNoCollInROFStrict && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStrict)) { + return false; + } + if (fillHists) { + histos.fill(HIST("hEventSelection"), 15 /* No other collision within the same ITS ROF */); + if (doEventQA) { + histos.fill(HIST("hEventSelectionVsCentrality"), 15 /* No other collision within the same ITS ROF */, centrality); + } + } + + if (doPPAnalysis) { // we are in pp + if (eventSelections.requireINEL0 && collision.multNTracksPVeta1() < 1) { + return false; + } + if (fillHists) { + histos.fill(HIST("hEventSelection"), 16 /* INEL > 0 */); + if (doEventQA) { + histos.fill(HIST("hEventSelectionVsCentrality"), 16 /* INEL > 0 */, centrality); + } + } + + if (eventSelections.requireINEL1 && collision.multNTracksPVeta1() < 2) { + return false; + } + if (fillHists) { + histos.fill(HIST("hEventSelection"), 17 /* INEL > 1 */); + if (doEventQA) { + histos.fill(HIST("hEventSelectionVsCentrality"), 17 /* INEL > 1 */, centrality); + } + } + + } else { // we are in Pb-Pb + float collisionOccupancy = eventSelections.useFT0CbasedOccupancy ? collision.ft0cOccupancyInTimeRange() : collision.trackOccupancyInTimeRange(); + if (eventSelections.minOccupancy >= 0 && collisionOccupancy < eventSelections.minOccupancy) { + return false; + } + if (fillHists) { + histos.fill(HIST("hEventSelection"), 16 /* Below min occupancy */); + if (doEventQA) { + histos.fill(HIST("hEventSelectionVsCentrality"), 16 /* Below min occupancy */, centrality); + } + } + + if (eventSelections.maxOccupancy >= 0 && collisionOccupancy > eventSelections.maxOccupancy) { + return false; + } + if (fillHists) { + histos.fill(HIST("hEventSelection"), 17 /* Above max occupancy */); + if (doEventQA) { + histos.fill(HIST("hEventSelectionVsCentrality"), 17 /* Above max occupancy */, centrality); + } + } + } + + // Fetch interaction rate only if required (in order to limit ccdb calls) + double interactionRate = (eventSelections.minIR >= 0 || eventSelections.maxIR >= 0) ? rateFetcher.fetch(ccdb.service, collision.timestamp(), collision.runNumber(), irSource) * 1.e-3 : -1; + if (eventSelections.minIR >= 0 && interactionRate < eventSelections.minIR) { + return false; + } + if (fillHists) { + histos.fill(HIST("hEventSelection"), 18 /* Below min IR */); + if (doEventQA) { + histos.fill(HIST("hEventSelectionVsCentrality"), 18 /* Below min IR */, centrality); + } + } + + if (eventSelections.maxIR >= 0 && interactionRate > eventSelections.maxIR) { + return false; + } + if (fillHists) { + histos.fill(HIST("hEventSelection"), 19 /* Above max IR */); + if (doEventQA) { + histos.fill(HIST("hEventSelectionVsCentrality"), 19 /* Above max IR */, centrality); + } + } + + if (!rctConfigurations.cfgRCTLabel.value.empty() && !rctFlagsChecker(collision)) { + return false; + } + if (fillHists) { + histos.fill(HIST("hEventSelection"), 20 /* Pass CBT condition */); + if (doEventQA) { + histos.fill(HIST("hEventSelectionVsCentrality"), 20 /* Pass CBT condition */, centrality); + } + } + + } else { // we are in Run 2 + if (eventSelections.requireSel8 && !collision.sel8()) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 1 /* sel8 collisions */); + + if (eventSelections.requireSel7 && !collision.sel7()) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 2 /* sel7 collisions */); + + if (eventSelections.requireINT7 && !collision.alias_bit(kINT7)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 3 /* INT7-triggered collisions */); + + if (eventSelections.requireTriggerTVX && !collision.selection_bit(o2::aod::evsel::kIsTriggerTVX)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 4 /* FT0 vertex (acceptable FT0C-FT0A time difference) at trigger level */); + + if (eventSelections.rejectIncompleteDAQ && !collision.selection_bit(o2::aod::evsel::kNoIncompleteDAQ)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 5 /* Complete events according to DAQ flags */); + + if (std::abs(collision.posZ()) > eventSelections.maxZVtxPosition) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 6 /* vertex-Z selected */); + + if (eventSelections.requireConsistentSPDAndTrackVtx && !collision.selection_bit(o2::aod::evsel::kNoInconsistentVtx)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 7 /* No inconsistency in SPD and Track vertices */); + + if (eventSelections.rejectPileupFromSPD && !collision.selection_bit(o2::aod::evsel::kNoPileupFromSPD)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 8 /* No pileup according to SPD vertexer */); + + if (eventSelections.rejectV0PFPileup && !collision.selection_bit(o2::aod::evsel::kNoV0PFPileup)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 9 /* No out-of-bunch pileup according to V0 past-future info */); + + if (eventSelections.rejectPileupInMultBins && !collision.selection_bit(o2::aod::evsel::kNoPileupInMultBins)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 10 /* No pileup according to multiplicity-differential pileup checks */); + + if (eventSelections.rejectPileupMV && !collision.selection_bit(o2::aod::evsel::kNoPileupMV)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 11 /* No pileup according to multi-vertexer */); + + if (eventSelections.rejectTPCPileup && !collision.selection_bit(o2::aod::evsel::kNoPileupTPC)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 12 /* No pileup in TPC */); + + if (eventSelections.requireNoV0MOnVsOffPileup && !collision.selection_bit(o2::aod::evsel::kNoV0MOnVsOfPileup)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 13 /* No out-of-bunch pileup according to online-vs-offline VOM correlation */); + + if (eventSelections.requireNoSPDOnVsOffPileup && !collision.selection_bit(o2::aod::evsel::kNoSPDOnVsOfPileup)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 14 /* No out-of-bunch pileup according to online-vs-offline SPD correlation */); + + if (eventSelections.requireNoSPDClsVsTklBG && !collision.selection_bit(o2::aod::evsel::kNoSPDClsVsTklBG)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 15 /* No beam-gas according to cluster-vs-tracklet correlation */); + + if (doPPAnalysis) { // we are in pp + if (eventSelections.requireINEL0 && collision.multNTracksPVeta1() < 1) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 16 /* INEL > 0 */); + + if (eventSelections.requireINEL1 && collision.multNTracksPVeta1() < 2) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 17 /* INEL > 1 */); + } + } + + return true; + } + + // ______________________________________________________ + // Real data processing - no MC subscription + template + void analyzeRecoedV0sInRealData(TCollision const& collision, TV0s const& fullV0s) + { + // Fire up CCDB + if ((mlConfigurations.useLambdaScores && mlConfigurations.calculateLambdaScores) || + v0Selections.rejectTPCsectorBoundary) { + initCCDB(collision); + } + + if (!isEventAccepted(collision, true)) { + return; + } + + float centrality = -1; + float collisionOccupancy = -2; // -1 already taken for the case where occupancy cannot be evaluated + double interactionRate = -1; + // gap side + int gapSide = -1; + int selGapSide = -1; // -1 --> Hadronic ; 0 --> Single Gap - A side ; 1 --> Single Gap - C side ; 2 --> Double Gap - both A & C sides + // Fill recoed event properties + fillReconstructedEventProperties(collision, centrality, collisionOccupancy, interactionRate, gapSide, selGapSide); + + histos.fill(HIST("hInteractionRateVsOccupancy"), interactionRate, collisionOccupancy); + + // __________________________________________ + // perform main analysis + int nLambdas = 0; + for (auto const& v0 : fullV0s) { + if (std::abs(v0.negativeeta()) > v0Selections.daughterEtaCut || std::abs(v0.positiveeta()) > v0Selections.daughterEtaCut) + continue; // remove acceptance that's badly reproduced by MC / superfluous in future + + if (v0.v0Type() != v0Selections.v0TypeSelection && v0Selections.v0TypeSelection > -1) + continue; // skip V0s that are not standard + + // fill AP plot for all V0s + histos.fill(HIST("GeneralQA/h2dArmenterosAll"), v0.alpha(), v0.qtarm()); + + // uint64_t selMap = computeReconstructionBitmap(v0, collision, v0.yLambda(), v0.yK0Short(), v0.pt()); + uint64_t selMap = computeReconstructionBitmap(v0, collision, v0.yLambda(), v0.pt()); // Removed unneeded K0Short info + + // consider for histograms for all species + BITSET(selMap, selConsiderLambda); + BITSET(selMap, selPhysPrimLambda); + + // analyseCandidate(v0, v0.pt(), centrality, selMap, selGapSide, nK0Shorts, nLambdas, nAntiLambdas); + analyseCandidate(v0, v0.pt(), centrality, selMap, selGapSide, nLambdas); + } // end v0 loop + + // fill the histograms with the number of reconstructed Lambda per collision + if (analyseLambda) { + histos.fill(HIST("h2dNbrOfLambdaVsCentrality"), centrality, nLambdas); + } + } + + // Subscriping to the appropriate tables and running the code: + // ______________________________________________________ + // Real data processing in Run 3 - no MC subscription + void processRealDataRun3(soa::Join::iterator const& collision, V0Candidates const& fullV0s, DauTracks const&) + { + analyzeRecoedV0sInRealData(collision, fullV0s); + } + + // Kept only the process switch that is relevant for this particular work: + PROCESS_SWITCH(lambdaInvMassTest, processRealDataRun3, "process as if real data in Run 3", true); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} \ No newline at end of file From dc178bec2157e94973af81a822569f7c43db1cde Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Thu, 16 Oct 2025 16:01:31 -0300 Subject: [PATCH 02/40] Adding cmakelist changes --- PWGLF/Tasks/Strangeness/CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/PWGLF/Tasks/Strangeness/CMakeLists.txt b/PWGLF/Tasks/Strangeness/CMakeLists.txt index 31a0087b3fa..adc1d5788b1 100644 --- a/PWGLF/Tasks/Strangeness/CMakeLists.txt +++ b/PWGLF/Tasks/Strangeness/CMakeLists.txt @@ -94,6 +94,11 @@ o2physics_add_dpl_workflow(lambdapolarization PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(lambdaInvMassTest + SOURCES lambdaInvMassTest.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(lambdapolsp SOURCES lambdapolsp.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore From e6033133329c171f96572eda224a2a9eea5561c5 Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Thu, 16 Oct 2025 17:21:18 -0300 Subject: [PATCH 03/40] Fixing CMakeLists to actually have the correct links for the code to work! --- PWGLF/Tasks/Strangeness/CMakeLists.txt | 4 ++-- PWGLF/Tasks/Strangeness/lambdaInvMassTest.cxx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/PWGLF/Tasks/Strangeness/CMakeLists.txt b/PWGLF/Tasks/Strangeness/CMakeLists.txt index adc1d5788b1..4f23de408b1 100644 --- a/PWGLF/Tasks/Strangeness/CMakeLists.txt +++ b/PWGLF/Tasks/Strangeness/CMakeLists.txt @@ -94,9 +94,9 @@ o2physics_add_dpl_workflow(lambdapolarization PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) -o2physics_add_dpl_workflow(lambdaInvMassTest +o2physics_add_dpl_workflow(lambdainvmasstest SOURCES lambdaInvMassTest.cxx - PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore O2Physics::AnalysisCCDB COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(lambdapolsp diff --git a/PWGLF/Tasks/Strangeness/lambdaInvMassTest.cxx b/PWGLF/Tasks/Strangeness/lambdaInvMassTest.cxx index e7fa162c2f4..951bd68f633 100644 --- a/PWGLF/Tasks/Strangeness/lambdaInvMassTest.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaInvMassTest.cxx @@ -33,7 +33,7 @@ #include "Common/DataModel/PIDResponse.h" #include "Common/DataModel/TrackSelectionTables.h" #include "Tools/ML/MlResponse.h" -#include "Tools/ML/model.h" +#include "Tools/ML/model.h" // This actually needs ONNX to be installed in the system! Or, at least, you should link the libraries properly in the CMakeLists.txt #include "CommonConstants/MathConstants.h" #include "CommonConstants/PhysicsConstants.h" From 7b939000bc21bb881bc7a4c5ac002cb33fd7934c Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Thu, 16 Oct 2025 19:03:15 -0300 Subject: [PATCH 04/40] Fixing dependencies, wrong syntax and missing variables/functions --- PWGLF/Tasks/Strangeness/lambdaInvMassTest.cxx | 199 ++++++++++++++++-- 1 file changed, 185 insertions(+), 14 deletions(-) diff --git a/PWGLF/Tasks/Strangeness/lambdaInvMassTest.cxx b/PWGLF/Tasks/Strangeness/lambdaInvMassTest.cxx index 951bd68f633..fc6691ae079 100644 --- a/PWGLF/Tasks/Strangeness/lambdaInvMassTest.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaInvMassTest.cxx @@ -138,7 +138,7 @@ struct lambdaInvMassTest{ // Configurable minInvMass{"minInvMass", 0.7f, "Lower bound of invariant mass axis"}; // Configurable maxInvMass{"maxInvMass", 1.3f, "Upper bound of invariant mass axis"}; // From David's code: - ConfigurableAxis axisLambdaMass{"axisLambdaMass", {200, 1.101f, 1.131f}, ""}; + // ConfigurableAxis axisLambdaMass{"axisLambdaMass", {200, 1.101f, 1.131f}, ""}; // Defining a configurable object group for the event selections: struct : ConfigurableGroup { @@ -189,6 +189,8 @@ struct lambdaInvMassTest{ Configurable useSPDTrackletsCent{"useSPDTrackletsCent", false, "Use SPD tracklets for estimating centrality? If not, use V0M-based centrality (Run 2 only)"}; } eventSelections; + static constexpr float DefaultLifetimeCuts[1][2] = {{30., 20.}}; + // Defining the configurable object that is going to be used: v0Selections, for selections later on struct : ConfigurableGroup { std::string prefix = "v0Selections"; // JSON group name @@ -241,22 +243,34 @@ struct lambdaInvMassTest{ Configurable maxDeltaTimePion{"maxDeltaTimePion", 1e+9, "check maximum allowed time"}; } v0Selections; - // Machine learning evaluation for pre-selection and corresponding information generation - // o2::ml::OnnxModel mlCustomModelK0Short; + TF1* fPhiCutLow = new TF1("fPhiCutLow", v0Selections.phiLowCut.value.data(), 0, 100); + TF1* fPhiCutHigh = new TF1("fPhiCutHigh", v0Selections.phiHighCut.value.data(), 0, 100); + + 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 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; + + RCTFlagsChecker rctFlagsChecker{rctConfigurations.cfgRCTLabel.value}; + + // Machine learning evaluation for pre-selection and corresponding information generation + o2::ml::OnnxModel mlCustomModelK0Short; o2::ml::OnnxModel mlCustomModelLambda; o2::ml::OnnxModel mlCustomModelAntiLambda; o2::ml::OnnxModel mlCustomModelGamma; - struct : ConfigurableGroup { + struct : ConfigurableGroup { // Kept the original configurable scores for K0Short and all else due to the line "if (lambdaScore > mlConfigurations.thresholdK0Short.value) (...)" std::string prefix = "mlConfigurations"; // JSON group name // ML classifiers: master flags to control whether we should use custom ML classifiers or the scores in the derived data - // Configurable useK0ShortScores{"useK0ShortScores", false, "use ML scores to select K0Short"}; + Configurable useK0ShortScores{"useK0ShortScores", false, "use ML scores to select K0Short"}; Configurable useLambdaScores{"useLambdaScores", false, "use ML scores to select Lambda"}; - // Configurable useAntiLambdaScores{"useAntiLambdaScores", false, "use ML scores to select AntiLambda"}; + Configurable useAntiLambdaScores{"useAntiLambdaScores", false, "use ML scores to select AntiLambda"}; - // Configurable calculateK0ShortScores{"calculateK0ShortScores", false, "calculate K0Short ML scores"}; + Configurable calculateK0ShortScores{"calculateK0ShortScores", false, "calculate K0Short ML scores"}; Configurable calculateLambdaScores{"calculateLambdaScores", false, "calculate Lambda ML scores"}; - // Configurable calculateAntiLambdaScores{"calculateAntiLambdaScores", false, "calculate AntiLambda ML scores"}; + Configurable calculateAntiLambdaScores{"calculateAntiLambdaScores", false, "calculate AntiLambda ML scores"}; // ML input for ML calculation Configurable customModelPathCCDB{"customModelPathCCDB", "", "Custom ML Model path in CCDB"}; @@ -266,13 +280,13 @@ struct lambdaInvMassTest{ // Local paths for test purposes Configurable localModelPathLambda{"localModelPathLambda", "Lambda_BDTModel.onnx", "(std::string) Path to the local .onnx file."}; - // Configurable localModelPathAntiLambda{"localModelPathAntiLambda", "AntiLambda_BDTModel.onnx", "(std::string) Path to the local .onnx file."}; - // Configurable localModelPathK0Short{"localModelPathK0Short", "KZeroShort_BDTModel.onnx", "(std::string) Path to the local .onnx file."}; + Configurable localModelPathAntiLambda{"localModelPathAntiLambda", "AntiLambda_BDTModel.onnx", "(std::string) Path to the local .onnx file."}; + Configurable localModelPathK0Short{"localModelPathK0Short", "KZeroShort_BDTModel.onnx", "(std::string) Path to the local .onnx file."}; // Thresholds for choosing to populate V0Cores tables with pre-selections Configurable thresholdLambda{"thresholdLambda", -1.0f, "Threshold to keep Lambda candidates"}; - // Configurable thresholdAntiLambda{"thresholdAntiLambda", -1.0f, "Threshold to keep AntiLambda candidates"}; - // Configurable thresholdK0Short{"thresholdK0Short", -1.0f, "Threshold to keep K0Short candidates"}; + Configurable thresholdAntiLambda{"thresholdAntiLambda", -1.0f, "Threshold to keep AntiLambda candidates"}; + Configurable thresholdK0Short{"thresholdK0Short", -1.0f, "Threshold to keep K0Short candidates"}; } mlConfigurations; // CCDB options @@ -290,6 +304,14 @@ struct lambdaInvMassTest{ Configurable customMagField{"customMagField", 5.0f, "Manually set magnetic field"}; } ccdbConfigurations; + o2::ccdb::CcdbApi ccdbApi; + Service ccdb; + ctpRateFetcher rateFetcher; + int mRunNumber; + float magField; + std::map metadata; + o2::parameters::GRPMagField* grpmag = nullptr; + // CCDB options struct : ConfigurableGroup { 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"}; @@ -345,6 +367,18 @@ struct lambdaInvMassTest{ ConfigurableAxis axisMonteCarloNch{"axisMonteCarloNch", {300, 0.0f, 3000.0f}, "N_{ch} MC"}; } axisConfigurations; + // These will not be used, but the variables were needed for some cut David did: + // UPC selections + SGSelector sgSelector; + struct : ConfigurableGroup { + std::string prefix = "upcCuts"; // JSON group name + Configurable fv0Cut{"fv0Cut", 100., "FV0A threshold"}; + Configurable ft0Acut{"ft0Acut", 200., "FT0A threshold"}; + Configurable ft0Ccut{"ft0Ccut", 100., "FT0C threshold"}; + Configurable zdcCut{"zdcCut", 10., "ZDC threshold"}; + // Configurable gapSel{"gapSel", 2, "Gap selection"}; + } upcCuts; + // For manual sliceBy // Preslice> perMcCollision = aod::v0data::straMCCollisionId; @@ -421,11 +455,11 @@ struct lambdaInvMassTest{ // histos.add("eventCounter", "eventCounter", kTH1F, {axisCounter}); // // histos.add("LambdaInvMass1D", "Test LambdaInvMass 1D", kTH1F, {axisLambdaMass}); histos.add("hMassLambda", "hMassLambda", kTH1D, {axisConfigurations.axisLambdaMass}); - histos.add("ptQAHist", "ptQAHist", kTH1F, {axisPtQA}); + // histos.add("ptQAHist", "ptQAHist", kTH1F, {axisPtQA}); // From the analyseLambda flag: histos.add("h2dNbrOfLambdaVsCentrality", "h2dNbrOfLambdaVsCentrality", kTH2D, {axisConfigurations.axisCentrality, {10, -0.5f, 9.5f}}); - histos.add("h3dMassLambda", "h3dMassLambda", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtQA, axisConfigurations.axisLambdaMass}); + histos.add("h3dMassLambda", "h3dMassLambda", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisLambdaMass}); // Non-UPC info histos.add("h3dMassLambdaHadronic", "h3dMassLambdaHadronic", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisLambdaMass}); // Not doing ultra-peripheral! @@ -502,6 +536,32 @@ struct lambdaInvMassTest{ } } + 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 + { + if (magField < 0) // for negative polarity field + phi = o2::constants::math::TwoPI - phi; + if (sign < 0) // for negative charge + phi = o2::constants::math::TwoPI - phi; + if (phi < 0) + LOGF(warning, "phi < 0: %g", phi); + + phi += o2::constants::math::PI / 18.0; // to center gap in the middle + return fmod(phi, o2::constants::math::PI / 9.0); + } + + bool isTrackFarFromTPCBoundary(double trackPt, double trackPhi, int sign) + // check whether the track passes close to a TPC sector boundary + { + double phiModn = computePhiMod(trackPhi, sign); + if (phiModn > fPhiCutHigh->Eval(trackPt)) + return true; // keep track + if (phiModn < fPhiCutLow->Eval(trackPt)) + return true; // keep track + return false; // reject track + } + template // uint64_t computeReconstructionBitmap(TV0 v0, TCollision collision, float rapidityLambda, float rapidityK0Short, float /*pT*/) uint64_t computeReconstructionBitmap(TV0 v0, TCollision collision, float rapidityLambda, float /*pT*/) @@ -614,6 +674,117 @@ struct lambdaInvMassTest{ return bitMap; } + bool verifyMask(uint64_t bitmap, uint64_t mask) + { + return (bitmap & mask) == mask; + } + + int computeITSclusBitmap(uint8_t itsClusMap, bool fromAfterburner) + // Focus on the 12 dominant ITS cluster configurations + { + int bitMap = 0; + + if (verifyMask(itsClusMap, ((uint8_t(1) << 0) | (uint8_t(1) << 1) | (uint8_t(1) << 2) | (uint8_t(1) << 3) | (uint8_t(1) << 4) | (uint8_t(1) << 5) | (uint8_t(1) << 6)))) { + // ITS : IB OB + // ITS : L0 L1 L2 L3 L4 L5 L6 + // ITS : x x x x x x x + bitMap = 12; + } else if (verifyMask(itsClusMap, ((uint8_t(1) << 1) | (uint8_t(1) << 2) | (uint8_t(1) << 3) | (uint8_t(1) << 4) | (uint8_t(1) << 5) | (uint8_t(1) << 6)))) { + // ITS : IB OB + // ITS : L0 L1 L2 L3 L4 L5 L6 + // ITS : x x x x x x + bitMap = 11; + } else if (verifyMask(itsClusMap, ((uint8_t(1) << 2) | (uint8_t(1) << 3) | (uint8_t(1) << 4) | (uint8_t(1) << 5) | (uint8_t(1) << 6)))) { + // ITS : IB OB + // ITS : L0 L1 L2 L3 L4 L5 L6 + // ITS : x x x x x + bitMap = 10; + } else if (verifyMask(itsClusMap, ((uint8_t(1) << 3) | (uint8_t(1) << 4) | (uint8_t(1) << 5) | (uint8_t(1) << 6)))) { + // ITS : IB OB + // ITS : L0 L1 L2 L3 L4 L5 L6 + // ITS : x x x x + bitMap = 9; + if (fromAfterburner) + bitMap = -3; + } else if (verifyMask(itsClusMap, ((uint8_t(1) << 4) | (uint8_t(1) << 5) | (uint8_t(1) << 6)))) { + // ITS : IB OB + // ITS : L0 L1 L2 L3 L4 L5 L6 + // ITS : x x x + bitMap = 8; + if (fromAfterburner) + bitMap = -2; + } else if (verifyMask(itsClusMap, ((uint8_t(1) << 5) | (uint8_t(1) << 6)))) { + // ITS : IB OB + // ITS : L0 L1 L2 L3 L4 L5 L6 + // ITS : x x + bitMap = 7; + if (fromAfterburner) + bitMap = -1; + } else if (verifyMask(itsClusMap, ((uint8_t(1) << 0) | (uint8_t(1) << 1) | (uint8_t(1) << 2) | (uint8_t(1) << 3) | (uint8_t(1) << 4) | (uint8_t(1) << 5)))) { + // ITS : IB OB + // ITS : L0 L1 L2 L3 L4 L5 L6 + // ITS : x x x x x x + bitMap = 6; + } else if (verifyMask(itsClusMap, ((uint8_t(1) << 1) | (uint8_t(1) << 2) | (uint8_t(1) << 3) | (uint8_t(1) << 4) | (uint8_t(1) << 5)))) { + // ITS : IB OB + // ITS : L0 L1 L2 L3 L4 L5 L6 + // ITS : x x x x x + bitMap = 5; + } else if (verifyMask(itsClusMap, ((uint8_t(1) << 2) | (uint8_t(1) << 3) | (uint8_t(1) << 4) | (uint8_t(1) << 5)))) { + // ITS : IB OB + // ITS : L0 L1 L2 L3 L4 L5 L6 + // ITS : x x x x + bitMap = 4; + } else if (verifyMask(itsClusMap, ((uint8_t(1) << 0) | (uint8_t(1) << 1) | (uint8_t(1) << 2) | (uint8_t(1) << 3) | (uint8_t(1) << 4)))) { + // ITS : IB OB + // ITS : L0 L1 L2 L3 L4 L5 L6 + // ITS : x x x x x + bitMap = 3; + } else if (verifyMask(itsClusMap, ((uint8_t(1) << 1) | (uint8_t(1) << 2) | (uint8_t(1) << 3) | (uint8_t(1) << 4)))) { + // ITS : IB OB + // ITS : L0 L1 L2 L3 L4 L5 L6 + // ITS : x x x x + bitMap = 2; + } else if (verifyMask(itsClusMap, ((uint8_t(1) << 0) | (uint8_t(1) << 1) | (uint8_t(1) << 2) | (uint8_t(1) << 3)))) { + // ITS : IB OB + // ITS : L0 L1 L2 L3 L4 L5 L6 + // ITS : x x x x + bitMap = 1; + } else { + // ITS : other configurations + bitMap = 0; + } + + return bitMap; + } + + uint computeDetBitmap(uint8_t detMap) + // Focus on the 4 dominant track configurations : + // Others + // ITS-TPC + // ITS-TPC-TRD + // ITS-TPC-TOF + // ITS-TPC-TRD-TOF + { + uint bitMap = 0; + + if (verifyMask(detMap, (o2::aod::track::ITS | o2::aod::track::TPC | o2::aod::track::TRD | o2::aod::track::TOF))) { + // ITS-TPC-TRD-TOF + bitMap = 4; + } else if (verifyMask(detMap, (o2::aod::track::ITS | o2::aod::track::TPC | o2::aod::track::TOF))) { + // ITS-TPC-TOF + bitMap = 3; + } else if (verifyMask(detMap, (o2::aod::track::ITS | o2::aod::track::TPC | o2::aod::track::TRD))) { + // ITS-TPC-TRD + bitMap = 2; + } else if (verifyMask(detMap, (o2::aod::track::ITS | o2::aod::track::TPC))) { + // ITS-TPC + bitMap = 1; + } + + return bitMap; + } + // function to load models for ML-based classifiers void loadMachines(int64_t timeStampML) { From 785d53bc9a8baf227c41fadcfabaa88c21d3164a Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Fri, 17 Oct 2025 17:39:27 -0300 Subject: [PATCH 05/40] Fixing some more dependencies for the event selection histograms --- PWGLF/Tasks/Strangeness/lambdaInvMassTest.cxx | 174 +++++++++++++++++- 1 file changed, 173 insertions(+), 1 deletion(-) diff --git a/PWGLF/Tasks/Strangeness/lambdaInvMassTest.cxx b/PWGLF/Tasks/Strangeness/lambdaInvMassTest.cxx index fc6691ae079..f2ecb10d34f 100644 --- a/PWGLF/Tasks/Strangeness/lambdaInvMassTest.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaInvMassTest.cxx @@ -457,6 +457,178 @@ struct lambdaInvMassTest{ histos.add("hMassLambda", "hMassLambda", kTH1D, {axisConfigurations.axisLambdaMass}); // histos.add("ptQAHist", "ptQAHist", kTH1F, {axisPtQA}); + + /////////////////////////////////////////////////////////// + // Event Counters + histos.add("hEventSelection", "hEventSelection", kTH1D, {{21, -0.5f, +20.5f}}); + if (isRun3) { + 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"); + if (doPPAnalysis) { + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(17, "INEL>0"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(18, "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(19, "Below min IR"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(20, "Above max IR"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(21, "RCT flags"); + } else { + 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, "sel7 cut"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(4, "kINT7"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(5, "kIsTriggerTVX"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(6, "kNoIncompleteDAQ"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(7, "posZ cut"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(8, "kNoInconsistentVtx"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(9, "kNoPileupFromSPD"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(10, "kNoV0PFPileup"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(11, "kNoPileupInMultBins"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(12, "kNoPileupMV"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(13, "kNoPileupTPC"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(14, "kNoV0MOnVsOfPileup"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(15, "kNoSPDOnVsOfPileup"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(16, "kNoSPDClsVsTklBG"); + if (doPPAnalysis) { + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(17, "INEL>0"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(18, "INEL>1"); + } + } + + histos.add("hEventCentrality", "hEventCentrality", kTH1D, {{101, 0.0f, 101.0f}}); + histos.add("hCentralityVsNch", "hCentralityVsNch", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisNch}); + if (doEventQA) { + if (isRun3) { + histos.add("hEventSelectionVsCentrality", "hEventSelectionVsCentrality", kTH2D, {{21, -0.5f, +20.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"); + if (doPPAnalysis) { + histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(17, "INEL>0"); + histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(18, "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(19, "Below min IR"); + histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(20, "Above max IR"); + histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(21, "RCT flags"); + + histos.add("hCentralityVsNGlobal", "hCentralityVsNGlobal", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisNch}); + histos.add("hEventCentVsMultFT0M", "hEventCentVsMultFT0M", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisMultFT0M}); + histos.add("hEventCentVsMultFT0C", "hEventCentVsMultFT0C", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisMultFT0C}); + histos.add("hEventCentVsMultNGlobal", "hEventCentVsMultNGlobal", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisNch}); + histos.add("hEventCentVsMultFV0A", "hEventCentVsMultFV0A", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisMultFV0A}); + histos.add("hEventMultFT0MvsMultNGlobal", "hEventMultFT0MvsMultNGlobal", kTH2D, {axisConfigurations.axisMultFT0M, axisConfigurations.axisNch}); + histos.add("hEventMultFT0CvsMultNGlobal", "hEventMultFT0CvsMultNGlobal", kTH2D, {axisConfigurations.axisMultFT0C, axisConfigurations.axisNch}); + histos.add("hEventMultFV0AvsMultNGlobal", "hEventMultFV0AvsMultNGlobal", kTH2D, {axisConfigurations.axisMultFV0A, axisConfigurations.axisNch}); + histos.add("hEventMultPVvsMultNGlobal", "hEventMultPVvsMultNGlobal", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisNch}); + histos.add("hEventMultFT0CvsMultFV0A", "hEventMultFT0CvsMultFV0A", 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}}); + if (isRun3) { + histos.add("hEventPVzMC", "hEventPVzMC", kTH1D, {{100, -20.0f, +20.0f}}); + histos.add("hCentralityVsPVzMC", "hCentralityVsPVzMC", kTH2D, {{101, 0.0f, 101.0f}, {100, -20.0f, +20.0f}}); + } + + histos.add("hEventOccupancy", "hEventOccupancy", kTH1D, {axisConfigurations.axisOccupancy}); + histos.add("hCentralityVsOccupancy", "hCentralityVsOccupancy", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisOccupancy}); + + histos.add("hGapSide", "Gap side; Entries", kTH1D, {{5, -0.5, 4.5}}); + histos.add("hSelGapSide", "Selected gap side; Entries", kTH1D, {axisConfigurations.axisSelGap}); + histos.add("hEventCentralityVsSelGapSide", ";Centrality (%); Selected gap side", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisSelGap}); + + 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}); + + // for QA and test purposes + auto hRawCentrality = histos.add("hRawCentrality", "hRawCentrality", kTH1D, {axisConfigurations.axisRawCentrality}); + + for (int ii = 1; ii < 101; ii++) { + float value = 100.5f - static_cast(ii); + hRawCentrality->SetBinContent(ii, value); + } + + auto hSelectionV0s = histos.add("GeneralQA/hSelectionV0s", "hSelectionV0s", kTH1D, {{static_cast(selPhysPrimAntiLambda) + 3, -0.5f, static_cast(selPhysPrimAntiLambda) + 2.5f}}); + hSelectionV0s->GetXaxis()->SetBinLabel(1, "All"); + hSelectionV0s->GetXaxis()->SetBinLabel(selCosPA + 2, "cosPA"); + hSelectionV0s->GetXaxis()->SetBinLabel(selRadius + 2, "Radius min."); + hSelectionV0s->GetXaxis()->SetBinLabel(selRadiusMax + 2, "Radius max."); + hSelectionV0s->GetXaxis()->SetBinLabel(selDCANegToPV + 2, "DCA neg. to PV"); + hSelectionV0s->GetXaxis()->SetBinLabel(selDCAPosToPV + 2, "DCA pos. to PV"); + hSelectionV0s->GetXaxis()->SetBinLabel(selDCAV0Dau + 2, "DCA V0 dau."); + hSelectionV0s->GetXaxis()->SetBinLabel(selK0ShortRapidity + 2, "K^{0}_{S} rapidity"); + hSelectionV0s->GetXaxis()->SetBinLabel(selLambdaRapidity + 2, "#Lambda rapidity"); + hSelectionV0s->GetXaxis()->SetBinLabel(selTPCPIDPositivePion + 2, "TPC PID #pi^{+}"); + hSelectionV0s->GetXaxis()->SetBinLabel(selTPCPIDNegativePion + 2, "TPC PID #pi^{-}"); + hSelectionV0s->GetXaxis()->SetBinLabel(selTPCPIDPositiveProton + 2, "TPC PID p"); + hSelectionV0s->GetXaxis()->SetBinLabel(selTPCPIDNegativeProton + 2, "TPC PID #bar{p}"); + hSelectionV0s->GetXaxis()->SetBinLabel(selTOFDeltaTPositiveProtonLambda + 2, "TOF #Delta t p from #Lambda"); + hSelectionV0s->GetXaxis()->SetBinLabel(selTOFDeltaTPositivePionLambda + 2, "TOF #Delta t #pi^{+} from #Lambda"); + hSelectionV0s->GetXaxis()->SetBinLabel(selTOFDeltaTPositivePionK0Short + 2, "TOF #Delta t #pi^{+} from K^{0}_{S}"); + hSelectionV0s->GetXaxis()->SetBinLabel(selTOFDeltaTNegativeProtonLambda + 2, "TOF #Delta t #bar{p} from #Lambda"); + hSelectionV0s->GetXaxis()->SetBinLabel(selTOFDeltaTNegativePionLambda + 2, "TOF #Delta t #pi^{-} from #Lambda"); + hSelectionV0s->GetXaxis()->SetBinLabel(selTOFDeltaTNegativePionK0Short + 2, "TOF #Delta t #pi^{-} from K^{0}_{S}"); + hSelectionV0s->GetXaxis()->SetBinLabel(selTOFNSigmaPositiveProtonLambda + 2, "TOF PID p from #Lambda"); + hSelectionV0s->GetXaxis()->SetBinLabel(selTOFNSigmaPositivePionLambda + 2, "TOF PID #pi^{+} from #Lambda"); + hSelectionV0s->GetXaxis()->SetBinLabel(selTOFNSigmaPositivePionK0Short + 2, "TOF PID #pi^{+} from K^{0}_{S}"); + hSelectionV0s->GetXaxis()->SetBinLabel(selTOFNSigmaNegativeProtonLambda + 2, "TOF PID #bar{p} from #Lambda"); + hSelectionV0s->GetXaxis()->SetBinLabel(selTOFNSigmaNegativePionLambda + 2, "TOF PID #pi^{-} from #Lambda"); + hSelectionV0s->GetXaxis()->SetBinLabel(selTOFNSigmaNegativePionK0Short + 2, "TOF PID #pi^{-} from K^{0}_{S}"); + hSelectionV0s->GetXaxis()->SetBinLabel(selK0ShortCTau + 2, "K^{0}_{S} lifetime"); + hSelectionV0s->GetXaxis()->SetBinLabel(selLambdaCTau + 2, "#Lambda lifetime"); + hSelectionV0s->GetXaxis()->SetBinLabel(selK0ShortArmenteros + 2, "Arm. pod. cut"); + hSelectionV0s->GetXaxis()->SetBinLabel(selPosGoodTPCTrack + 2, "Pos. good TPC track"); + hSelectionV0s->GetXaxis()->SetBinLabel(selNegGoodTPCTrack + 2, "Neg. good TPC track"); + hSelectionV0s->GetXaxis()->SetBinLabel(selPosGoodITSTrack + 2, "Pos. good ITS track"); + hSelectionV0s->GetXaxis()->SetBinLabel(selNegGoodITSTrack + 2, "Neg. good ITS track"); + hSelectionV0s->GetXaxis()->SetBinLabel(selPosItsOnly + 2, "Pos. ITS-only"); + hSelectionV0s->GetXaxis()->SetBinLabel(selNegItsOnly + 2, "Neg. ITS-only"); + hSelectionV0s->GetXaxis()->SetBinLabel(selPosNotTPCOnly + 2, "Pos. not TPC-only"); + hSelectionV0s->GetXaxis()->SetBinLabel(selNegNotTPCOnly + 2, "Neg. not TPC-only"); + hSelectionV0s->GetXaxis()->SetBinLabel(selConsiderK0Short + 2, "True K^{0}_{S}"); + hSelectionV0s->GetXaxis()->SetBinLabel(selConsiderLambda + 2, "True #Lambda"); + hSelectionV0s->GetXaxis()->SetBinLabel(selConsiderAntiLambda + 2, "True #bar{#Lambda}"); + hSelectionV0s->GetXaxis()->SetBinLabel(selPhysPrimK0Short + 2, "Phys. prim. K^{0}_{S}"); + hSelectionV0s->GetXaxis()->SetBinLabel(selPhysPrimLambda + 2, "Phys. prim. #Lambda"); + hSelectionV0s->GetXaxis()->SetBinLabel(selPhysPrimAntiLambda + 2, "Phys. prim. #bar{#Lambda}"); + hSelectionV0s->GetXaxis()->SetBinLabel(selPhysPrimAntiLambda + 3, "Cand. selected"); + /////////////////////////////////////////////////////////// + // From the analyseLambda flag: histos.add("h2dNbrOfLambdaVsCentrality", "h2dNbrOfLambdaVsCentrality", kTH2D, {axisConfigurations.axisCentrality, {10, -0.5f, 9.5f}}); histos.add("h3dMassLambda", "h3dMassLambda", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisLambdaMass}); @@ -536,7 +708,7 @@ struct lambdaInvMassTest{ } } - double computePhiMod(double phi, int sign) + 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 { From 7d1470159df847ae9a444c3f511c55fe945493ea Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Wed, 4 Feb 2026 11:19:08 -0300 Subject: [PATCH 06/40] First tests, earlier version of passesLambdaLambdaBarHypothesis --- PWGLF/Tasks/Strangeness/lambdaInvMassTest.cxx | 1 - .../Strangeness/lambdaJetPolarizationIons.cxx | 685 ++++++++++++++++++ 2 files changed, 685 insertions(+), 1 deletion(-) create mode 100644 PWGLF/Tasks/Strangeness/lambdaJetPolarizationIons.cxx diff --git a/PWGLF/Tasks/Strangeness/lambdaInvMassTest.cxx b/PWGLF/Tasks/Strangeness/lambdaInvMassTest.cxx index f2ecb10d34f..dfd033b466d 100644 --- a/PWGLF/Tasks/Strangeness/lambdaInvMassTest.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaInvMassTest.cxx @@ -30,7 +30,6 @@ #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" -#include "Common/DataModel/PIDResponse.h" #include "Common/DataModel/TrackSelectionTables.h" #include "Tools/ML/MlResponse.h" #include "Tools/ML/model.h" // This actually needs ONNX to be installed in the system! Or, at least, you should link the libraries properly in the CMakeLists.txt diff --git a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIons.cxx b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIons.cxx new file mode 100644 index 00000000000..fd2d6cc53a3 --- /dev/null +++ b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIons.cxx @@ -0,0 +1,685 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +/// \file lambdaJetPolarizationIons.cxx +/// \brief Lambda and antiLambda polarization analysis task using raw data +/// +/// \author Cicero Domenico Muncinelli , Campinas State University +// +// Jet Polarization Ions task +// ================ +// +// This code loops over a V0Cores table and produces some +// standard analysis output. It is meant to be run over +// derived data. +// +// Comments, questions, complaints, suggestions? +// Please write to: +// cicero.domenico.muncinelli@cern.ch +// + +#include "PWGJE/Core/JetBkgSubUtils.h" +#include "PWGJE/Core/JetDerivedDataUtilities.h" +#include "PWGJE/Core/JetUtilities.h" +#include "PWGJE/DataModel/Jet.h" +#include "PWGJE/DataModel/JetReducedData.h" +#include "PWGLF/DataModel/LFStrangenessTables.h" +#include "PWGLF/DataModel/mcCentrality.h" +#include "Common/DataModel/Centrality.h" + +#include "Common/Core/RecoDecay.h" +#include "Common/Core/TrackSelection.h" +#include "Common/Core/trackUtilities.h" +#include "Common/DataModel/EventSelection.h" +// #include "Common/DataModel/Multiplicity.h" +// #include "Common/DataModel/PIDResponseTOF.h" // Not using right now + +#include "Common/DataModel/McCollisionExtra.h" +#include "Common/DataModel/PIDResponseTPC.h" +// #include "Common/DataModel/Qvectors.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +// Jets: +#include +#include +#include +#include +#include +#include +#include +#include + + +////// Others not included from strangederivedbuilder.cxx: +// #include "CommonConstants/PhysicsConstants.h" +// #include "DCAFitter/DCAFitterN.h" +// #include "DataFormatsParameters/GRPMagField.h" +// #include "DataFormatsParameters/GRPObject.h" +// #include "DetectorsBase/GeometryManager.h" +// #include "DetectorsBase/Propagator.h" +// #include "Framework/O2DatabasePDGPlugin.h" +// #include "Framework/RunningWorkflowInfo.h" +// #include "Framework/StaticFor.h" + +#include +#include +#include +#include + +////////////////////////////////////////////// +// From Youpeng's: +#include "Common/DataModel/CollisionAssociationTables.h" +#include "Framework/ASoA.h" + +#include "Math/GenVector/Boost.h" +#include "Math/Vector3D.h" +#include "Math/Vector4D.h" +// #include "TProfile2D.h" +// #include +// // #include +// #include +// #include +////////////////////////////////////////////// + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using std::array; +using namespace o2::aod::rctsel; + +// Aliases for joined tables: +using SelCollisions = soa::Join; // Estimates centrality from FT0M. Will add other variations for cross-check + // Cross-check with derivedlambdakzeroanalysis' CentEstimator enumerable. It has a lot of centrality estimators! (TODO) +// using SelV0Collisions = soa::Join; // Is this better for event selection? Maybe the pre-ordered centralities are useful? +using DauTracks = soa::Join; + // , aod::pidTOFFullPi, aod::pidTOFFullKa, aod::pidTOFFullPr>; // Not using TOF right now due to some possible mismatches +// using SimCollisions = soa::Join; +// using DauTracksMC = soa::Join; + +// (TODO: check what is gapSide and how to implement related selections) + +////////////////////////////////////////////// +struct lambdajetpolarizationions { + + // HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + // Define histogram registries + HistogramRegistry registryData{"registryData", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry registryMC{"registryMC", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry registryQC{"registryQC", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + + // 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 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 centralityEstimator{"centralityEstimator", kCentFT0C, "Run 3 centrality estimator (0:CentFT0C, 1:CentFT0M, 2:CentFT0CVariant1, 3:CentMFT, 4:CentNGlobal, 5:CentFV0A)"}; // Default is FT0M + + /////////////////////////////////////////////// + // QA block -- not implemented! (TODO) + // Configurable doEventQA{"doEventQA", false, "do event QA histograms"}; + // Configurable doCompleteTopoQA{"doCompleteTopoQA", false, "do topological variable QA histograms"}; + // Configurable doTPCQA{"doTPCQA", false, "do TPC QA histograms"}; + // Configurable doTOFQA{"doTOFQA", false, "do TOF QA histograms"}; + // Configurable doDetectPropQA{"doDetectPropQA", 0, "do Detector/ITS map QA: 0: no, 1: 4D, 2: 5D with mass; 3: plain in 3D"}; + // Configurable doEtaPhiQA{"doEtaPhiQA", false, "do Eta/Phi QA histograms"}; + + // Configurable doPlainTopoQA{"doPlainTopoQA", true, "do simple 1D QA of candidates"}; + // Configurable qaMinPt{"qaMinPt", 0.0f, "minimum pT for QA plots"}; + // Configurable qaMaxPt{"qaMaxPt", 1000.0f, "maximum pT for QA plots"}; + // Configurable qaCentrality{"qaCentrality", false, "qa centrality flag: check base raw values"}; + /////////////////////////////////////////////// + + /////////////////////////////////////////////// + // MC block -- not implemented! (TODO) + // Configurable doMCAssociation{"doMCAssociation", true, "if MC, do MC association"}; + // Configurable doTreatPiToMuon{"doTreatPiToMuon", false, "Take pi decay into muon into account in MC"}; + // Configurable doCollisionAssociationQA{"doCollisionAssociationQA", true, "check collision association"}; + /////////////////////////////////////////////// + + ////////////////////////////////////////////// + // Manual slice by: (TODO) + // SliceCache cache; + // Preslice V0perCollision = o2::aod::v0data::collisionId; + // Preslice CascperCollision = o2::aod::cascdata::collisionId; + // Preslice KFCascperCollision = o2::aod::cascdata::collisionId; + // Preslice TraCascperCollision = o2::aod::cascdata::collisionId; + // Preslice mcParticlePerMcCollision = o2::aod::mcparticle::mcCollisionId; + // Preslice udCollisionsPerCollision = o2::aod::udcollision::collisionId; + ///////////////////////////////////////////// + + + // Configurable groups: + struct : ConfigurableGroup { + std::string prefix = "eventSelections"; // JSON group name + Configurable requireSel8{"requireSel8", true, "require sel8 event selection"}; + Configurable requireTriggerTVX{"requireTriggerTVX", true, "require FT0 vertex (acceptable FT0C-FT0A time difference) at trigger level"}; // 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 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 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)"}; + 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 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 + 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 + Configurable minIR{"minIR", -1, "minimum IR collisions"}; + Configurable maxIR{"maxIR", -1, "maximum IR collisions"}; + } eventSelections; + + struct : ConfigurableGroup { + std::string prefix = "v0Selections"; // JSON group name + Configurable v0TypeSelection{"v0TypeSelection", 1, "select on a certain V0 type (leave negative if no selection desired)"}; + + // Selection criteria: acceptance + Configurable rapidityCut{"rapidityCut", 0.5, "rapidity"}; + Configurable daughterEtaCut{"daughterEtaCut", 0.9, "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.0, "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 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 v0radiusMax{"v0radiusMax", 1E5, "maximum V0 radius (cm)"}; + Configurable lambdaLifetimeCut{"lambdaLifetimeCut", 30., "lifetime cut (c*tau) for Lambda (cm)"} + + // invariant mass selection + Configurable compMassRejection{"compMassRejection", -1, "Competing mass rejection (GeV/#it{c}^{2})"}; // This was creating some bumps in Youpeng's inv mass spectra. Turned off for now. + + // Track quality + Configurable minTPCrows{"minTPCrows", 70, "minimum TPC crossed rows"}; + Configurable minITSclusters{"minITSclusters", 3, "minimum ITS clusters"}; // Default is off + Configurable minTPCrowsOverFindableClusters{"minTPCrowsOverFindableClusters", -1, "minimum nbr of TPC crossed rows over findable clusters"}; + Configurable minTPCfoundOverFindableClusters{"minTPCfoundOverFindableClusters", -1, "minimum nbr of found over findable TPC clusters"}; + Configurable maxFractionTPCSharedClusters{"maxFractionTPCSharedClusters", 1e+09, "maximum fraction of TPC shared clusters"}; + Configurable maxITSchi2PerNcls{"maxITSchi2PerNcls", 36.0f, "maximum ITS chi2 per clusters"}; // Default is 1e+09. New values from StraInJets recommendations + Configurable maxTPCchi2PerNcls{"maxTPCchi2PerNcls", 4.0f, "maximum TPC chi2 per clusters"}; // Default is 1e+09 + Configurable skipTPConly{"skipTPConly", false, "skip V0s comprised of at least one TPC only prong"}; + Configurable requirePosITSonly{"requirePosITSonly", false, "require that positive track is ITSonly (overrides TPC quality)"}; + Configurable requireNegITSonly{"requireNegITSonly", false, "require that negative track is ITSonly (overrides TPC quality)"}; + Configurable rejectPosITSafterburner{"rejectPosITSafterburner", false, "reject positive track formed out of afterburner ITS tracks"}; + Configurable rejectNegITSafterburner{"rejectNegITSafterburner", false, "reject negative track formed out of afterburner ITS tracks"}; + Configurable requirePosITSafterburnerOnly{"requirePosITSafterburnerOnly", false, "require positive track formed out of afterburner ITS tracks"}; + Configurable requireNegITSafterburnerOnly{"requireNegITSafterburnerOnly", false, "require negative track formed out of afterburner ITS tracks"}; + Configurable rejectTPCsectorBoundary{"rejectTPCsectorBoundary", false, "reject tracks close to the TPC sector boundaries"}; + Configurable phiLowCut{"phiLowCut", "0.06/x+pi/18.0-0.06", "Low azimuth cut parametrisation"}; + 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 tofPidNsigmaCutLaPr{"tofPidNsigmaCutLaPr", 1e+6, "tofPidNsigmaCutLaPr"}; + Configurable tofPidNsigmaCutLaPi{"tofPidNsigmaCutLaPi", 1e+6, "tofPidNsigmaCutLaPi"}; + Configurable tofPidNsigmaCutK0Pi{"tofPidNsigmaCutK0Pi", 1e+6, "tofPidNsigmaCutK0Pi"}; + + // PID (TOF) + Configurable maxDeltaTimeProton{"maxDeltaTimeProton", 1e+9, "check maximum allowed time"}; + Configurable maxDeltaTimePion{"maxDeltaTimePion", 1e+9, "check maximum allowed time"}; + } v0Selections; + + // Helpers for the "isTrackFarFromTPCBoundary" function: + TF1* fPhiCutLow = new TF1("fPhiCutLow", v0Selections.phiLowCut.value.data(), 0, 100); + TF1* fPhiCutHigh = new TF1("fPhiCutHigh", v0Selections.phiHighCut.value.data(), 0, 100); + + // 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 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; + RCTFlagsChecker rctFlagsChecker{rctConfigurations.cfgRCTLabel.value}; + + // ML SELECTIONS BLOCK -- NOT IMPLEMENTED! (TODO) + + // CCDB options + struct : ConfigurableGroup { + std::string prefix = "ccdbConfigurations"; // JSON group name + Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable grpPath{"grpPath", "GLO/GRP/GRP", "Path of the grp file"}; + Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; + Configurable lutPath{"lutPath", "GLO/Param/MatLUT", "Path of the Lut parametrization"}; + Configurable geoPath{"geoPath", "GLO/Config/GeometryAligned", "Path of the geometry file"}; + Configurable mVtxPath{"mVtxPath", "GLO/Calib/MeanVertex", "Path of the mean vertex file"}; + + // manual magnetic field: + Configurable useCustomMagField{"useCustomMagField", false, "Use custom magnetic field value"}; + Configurable customMagField{"customMagField", 5.0f, "Manually set magnetic field"}; + } ccdbConfigurations; + + // Instantiating CCDB: + o2::ccdb::CcdbApi ccdbApi; + Service ccdb; + + // Other useful variables: + ctpRateFetcher rateFetcher; + int mRunNumber; + float magField; + std::map metadata; + o2::parameters::GRPMagField* grpmag = nullptr; + + // Histogram axes configuration: + 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 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 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"}; // (TODO) + // 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)"}; + ConfigurableAxis axisDCAdau{"axisDCAdau", {20, 0.0f, 2.0f}, "DCA (cm)"}; + 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"}; + ConfigurableAxis axisTPCsignal{"axisTPCsignal", {200, 0.0f, 200.0f}, "TPC signal"}; + ConfigurableAxis axisNsigmaTOF{"axisNsigmaTOF", {200, -10.0f, 10.0f}, "N sigma TOF"}; + ConfigurableAxis axisTOFdeltaT{"axisTOFdeltaT", {200, -5000.0f, 5000.0f}, "TOF Delta T (ps)"}; + ConfigurableAxis axisPhi{"axisPhi", {18, 0.0f, constants::math::TwoPI}, "Azimuth angle (rad)"}; + ConfigurableAxis axisPhiMod{"axisPhiMod", {100, 0.0f, constants::math::TwoPI / 18}, "Azimuth angle wrt TPC sector (rad.)"}; + ConfigurableAxis axisEta{"axisEta", {10, -1.0f, 1.0f}, "#eta"}; + 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 + 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"}; + + // MC coll assoc QA axis + ConfigurableAxis axisMonteCarloNch{"axisMonteCarloNch", {300, 0.0f, 3000.0f}, "N_{ch} MC"}; + } axisConfigurations; + + // Jet selection configuration: + // (TODO: Add a configurable to select charged jets, neutral jets, full jets, photon-tagged jets and so on) + 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 + Configurable radiusJet{"rJet", 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. + + Configurable jetAlgorithm{"jetAlgorithm", 2, "jet clustering algorithm. 0 = kT, 1 = C/A, 2 = Anti-kT"}; + Configurable jetRecombScheme{"jetRecombScheme", 0, "Jet recombination scheme: E_scheme (0), pT-scheme (1), pt2-scheme (2), WTA_pt_scheme (7)"} // See PWGJE/JetFinders/jetFinder.h for more info. + Configurable bkgSubtractionStrategy{"bkgSubtractionStrategy", 0, "Jet background subtraction: Area (0), Constituent (1)"}; // Selection bool for background subtraction strategy + Configurable jetType{"jetType", 0, "Jet type: Charged Jet (0), Full Jet (1), Photon-tagged (2), Z-tagged (3)"} // (TODO: create a reasonable track selection for photon/Z-tagged jet tracks, including detector angular acceptance parameters) + + // // Configurables from JE PWG: + // // (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!) + // Configurable jetEWSPtMin{"jetEWSPtMin", 0.0, "minimum event-wise subtracted jet pT"}; + // Configurable jetEWSPtMax{"jetEWSPtMax", 1000.0, "maximum event-wise subtracted jet pT"}; + // Configurable jetGhostArea{"jetGhostArea", 0.005, "jet ghost area"}; + // Configurable ghostRepeat{"ghostRepeat", 0, "set to 0 to gain speed if you dont need area calculation"}; + // Configurable DoTriggering{"DoTriggering", false, "used for the charged jet trigger to remove the eta constraint on the jet axis"}; + // Configurable jetAreaFractionMin{"jetAreaFractionMin", -99.0, "used to make a cut on the jet areas"}; + + // (TODO: Check which of these configurables might be useful for the photon-tagged and regular analyses) + // // event level configurables + // Configurable trackOccupancyInTimeRangeMax{"trackOccupancyInTimeRangeMax", 999999, "maximum occupancy of tracks in neighbouring collisions in a given time range"}; + // Configurable triggerMasks{"triggerMasks", "", "possible JE Trigger masks: fJetChLowPt,fJetChHighPt,fTrackLowPt,fTrackHighPt,fJetD0ChLowPt,fJetD0ChHighPt,fJetLcChLowPt,fJetLcChHighPt,fEMCALReadout,fJetFullHighPt,fJetFullLowPt,fJetNeutralHighPt,fJetNeutralLowPt,fGammaVeryHighPtEMCAL,fGammaVeryHighPtDCAL,fGammaHighPtEMCAL,fGammaHighPtDCAL,fGammaLowPtEMCAL,fGammaLowPtDCAL,fGammaVeryLowPtEMCAL,fGammaVeryLowPtDCAL"}; + // Configurable skipMBGapEvents{"skipMBGapEvents", true, "decide to run over MB gap events or not"}; + // Configurable applyRCTSelections{"applyRCTSelections", true, "decide to apply RCT selections"}; + // // track level configurables + // Configurable trackSelections{"trackSelections", "globalTracks", "set track selections"}; + // Configurable particleSelections{"particleSelections", "PhysicalPrimary", "set particle selections"}; + // // cluster level configurables + // Configurable clusterDefinitionS{"clusterDefinition", "kV3Default", "cluster definition to be selected, e.g. V3Default"}; + // Configurable clusterEtaMin{"clusterEtaMin", -0.71, "minimum cluster eta"}; // For ECMAL: |eta| < 0.7, phi = 1.40 - 3.26 + // Configurable clusterEtaMax{"clusterEtaMax", 0.71, "maximum cluster eta"}; // For ECMAL: |eta| < 0.7, phi = 1.40 - 3.26 + // Configurable clusterPhiMin{"clusterPhiMin", 1.39, "minimum cluster phi"}; + // Configurable clusterPhiMax{"clusterPhiMax", 3.27, "maximum cluster phi"}; + // Configurable clusterEnergyMin{"clusterEnergyMin", 0.5, "minimum cluster energy in EMCAL (GeV)"}; + // Configurable clusterTimeMin{"clusterTimeMin", -25., "minimum Cluster time (ns)"}; + // Configurable clusterTimeMax{"clusterTimeMax", 25., "maximum Cluster time (ns)"}; + // Configurable clusterRejectExotics{"clusterRejectExotics", true, "Reject exotic clusters"}; + // Configurable hadronicCorrectionType{"hadronicCorrectionType", 0, "0 = no correction, 1 = CorrectedOneTrack1, 2 = CorrectedOneTrack2, 3 = CorrectedAllTracks1, 4 = CorrectedAllTracks2"}; + // Configurable doEMCALEventSelection{"doEMCALEventSelection", true, "apply the selection to the event alias_bit for full and neutral jets"}; + // Configurable doEMCALEventSelectionChargedJets{"doEMCALEventSelectionChargedJets", false, "apply the selection to the event alias_bit for charged jets"}; + } jetConfigurations; + + // Track analysis parameters -- A specific group that is different from the v0Selections. In jet analyses we need to control our PseudoJet candidates! + // (TODO: include minimal selection criteria for electrons, muons and photons) + // Notice you do NOT need any PID for the PseudoJet candidates! Only need is to know the 4-momentum appropriately. Thus removed nsigma checks on PID + struct : ConfigurableGroup { + std::string prefix = "pseudoJetCandidateTrackSelections"; // JSON group name + Configurable minNCrossedRowsTPC{"minNCrossedRowsTPC", 70, "Minimum number of TPC crossed rows"}; + Configurable minITSnCls{"minITSnCls", -1, "Minimum number of ITS clusters"}; + Configurable maxChi2TPC{"maxChi2TPC", 4.0f, "Maximum chi2 per cluster TPC"}; + Configurable maxChi2ITS{"maxChi2ITS", 36.0f, "Maximum chi2 per cluster ITS"}; + Configurable etaCut{"etaCut", 0.9f, "Maximum eta absolute value"}; // (TODO: same test as the previous 0.8 eta cut) + + Configurable minCandidatePt{"minCandidatePt", 0.15f,"Minimum track pT for pseudojet candidate (GeV/c)"}; // Reduces number of pseudojet candidates from IR radiation + // (TODO: test these minimal ratios to suppress split tracks in high occupancy PbPb or OO) + // 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"}; + + // Jets typical cuts (suppress non-primary candidates): + Configurable doDCAcuts{"doDCAcuts", false, "Apply DCA cuts to jet candidates (use with care in this analysis!)"} + Configurable maxDCAz{"maxDCAz", 3.2f, "Max DCAz to primary vertex [cm]"}; + + // Configurable maxDCAxy{"maxDCAxy", 2.4f,"Max DCAxy to primary vertex [cm]"}; + // Using same cuts as the StrangenessInJets analysis, with a pt dependence (which may bias high pt, so use with care): + Configurable dcaxyMaxTrackPar0{"dcaxyMaxTrackPar0", 0.0105f, ""}; + Configurable dcaxyMaxTrackPar1{"dcaxyMaxTrackPar1", 0.035f, ""}; + Configurable dcaxyMaxTrackPar2{"dcaxyMaxTrackPar2", 1.1f, ""}; + } pseudoJetCandidateTrackSelections; + + // struct : ConfigurableGroup { + // std::string prefix = "jetQAConfigurations"; // JSON group name + // Configurable + // } jetQAConfigurations; // (TODO) + + // Instantiate utility class for jet background subtraction + JetBkgSubUtils backgroundSub; + + // Lambda Ring Polarization axes configurable group: + struct : ConfigurableGroup { + std::string prefix = "ringPolConfigurations"; // JSON group name + + } ringPolConfigurations; // (TODO) + + + // For manual sliceBy + Preslice V0perCollision = o2::aod::v0data::collisionId; + Preslice mcParticlePerMcCollision = o2::aod::mcparticle::mcCollisionId; + + Service pdg; + + // std::vector genLambda; + // std::vector genAntiLambda; + // std::vector genXiMinus; + // std::vector genXiPlus; + // std::vector genOmegaMinus; + // std::vector genOmegaPlus; + + void init(InitContext const&){ // (TODO: add all useful histograms here! Add flags for QA plots and the such too) + + } + + ///////////////////////////////////////////// + // Computation helper functions: + 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 + { + if (magField < 0) // for negative polarity field + phi = o2::constants::math::TwoPI - phi; + if (sign < 0) // for negative charge + phi = o2::constants::math::TwoPI - phi; + if (phi < 0) + LOGF(warning, "phi < 0: %g", phi); + + phi += o2::constants::math::PI / 18.0; // to center gap in the middle + return fmod(phi, o2::constants::math::PI / 9.0); + } + + bool isTrackFarFromTPCBoundary(double trackPt, double trackPhi, int sign) + // check whether the track passes close to a TPC sector boundary + { + double phiModn = computePhiMod(trackPhi, sign); + if (phiModn > fPhiCutHigh->Eval(trackPt)) return true; // keep track + if (phiModn < fPhiCutLow->Eval(trackPt)) return true; // keep track + + return false; // reject track + } + + ///////////////////////////////////////////// + // Helper functions for event and candidate selection: + template // (TODO: add fillHists and doEventQA capabilities from derivedlambdakzeroanalysis) + bool isEventAccepted(TCollision const& collision){ // check whether the collision passes our collision selections + if (eventSelections.requireSel8 && !collision.sel8()) return false; + if (eventSelections.requireTriggerTVX && !collision.selection_bit(aod::evsel::kIsTriggerTVX)) return false; + if (eventSelections.rejectITSROFBorder && !collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) return false; + if (eventSelections.rejectTFBorder && !collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) return false; + + if (std::abs(collision.posZ()) > eventSelections.maxZVtxPosition) return false; + + if (eventSelections.requireIsVertexITSTPC && !collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) return false; + if (eventSelections.requireIsGoodZvtxFT0VsPV && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) return false; + if (eventSelections.requireIsVertexTOFmatched && !collision.selection_bit(o2::aod::evsel::kIsVertexTOFmatched)) return false; + if (eventSelections.requireIsVertexTRDmatched && !collision.selection_bit(o2::aod::evsel::kIsVertexTRDmatched)) return false; + if (eventSelections.rejectSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) return false; + if (eventSelections.requireNoCollInTimeRangeStd && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) return false; + if (eventSelections.requireNoCollInTimeRangeStrict && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStrict)) return false; + if (eventSelections.requireNoCollInTimeRangeNarrow && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeNarrow)) return false; + if (eventSelections.requireNoCollInROFStd && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) return false; + if (eventSelections.requireNoCollInROFStrict && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStrict)) return false; + + if (doPPAnalysis) { // we are in pp + if (eventSelections.requireINEL0 && collision.multNTracksPVeta1() < 1) return false; + if (eventSelections.requireINEL1 && collision.multNTracksPVeta1() < 2) return false; + } + else { // Performing selections as if in Pb-Pb: + float collisionOccupancy = eventSelections.useFT0CbasedOccupancy ? collision.ft0cOccupancyInTimeRange() : collision.trackOccupancyInTimeRange(); + if (eventSelections.minOccupancy >= 0 && collisionOccupancy < eventSelections.minOccupancy) return false; + if (eventSelections.maxOccupancy >= 0 && collisionOccupancy > eventSelections.maxOccupancy) return false; + + // Fetch interaction rate only if required (in order to limit ccdb calls) + double interactionRate = (eventSelections.minIR >= 0 || eventSelections.maxIR >= 0) ? rateFetcher.fetch(ccdb.service, collision.timestamp(), collision.runNumber(), irSource) * 1.e-3 : -1; + if (eventSelections.minIR >= 0 && interactionRate < eventSelections.minIR) return false; + if (eventSelections.maxIR >= 0 && interactionRate > eventSelections.maxIR) return false; + if (!rctConfigurations.cfgRCTLabel.value.empty() && !rctFlagsChecker(collision)) return false; + } + return true; + } + + + template + inline bool isCandidateForPseudojetAccepted(JetCandidate const& track){ + // ITS/TPC cuts: + if (!track.hasTPC()) return false; // Always demand TPC + if (pseudoJetCandidateTrackSelections.minITSnCls >= 0){ + if (track.itsNCls() < pseudoJetCandidateTrackSelections.minITSnCls) return false; + } + + if (track.tpcNClsCrossedRows() < pseudoJetCandidateTrackSelections.minNCrossedRowsTPC) return false; + + if (track.tpcChi2NCl() > pseudoJetCandidateTrackSelections.maxChi2TPC) return false; + if (track.itsChi2NCl() > pseudoJetCandidateTrackSelections.maxChi2ITS) return false; + + // Kinematics: + const float pt = track.pt(); + if (pt < pseudoJetCandidateTrackSelections.minCandidatePt) return false; + if (std::fabs(track.eta()) > pseudoJetCandidateTrackSelections.etaCut) return false; + + // DCA pseudojet candidate selections: + if (pseudoJetCandidateTrackSelections.doDCAcuts){ + // if (std::fabs(track.dcaXY()) > pseudoJetCandidateTrackSelections.maxDCAxy) return false; + if (std::fabs(track.dcaZ()) > pseudoJetCandidateTrackSelections.maxDCAz) return false; + // 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; + } + return true; + } + + // Lambda selections + // Tests the hypothesis of the V0 being a Lambda or of it being an antiLambda. + // (TODO: implement Armenteros cuts for extra quality control?) + template + bool passesLambdaLambdaBarHypothesis(TV0 const& v0, TCollision const& collision, bool Lambda_hypothesis){ + // Base topological variables (high rejection, low cost checks) + if (v0.v0radius() < v0Selections.v0radius) return false; + if (v0.v0radius() > v0Selections.v0radiusMax) return false; + + const float dcaProtonToPV = isLambda ? std::abs(v0.dcapostopv()) : std::abs(v0.dcanegtopv()); + if (std::fabs(dcaProtonToPV) < v0Selections.dcaProtonToPV) return false; + const float dcaPionToPV = isLambda ? std::abs(v0.dcanegtopv()) : std::abs(v0.dcapostopv()); + if (std::fabs(dcaPionToPV) < v0Selections.dcaPionToPV) return false; + + if (v0.v0cosPA() < v0Selections.v0cospa) return false; + if (v0.dcaV0daughters() > v0Selections.dcav0dau) return false; + + // rapidity + if (std::fabs(v0.yLambda()) > v0Selections.rapidityCut) return false; + + // competing mass rejection + if (std::fabs(v0.mK0Short() - o2::constants::physics::MassK0Short) < v0Selections.compMassRejection) return false; + + const auto posTrackExtra = v0.template posTrackExtra_as(); + const auto negTrackExtra = v0.template negTrackExtra_as(); + + // For the cuts dcapostopv, dcanegtopv and 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 = isLambda ? posTrackExtra : negTrackExtra; + auto const& pionTrack = isLambda ? negTrackExtra : posTrackExtra; + + // ITS quality cuts + bool posIsFromAfterburner = posTrackExtra.hasITSAfterburner(); + bool negIsFromAfterburner = negTrackExtra.hasITSAfterburner(); + + // 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 + if (posTrackExtra.itsChi2NCl() >= v0Selections.maxITSchi2PerNcls) return false; // check maximum ITS chi2 per clusters + if (v0Selections.rejectPosITSafterburner && posIsFromAfterburner) return false; // reject afterburner track or not + if (v0Selections.requirePosITSafterburnerOnly && !posIsFromAfterburner) return false; // keep afterburner track or not + + if (negTrackExtra.itsNCls() < v0Selections.minITSclusters) return false; // check minimum ITS clusters + if (negTrackExtra.itsChi2NCl() >= v0Selections.maxITSchi2PerNcls) return false; // check maximum ITS chi2 per clusters + if (v0Selections.rejectNegITSafterburner && negIsFromAfterburner) return false; // reject afterburner track or not + if (v0Selections.requireNegITSafterburnerOnly && !negIsFromAfterburner) return false; // keep afterburner track or not + + // TPC quality cuts + if (posTrackExtra.tpcCrossedRows() < v0Selections.minTPCrows) return false; // check minimum TPC crossed rows + if (posTrackExtra.tpcChi2NCl() >= v0Selections.maxTPCchi2PerNcls) return false; // check maximum TPC chi2 per clusters + if (posTrackExtra.tpcCrossedRowsOverFindableCls() < v0Selections.minTPCrowsOverFindableClusters) return false; // check minimum fraction of TPC rows over findable + if (posTrackExtra.tpcFoundOverFindableCls() < v0Selections.minTPCfoundOverFindableClusters) return false; // check minimum fraction of found over findable TPC clusters + if (posTrackExtra.tpcFractionSharedCls() >= v0Selections.maxFractionTPCSharedClusters) return false; // check the maximum fraction of allowed shared TPC clusters + if (v0Selections.rejectTPCsectorBoundary && !isTrackFarFromTPCBoundary(v0.positivept(), v0.positivephi(), +1)) return false; // reject track far from TPC sector boundary or not + + if (negTrackExtra.tpcCrossedRows() < v0Selections.minTPCrows) return false; // check minimum TPC crossed rows + if (negTrackExtra.tpcChi2NCl() >= v0Selections.maxTPCchi2PerNcls) return false; // check maximum TPC chi2 per clusters + if (negTrackExtra.tpcCrossedRowsOverFindableCls() < v0Selections.minTPCrowsOverFindableClusters) return false; // check minimum fraction of TPC rows over findable + if (negTrackExtra.tpcFoundOverFindableCls() < v0Selections.minTPCfoundOverFindableClusters) return false; // check minimum fraction of found over findable TPC clusters + if (negTrackExtra.tpcFractionSharedCls() >= v0Selections.maxFractionTPCSharedClusters) return false; // check the maximum fraction of allowed shared TPC clusters + if (v0Selections.rejectTPCsectorBoundary && !isTrackFarFromTPCBoundary(v0.negativept(), v0.negativephi(), -1)) return false; // reject track far from TPC sector boundary or not + + // ITS only tag + if (v0Selections.requirePosITSonly && posTrackExtra.tpcCrossedRows() > 1) return false; + if (v0Selections.requireNegITSonly && negTrackExtra.tpcCrossedRows() > 1) return false; + + // TPC only tag + if (v0Selections.skipTPConly && posTrackExtra.detectorMap() == o2::aod::track::TPC) return false; + if (v0Selections.skipTPConly && negTrackExtra.detectorMap() == o2::aod::track::TPC) return false; + + ///// Expensive PID checks come last: + // TPC PID + if (std::fabs(protonTrack.tpcNSigmaPr()) > v0Selections.tpcPidNsigmaCut) return false; + if (std::fabs(pionTrack.tpcNSigmaPi()) > v0Selections.tpcPidNsigmaCut) return false; + + // TOF PID in DeltaT (if TOF is not available, then uses the track. If is available, uses it. In this sense, TOF is optional) + // const bool posHasTOF = posTrackExtra.hasTOF(); // For the older version, which worked only for Lambdas + const bool protonHasTOF = protonTrack.hasTOF(); + const bool pionHasTOF = pionTrack.hasTOF(); + + // Positive track + if (protonHasTOF && std::abs(isLambda ? v0.posTOFDeltaTLaPr() + : v0.negTOFDeltaTLaPr()) > v0Selections.maxDeltaTimeProton) return false; + // Negative track + // if (pionHasTOF && std::fabs(v0.negTOFDeltaTLaPi()) > v0Selections.maxDeltaTimePion) return false; // Older version, for Lambda only + + // TOF PID in NSigma + // Positive track + if (protonHasTOF && std::fabs(v0.tofNSigmaLaPr()) > v0Selections.tofPidNsigmaCutLaPr) return false; + // Negative track + if (pionHasTOF && std::fabs(v0.tofNSigmaLaPi()) > v0Selections.tofPidNsigmaCutLaPi) return false; + + // proper lifetime + if (v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * o2::constants::physics::MassLambda0 > v0Selections.lambdaLifetimeCut) return false; + } + + template + bool isLambdaCandidate(TV0 const& v0){ + + } + + template + bool isJetAccepted(const Jet& jet){ + + } + + + + + + + void processDataRing(){ + if (!isEventAccepted(collision)) return; + + + + } + + + + + + PROCESS_SWITCH(lambdajetpolarizationions, processDataRing, "Process Ring polarization in Run 3 Data", true); + PROCESS_SWITCH(lambdajetpolarizationions, processMCRing, "Process Ring polarization in Run 3 MC", false); + PROCESS_SWITCH(lambdajetpolarizationions, processJetQAData, "Process Jet QA in Run 3 Data", false); +}; + + + + + + + + From 03b70fc85b975b97ff77bb37eb3166437bd2ab13 Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Sun, 8 Feb 2026 21:45:58 -0300 Subject: [PATCH 07/40] First full version of lambdaJetPolarizationIons. Removed unneeded track table subscriptions, added QA histograms (event and V0 selection), derived data production, optimized duplicate parts on the Lambda vs AntiLambda hypothesis checks, added jet clustering. --- PWGLF/DataModel/lambdaJetPolarizationIons.h | 80 ++ .../Strangeness/lambdaJetPolarizationIons.cxx | 1088 +++++++++++++++-- 2 files changed, 1045 insertions(+), 123 deletions(-) create mode 100644 PWGLF/DataModel/lambdaJetPolarizationIons.h diff --git a/PWGLF/DataModel/lambdaJetPolarizationIons.h b/PWGLF/DataModel/lambdaJetPolarizationIons.h new file mode 100644 index 00000000000..3d109c4910a --- /dev/null +++ b/PWGLF/DataModel/lambdaJetPolarizationIons.h @@ -0,0 +1,80 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \file lambdaJetPolarizationIons.h +/// \brief Derived Data table for Jet-induced polarization analysis (HI) +/// \author Cicero Domenico Muncinelli (cicero.domenico.muncinelli@cern.ch) +// Comments, questions, complaints, suggestions? +// Please write to: +// cicero.domenico.muncinelli@cern.ch +// + +#ifndef PWGLF_DATAMODEL_LAMBDAJETPOL_H_ +#define PWGLF_DATAMODEL_LAMBDAJETPOL_H_ + +#include + +namespace o2::aod +{ + +namespace lambdajetpol +{ + +DECLARE_SOA_COLUMN(CollIdx, collIdx, uint64_t); +DECLARE_SOA_COLUMN(CentFT0M, centFT0M, float); + +DECLARE_SOA_COLUMN(JetPt, jetPt, float); +DECLARE_SOA_COLUMN(JetEta, jetEta, float); +DECLARE_SOA_COLUMN(JetPhi, jetPhi, float); + +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(PosPt, posPt, float); +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); + +} // namespace lambdajetpol + +DECLARE_SOA_TABLE(JetsRing, "AOD", "JETSRING", + lambdajetpol::CollIdx, + lambdajetpol::JetPt, + lambdajetpol::JetEta, + lambdajetpol::JetPhi); + +DECLARE_SOA_TABLE(LambdaLikeV0sRing, "AOD", "LAMBDALIKEV0SRING", + lambdajetpol::CollIdx, + lambdajetpol::CentFT0M, + lambdajetpol::V0Pt, + lambdajetpol::V0Eta, + lambdajetpol::V0Phi, + lambdajetpol::IsLambda, + lambdajetpol::IsAntiLambda, + lambdajetpol::MassLambda, + lambdajetpol::MassAntiLambda, + lambdajetpol::PosPt, + lambdajetpol::PosEta, + lambdajetpol::PosPhi, + lambdajetpol::NegPt, + lambdajetpol::NegEta, + lambdajetpol::NegPhi); +} // namespace o2::aod + +#endif // PWGLF_DATAMODEL_lambdajetpol_H_ \ No newline at end of file diff --git a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIons.cxx b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIons.cxx index fd2d6cc53a3..355d351faf1 100644 --- a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIons.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIons.cxx @@ -17,9 +17,9 @@ // Jet Polarization Ions task // ================ // -// This code loops over a V0Cores table and produces some -// standard analysis output. It is meant to be run over -// derived data. +// This code loops over a V0Cores table and produces standard derived +// data as output. In the post-processing stage, this analysis aims +// to measure the formation of vorticity rings in HI collisions. // // Comments, questions, complaints, suggestions? // Please write to: @@ -56,6 +56,9 @@ #include #include +// Custom data model: +#include "PWGLF/DataModel/lambdaJetPolarizationIons.h" + // Jets: #include #include @@ -105,25 +108,39 @@ using std::array; using namespace o2::aod::rctsel; // Aliases for joined tables: -using SelCollisions = soa::Join; // Estimates centrality from FT0M. Will add other variations for cross-check - // Cross-check with derivedlambdakzeroanalysis' CentEstimator enumerable. It has a lot of centrality estimators! (TODO) -// using SelV0Collisions = soa::Join; // Is this better for event selection? Maybe the pre-ordered centralities are useful? -using DauTracks = soa::Join; +using SelCollisions = soa::Join; +// using DauTracks = soa::Join; + // Actually used subscriptions (smaller memory usage): +using DauTracks = soa::Join; + +using PseudoJetTracks = soa::Join; // Simpler tracks access. (TODO: Should I include TracksIU and TracksCovIU? I don't use any getters from them!) // , aod::pidTOFFullPi, aod::pidTOFFullKa, aod::pidTOFFullPr>; // Not using TOF right now due to some possible mismatches // using SimCollisions = soa::Join; // using DauTracksMC = soa::Join; -// (TODO: check what is gapSide and how to implement related selections) +enum CentEstimator { + kCentFT0C = 0, + kCentFT0M, + kCentFT0CVariant1, + kCentMFT, + kCentNGlobal, + kCentFV0A +}; ////////////////////////////////////////////// struct lambdajetpolarizationions { - // HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - // Define histogram registries - HistogramRegistry registryData{"registryData", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; - HistogramRegistry registryMC{"registryMC", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; - HistogramRegistry registryQC{"registryQC", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + // struct : ProducesGroup { + // } products; + Produces tableV0s; + Produces tableJets; + + // Define histogram registries: + HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + // HistogramRegistry registryData{"registryData", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + // HistogramRegistry registryMC{"registryMC", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + // HistogramRegistry registryQC{"registryQC", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; // master analysis switches Configurable analyseLambda{"analyseLambda", true, "process Lambda-like candidates"}; @@ -131,31 +148,27 @@ struct lambdajetpolarizationions { 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 centralityEstimator{"centralityEstimator", kCentFT0C, "Run 3 centrality estimator (0:CentFT0C, 1:CentFT0M, 2:CentFT0CVariant1, 3:CentMFT, 4:CentNGlobal, 5:CentFV0A)"}; // Default is FT0M - - /////////////////////////////////////////////// - // QA block -- not implemented! (TODO) - // Configurable doEventQA{"doEventQA", false, "do event QA histograms"}; - // Configurable doCompleteTopoQA{"doCompleteTopoQA", false, "do topological variable QA histograms"}; - // Configurable doTPCQA{"doTPCQA", false, "do TPC QA histograms"}; - // Configurable doTOFQA{"doTOFQA", false, "do TOF QA histograms"}; - // Configurable doDetectPropQA{"doDetectPropQA", 0, "do Detector/ITS map QA: 0: no, 1: 4D, 2: 5D with mass; 3: plain in 3D"}; - // Configurable doEtaPhiQA{"doEtaPhiQA", false, "do Eta/Phi QA histograms"}; - - // Configurable doPlainTopoQA{"doPlainTopoQA", true, "do simple 1D QA of candidates"}; - // Configurable qaMinPt{"qaMinPt", 0.0f, "minimum pT for QA plots"}; - // Configurable qaMaxPt{"qaMaxPt", 1000.0f, "maximum pT for QA plots"}; - // Configurable qaCentrality{"qaCentrality", false, "qa centrality flag: check base raw values"}; - /////////////////////////////////////////////// - - /////////////////////////////////////////////// + Configurable centralityEstimator{"centralityEstimator", kCentFT0M, "Run 3 centrality estimator (0:CentFT0C, 1:CentFT0M, 2:CentFT0CVariant1, 3:CentMFT, 4:CentNGlobal, 5:CentFV0A)"}; // Default is FT0M + + ///////////////////////////////////////////// + Configurable doEventQA{"doEventQA", false, "do event QA histograms"}; + Configurable qaCentrality{"qaCentrality", false, "qa centrality flag: check base raw values"}; + Configurable doCompleteTopoQA{"doCompleteTopoQA", false, "do topological variable QA histograms"}; // Includes doPlainTopoQA from derivedlambdakzeroanalysis + 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"}; + ///////////////////////////////////////////// + + // ///////////////////////////////////////////// // MC block -- not implemented! (TODO) // Configurable doMCAssociation{"doMCAssociation", true, "if MC, do MC association"}; // Configurable doTreatPiToMuon{"doTreatPiToMuon", false, "Take pi decay into muon into account in MC"}; // Configurable doCollisionAssociationQA{"doCollisionAssociationQA", true, "check collision association"}; - /////////////////////////////////////////////// + // ///////////////////////////////////////////// - ////////////////////////////////////////////// + // //////////////////////////////////////////// // Manual slice by: (TODO) // SliceCache cache; // Preslice V0perCollision = o2::aod::v0data::collisionId; @@ -164,7 +177,7 @@ struct lambdajetpolarizationions { // Preslice TraCascperCollision = o2::aod::cascdata::collisionId; // Preslice mcParticlePerMcCollision = o2::aod::mcparticle::mcCollisionId; // Preslice udCollisionsPerCollision = o2::aod::udcollision::collisionId; - ///////////////////////////////////////////// + // /////////////////////////////////////////// // Configurable groups: @@ -205,7 +218,7 @@ struct lambdajetpolarizationions { Configurable v0TypeSelection{"v0TypeSelection", 1, "select on a certain V0 type (leave negative if no selection desired)"}; // Selection criteria: acceptance - Configurable rapidityCut{"rapidityCut", 0.5, "rapidity"}; + Configurable rapidityCut{"rapidityCut", 1.0f, "rapidity"}; Configurable daughterEtaCut{"daughterEtaCut", 0.9, "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 @@ -321,9 +334,9 @@ struct lambdajetpolarizationions { ConfigurableAxis axisTPCsignal{"axisTPCsignal", {200, 0.0f, 200.0f}, "TPC signal"}; ConfigurableAxis axisNsigmaTOF{"axisNsigmaTOF", {200, -10.0f, 10.0f}, "N sigma TOF"}; ConfigurableAxis axisTOFdeltaT{"axisTOFdeltaT", {200, -5000.0f, 5000.0f}, "TOF Delta T (ps)"}; - ConfigurableAxis axisPhi{"axisPhi", {18, 0.0f, constants::math::TwoPI}, "Azimuth angle (rad)"}; + ConfigurableAxis axisPhi{"axisPhi", {20, 0.0f, constants::math::TwoPI}, "Azimuth angle (rad)"}; ConfigurableAxis axisPhiMod{"axisPhiMod", {100, 0.0f, constants::math::TwoPI / 18}, "Azimuth angle wrt TPC sector (rad.)"}; - ConfigurableAxis axisEta{"axisEta", {10, -1.0f, 1.0f}, "#eta"}; + ConfigurableAxis axisEta{"axisEta", {20, -1.0f, 1.0f}, "#eta"}; 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"}; @@ -344,20 +357,29 @@ struct lambdajetpolarizationions { // MC coll assoc QA axis ConfigurableAxis axisMonteCarloNch{"axisMonteCarloNch", {300, 0.0f, 3000.0f}, "N_{ch} MC"}; + + // Jet QA axes: + ConfigurableAxis JetsPerEvent{"JetsPerEvent", {20, 0f, 20f}, "Jets per event"}; + + ConfigurableAxis axisCosTheta{"axisCosTheta", {50, -1.f, 1.f}, "cos(#Delta #theta_{jet})"}; + ConfigurableAxis axisDeltaPhi{"axisDeltaPhi", {50, -constants::math::PI, constants::math::PI}, "#Delta #phi"}; + ConfigurableAxis axisDeltaEta{"axisDeltaEta", {50, -1.5f, 1.5f}, "#Delta #phi"}; // Calculated as twice the subtraction "eta_max=0.9 - R_min=0.2", with a margin + ConfigurableAxis axisDeltaR{"axisDeltaR", {50, 0, 3.5f}, "#Delta R"}; // From 0 to about the maximum Delta R possible with R = 0.2 } axisConfigurations; // Jet selection configuration: // (TODO: Add a configurable to select charged jets, neutral jets, full jets, photon-tagged jets and so on) 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 - Configurable radiusJet{"rJet", 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 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 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. Configurable jetAlgorithm{"jetAlgorithm", 2, "jet clustering algorithm. 0 = kT, 1 = C/A, 2 = Anti-kT"}; Configurable jetRecombScheme{"jetRecombScheme", 0, "Jet recombination scheme: E_scheme (0), pT-scheme (1), pt2-scheme (2), WTA_pt_scheme (7)"} // See PWGJE/JetFinders/jetFinder.h for more info. - Configurable bkgSubtractionStrategy{"bkgSubtractionStrategy", 0, "Jet background subtraction: Area (0), Constituent (1)"}; // Selection bool for background subtraction strategy - Configurable jetType{"jetType", 0, "Jet type: Charged Jet (0), Full Jet (1), Photon-tagged (2), Z-tagged (3)"} // (TODO: create a reasonable track selection for photon/Z-tagged jet tracks, including detector angular acceptance parameters) + Configurable bkgSubtraction{"bkgSubtraction", false, "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 jetType{"jetType", 0, "Jet type: Charged Jet (0), Full Jet (1), Photon-tagged (2), Z-tagged (3)"} // (TODO: create a reasonable track selection for full jets and photon/Z-tagged jet tracks, including detector angular acceptance parameters for EMCal) // // Configurables from JE PWG: // // (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!) @@ -392,6 +414,28 @@ struct lambdajetpolarizationions { // Configurable doEMCALEventSelectionChargedJets{"doEMCALEventSelectionChargedJets", false, "apply the selection to the event alias_bit for charged jets"}; } jetConfigurations; + // Creating a short map to make sure the proper FastJet enums are used (safeguard against possible updates in FastJet indices): + fastjet::JetAlgorithm mapFJAlgorithm(int algoIdx){ + switch (algoIdx) { + case 0: return fastjet::kt_algorithm; + case 1: return fastjet::cambridge_algorithm; + case 2: return fastjet::antikt_algorithm; + default: + throw std::invalid_argument("Unknown jet algorithm"); + } + } + fastjet::RecombinationScheme mapFJRecombScheme(int schemeIdx){ + switch (schemeIdx){ + case 0: return fastjet::E_scheme; + case 1: return fastjet::pt_scheme; + case 2: return fastjet::pt2_scheme; + case 7: return fastjet::WTA_pt_scheme; + default: + throw std::invalid_argument("Unknown recombination scheme"); + } + } + + // Track analysis parameters -- A specific group that is different from the v0Selections. In jet analyses we need to control our PseudoJet candidates! // (TODO: include minimal selection criteria for electrons, muons and photons) // Notice you do NOT need any PID for the PseudoJet candidates! Only need is to know the 4-momentum appropriately. Thus removed nsigma checks on PID @@ -399,24 +443,24 @@ struct lambdajetpolarizationions { std::string prefix = "pseudoJetCandidateTrackSelections"; // JSON group name Configurable minNCrossedRowsTPC{"minNCrossedRowsTPC", 70, "Minimum number of TPC crossed rows"}; Configurable minITSnCls{"minITSnCls", -1, "Minimum number of ITS clusters"}; - Configurable maxChi2TPC{"maxChi2TPC", 4.0f, "Maximum chi2 per cluster TPC"}; - Configurable maxChi2ITS{"maxChi2ITS", 36.0f, "Maximum chi2 per cluster ITS"}; + Configurable maxChi2TPC{"maxChi2TPC", 5.0f, "Maximum chi2 per cluster TPC"}; // Loose cuts for pseudojet candidate selection + Configurable maxChi2ITS{"maxChi2ITS", 40.0f, "Maximum chi2 per cluster ITS"}; Configurable etaCut{"etaCut", 0.9f, "Maximum eta absolute value"}; // (TODO: same test as the previous 0.8 eta cut) - Configurable minCandidatePt{"minCandidatePt", 0.15f,"Minimum track pT for pseudojet candidate (GeV/c)"}; // Reduces number of pseudojet candidates from IR radiation + Configurable minCandidatePt{"minCandidatePt", 0.15f,"Minimum track pt for pseudojet candidate (GeV/c)"}; // Reduces number of pseudojet candidates from IR radiation // (TODO: test these minimal ratios to suppress split tracks in high occupancy PbPb or OO) // 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"}; // Jets typical cuts (suppress non-primary candidates): - Configurable doDCAcuts{"doDCAcuts", false, "Apply DCA cuts to jet candidates (use with care in this analysis!)"} - Configurable maxDCAz{"maxDCAz", 3.2f, "Max DCAz to primary vertex [cm]"}; + 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)"}; // Configurable maxDCAxy{"maxDCAxy", 2.4f,"Max DCAxy to primary vertex [cm]"}; // Using same cuts as the StrangenessInJets analysis, with a pt dependence (which may bias high pt, so use with care): - Configurable dcaxyMaxTrackPar0{"dcaxyMaxTrackPar0", 0.0105f, ""}; - Configurable dcaxyMaxTrackPar1{"dcaxyMaxTrackPar1", 0.035f, ""}; - Configurable dcaxyMaxTrackPar2{"dcaxyMaxTrackPar2", 1.1f, ""}; + Configurable dcaxyMaxTrackPar0{"dcaxyMaxTrackPar0", 0.0105f, "Asymptotic DCA resolution at high pt [cm]"}; + Configurable dcaxyMaxTrackPar1{"dcaxyMaxTrackPar1", 0.035f, "Low pt multiple-scattering term for DCA resolution [cm*(GeV/c)^Par2]"}; + Configurable dcaxyMaxTrackPar2{"dcaxyMaxTrackPar2", 1.1f, "Exponent of pt dependence of DCA resolution"}; } pseudoJetCandidateTrackSelections; // struct : ConfigurableGroup { @@ -427,16 +471,17 @@ struct lambdajetpolarizationions { // Instantiate utility class for jet background subtraction JetBkgSubUtils backgroundSub; - // Lambda Ring Polarization axes configurable group: - struct : ConfigurableGroup { - std::string prefix = "ringPolConfigurations"; // JSON group name + // // Lambda Ring Polarization axes configurable group: + // struct : ConfigurableGroup { + // std::string prefix = "ringPolConfigurations"; // JSON group name - } ringPolConfigurations; // (TODO) + // } ringPolConfigurations; // (TODO) - // For manual sliceBy + // Define per-collision preslices for V0s, MC particles, and daughter tracks: Preslice V0perCollision = o2::aod::v0data::collisionId; - Preslice mcParticlePerMcCollision = o2::aod::mcparticle::mcCollisionId; + // Preslice perMCCollision = o2::aod::mcparticle::mcCollisionId; + Preslice perCollisionTrk = o2::aod::track::collisionId; Service pdg; @@ -448,9 +493,410 @@ struct lambdajetpolarizationions { // std::vector genOmegaPlus; void init(InitContext const&){ // (TODO: add all useful histograms here! Add flags for QA plots and the such too) - + // setting CCDB service + ccdb->setURL(ccdbConfigurations.ccdbUrl); + ccdb->setCaching(true); + ccdb->setFatalWhenNull(false); + + // Initialise the RCTFlagsChecker + rctFlagsChecker.init(rctConfigurations.cfgRCTLabel.value, rctConfigurations.cfgCheckZDC, rctConfigurations.cfgTreatLimitedAcceptanceAsBad); + + // Event Counters + histos.add("hEventSelection", "hEventSelection", kTH1D, {{21, -0.5f, +20.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"); + if (doPPAnalysis) { + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(17, "INEL>0"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(18, "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(19, "Below min IR"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(20, "Above max IR"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(21, "RCT flags"); + + histos.add("hEventCentrality", "hEventCentrality", kTH1D, {{101, 0.0f, 101.0f}}); + histos.add("hCentralityVsNch", "hCentralityVsNch", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisNch}); + if (doEventQA) { + histos.add("hEventSelectionVsCentrality", "hEventSelectionVsCentrality", kTH2D, {{21, -0.5f, +20.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"); + if (doPPAnalysis) { + histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(17, "INEL>0"); + histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(18, "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(19, "Below min IR"); + histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(20, "Above max IR"); + histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(21, "RCT flags"); + + histos.add("hCentralityVsNGlobal", "hCentralityVsNGlobal", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisNch}); + histos.add("hEventCentVsMultFT0M", "hEventCentVsMultFT0M", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisMultFT0M}); + histos.add("hEventCentVsMultFT0C", "hEventCentVsMultFT0C", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisMultFT0C}); + histos.add("hEventCentVsMultNGlobal", "hEventCentVsMultNGlobal", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisNch}); + histos.add("hEventCentVsMultFV0A", "hEventCentVsMultFV0A", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisMultFV0A}); + histos.add("hEventMultFT0MvsMultNGlobal", "hEventMultFT0MvsMultNGlobal", kTH2D, {axisConfigurations.axisMultFT0M, axisConfigurations.axisNch}); + histos.add("hEventMultFT0CvsMultNGlobal", "hEventMultFT0CvsMultNGlobal", kTH2D, {axisConfigurations.axisMultFT0C, axisConfigurations.axisNch}); + histos.add("hEventMultFV0AvsMultNGlobal", "hEventMultFV0AvsMultNGlobal", kTH2D, {axisConfigurations.axisMultFV0A, axisConfigurations.axisNch}); + histos.add("hEventMultPVvsMultNGlobal", "hEventMultPVvsMultNGlobal", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisNch}); + histos.add("hEventMultFT0CvsMultFV0A", "hEventMultFT0CvsMultFV0A", 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}}); + + // (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}); + + // for QA and test purposes + auto hRawCentrality = histos.add("hRawCentrality", "hRawCentrality", kTH1D, {axisConfigurations.axisRawCentrality}); + + for (int ii = 1; ii < 101; ii++) { + float value = 100.5f - static_cast(ii); + hRawCentrality->SetBinContent(ii, value); + } + + ////////////////////////////////////////////////////////////// + /// Lambda / AntiLambda V0 selection QA + ////////////////////////////////////////////////////////////// + struct CutLabel{std::string label;bool enabled;}; // A method of hiding labels of selections which were not used! + std::vector v0LambdaSelectionLabels = { + {"All V0 candidates",true}, + {"V0 radius (min)",true},{"V0 radius (max)",true},{"V0 cosPA",true},{"DCA_{V0 daughters}",true},{"|y_{#Lambda}|",v0Selections.rapidityCut>0.f}, + {"K^{0}_{S} mass rejection",v0Selections.compMassRejection>=0.f}, + {"ITS clusters (pos)",v0Selections.minITSclusters>0},{"ITS #chi^{2}/N_{cls} (pos)",v0Selections.maxITSchi2PerNcls<1e8}, + {"Reject ITS afterburner (pos)",v0Selections.rejectPosITSafterburner},{"Require ITS afterburner (pos)",v0Selections.requirePosITSafterburnerOnly}, + {"ITS clusters (neg)",v0Selections.minITSclusters>0},{"ITS #chi^{2}/N_{cls} (neg)",v0Selections.maxITSchi2PerNcls<1e8}, + {"Reject ITS afterburner (neg)",v0Selections.rejectNegITSafterburner},{"Require ITS afterburner (neg)",v0Selections.requireNegITSafterburnerOnly}, + {"TPC crossed rows (pos)",v0Selections.minTPCrows>0},{"TPC #chi^{2}/N_{cls} (pos)",v0Selections.maxTPCchi2PerNcls<1e8}, + {"TPC rows / findable (pos)",v0Selections.minTPCrowsOverFindableClusters>=0},{"TPC found / findable (pos)",v0Selections.minTPCfoundOverFindableClusters>=0}, + {"TPC shared clusters (pos)",v0Selections.maxFractionTPCSharedClusters<1e8},{"TPC sector boundary (pos)",v0Selections.rejectTPCsectorBoundary}, + {"TPC crossed rows (neg)",v0Selections.minTPCrows>0},{"TPC #chi^{2}/N_{cls} (neg)",v0Selections.maxTPCchi2PerNcls<1e8}, + {"TPC rows / findable (neg)",v0Selections.minTPCrowsOverFindableClusters>=0},{"TPC found / findable (neg)",v0Selections.minTPCfoundOverFindableClusters>=0}, + {"TPC shared clusters (neg)",v0Selections.maxFractionTPCSharedClusters<1e8},{"TPC sector boundary (neg)",v0Selections.rejectTPCsectorBoundary}, + {"Require ITS-only (pos)",v0Selections.requirePosITSonly},{"Require ITS-only (neg)",v0Selections.requireNegITSonly}, + {"Reject TPC-only (pos)",v0Selections.skipTPConly},{"Reject TPC-only (neg)",v0Selections.skipTPConly}, + }; // First, the labels that are hypothesis-agnostic + // Adding the Lambda or AntiLambda hypothesis labels as needed: + auto addHypothesis = [&](bool isLambda, bool analysisEnabled) { + if (!analysisEnabled) return; // i.e., don't add these labels if not analyzing said particle type + std::string p = isLambda ? "#Lambda: " : "#bar{#Lambda}: "; + v0LambdaSelectionLabels.insert(v0LambdaSelectionLabels.end(), { + {p + "DCA_{p} to PV", true}, + {p + "DCA_{#pi} to PV", true}, + {p + "TPC PID p", v0Selections.tpcPidNsigmaCut > 0}, + {p + "TPC PID #pi", v0Selections.tpcPidNsigmaCut > 0}, + {p + "TOF #Delta t p", v0Selections.maxDeltaTimeProton < 1e8}, + {p + "TOF #Delta t #pi", v0Selections.maxDeltaTimePion < 1e8}, + {p + "TOF PID p", v0Selections.tofPidNsigmaCutLaPr < 1e8}, + {p + "TOF PID #pi", v0Selections.tofPidNsigmaCutLaPi < 1e8}, + {p + "c#tau", v0Selections.lambdaLifetimeCut > 0} + }); + }; + constexpr bool Lambda = true; // Some constexpr to make it more readable (works at compile level) + constexpr bool AntiLambda = false; + addHypothesis(Lambda, analyseLambda); + addHypothesis(AntiLambda, analyseAntiLambda); + + auto hSelectionV0s = histos.add("GeneralQA/hSelectionV0s","V0 #rightarrow #Lambda / #bar{#Lambda} selection flow",kTH1D, + {{(int)v0LambdaSelectionLabels.size(),-0.5,(double)v0LambdaSelectionLabels.size()-0.5}}); + for(size_t i=0; iGetXaxis()->SetBinLabel(i+1,lbl.c_str()); // First non-underflow bin is bin 1 + } + + // Histograms versus mass: + if (analyseLambda) { + histos.add("h2dNbrOfLambdaVsCentrality", "h2dNbrOfLambdaVsCentrality", kTH2D, {axisConfigurations.axisCentrality, {10, -0.5f, 9.5f}}); + histos.add("h3dMassLambda", "h3dMassLambda", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisLambdaMass}); + // Non-UPC info + histos.add("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}); + 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}); + } + 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}); + } + // (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("h2dNbrOfAntiLambdaVsCentrality", "h2dNbrOfAntiLambdaVsCentrality", kTH2D, {axisConfigurations.axisCentrality, {10, -0.5f, 9.5f}}); + histos.add("h3dMassAntiLambda", "h3dMassAntiLambda", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisLambdaMass}); + // Non-UPC info + histos.add("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}); + 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}); + } + 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}); + } + 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) histos.add("hMassLambda", "hMassLambda", kTH1D, {axisConfigurations.axisLambdaMass}); + if (analyseAntiLambda) histos.add("hMassAntiLambda", "hMassAntiLambda", kTH1D, {axisConfigurations.axisLambdaMass}); + if (analyseLambda && analyseAntiLambda) histos.add("hAmbiguousLambdaCandidates", "hAmbiguousLambdaCandidates", kTH1D, {1, 0.f, 1.f}) + + // QA histograms if requested + 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}); + } + + // For all received candidates: + histos.add("hPosDCAToPV", "hPosDCAToPV", kTH1D, {axisConfigurations.axisDCAtoPV}); + histos.add("hNegDCAToPV", "hNegDCAToPV", kTH1D, {axisConfigurations.axisDCAtoPV}); + histos.add("hDCADaughters", "hDCADaughters", kTH1D, {axisConfigurations.axisDCAdau}); + histos.add("hPointingAngle", "hPointingAngle", kTH1D, {axisConfigurations.axisPointingAngle}); + histos.add("hV0Radius", "hV0Radius", kTH1D, {axisConfigurations.axisV0Radius}); + histos.add("h2dPositiveITSvsTPCpts", "h2dPositiveITSvsTPCpts", kTH2D, {axisConfigurations.axisTPCrows, axisConfigurations.axisITSclus}); + histos.add("h2dNegativeITSvsTPCpts", "h2dNegativeITSvsTPCpts", kTH2D, {axisConfigurations.axisTPCrows, axisConfigurations.axisITSclus}); + histos.add("h2dPositivePtVsPhi", "h2dPositivePtVsPhi", kTH2D, {axisConfigurations.axisPtCoarse, axisConfigurations.axisPhiMod}); + histos.add("h2dNegativePtVsPhi", "h2dNegativePtVsPhi", 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}); + } + 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}); + } + } + + // Check if doing the right thing in AP space please + histos.add("GeneralQA/h2dArmenterosAll", "h2dArmenterosAll", kTH2D, {axisConfigurations.axisAPAlpha, axisConfigurations.axisAPQt}); + histos.add("GeneralQA/h2dArmenterosSelected", "h2dArmenterosSelected", kTH2D, {axisConfigurations.axisAPAlpha, axisConfigurations.axisAPQt}); + + // Jets histograms: + // Histogram that needs to be present even out of QA: + histos.add("hEventsWithJet", "hEventsWithJet", kTH1D, {{1, 0f, 1f}}); + 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 + if (doJetKinematicsQA){ + histos.add("JetKinematicsQA/hJetPt", "hJetPt", kTH1D, {axisConfigurations.axisPt}); + histos.add("JetKinematicsQA/hJetEta", "hJetEta", kTH1D, {axisConfigurations.axisEta}); + histos.add("JetKinematicsQA/hJetPhi", "hJetPhi", kTH1D, {axisConfigurations.axisPhi}); + + 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/hLeadingJetPt", "hLeadingJetPt", kTH1D, {axisConfigurations.axisPt}); + histos.add("JetKinematicsQA/hLeadingJetEta", "hLeadingJetEta", kTH1D, {axisConfigurations.axisEta}); + histos.add("JetKinematicsQA/hLeadingJetPhi", "hLeadingJetPhi", kTH1D, {axisConfigurations.axisPhi}); + + // 2D correlations: + histos.add("JetKinematicsQA/h2dJetsPerEventvsLeadJetPt", "h2dJetsPerEventvsLeadJetPt", kTH2D, {axisConfigurations.JetsPerEvent, axisConfigurations.axisPt}); + histos.add("JetKinematicsQA/h2dJetsPerEventvsJetPt", "h2dJetsPerEventvsJetPt", kTH2D, {axisConfigurations.JetsPerEvent, axisConfigurations.axisPt}); + 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 + } + + // inspect histogram sizes, please + histos.print(); + } + + template + auto getCentrality(TCollision const& collision) + { + if (centralityEstimator == kCentFT0M) return collision.centFT0M(); + else if (centralityEstimator == kCentFT0C) return collision.centFT0C(); + else if (centralityEstimator == kCentFT0CVariant1) return collision.centFT0CVariant1(); + else if (centralityEstimator == kCentMFT) return collision.centMFT(); + else if (centralityEstimator == kCentNGlobal) return collision.centNGlobal(); + else if (centralityEstimator == kCentFV0A) return collision.centFV0A(); + return -1.f; + } + + template + void initCCDB(const& TCollision collision) + { + if (mRunNumber == collision.runNumber()) { + return; + } + + mRunNumber = collision.runNumber(); + + // Fetching magnetic field if requested + if (v0Selections.rejectTPCsectorBoundary) { + // In case override, don't proceed, please - no CCDB access required + if (ccdbConfigurations.useCustomMagField) { + magField = ccdbConfigurations.customMagField; + } else { + grpmag = ccdb->getForRun(ccdbConfigurations.grpmagPath, mRunNumber); + if (!grpmag) { + LOG(fatal) << "Got nullptr from CCDB for path " << ccdbConfigurations.grpmagPath << " of object GRPMagField and " << ccdbConfigurations.grpPath << " of object GRPObject for run " << mRunNumber; + } + // Fetch magnetic field from ccdb for current collision + magField = std::lround(5.f * grpmag->getL3Current() / 30000.f); + LOG(info) << "Retrieved GRP for run " << mRunNumber << " with magnetic field of " << magField << " kZG"; + } + } + } + + // Minimal helper to fill the hSelectionV0s histogram without having to deal with bins by myself + // (CAUTION! If you change selection order, change this too!) + struct V0SelectionFlowCounter{ // Using struct to keep internal bin counter over different functions + int binValue = 0; // Starts at x=0, which is bin 1 in the definition of hSelectionV0s + + void resetForNewV0(){binValue = 0;} + void fill(){histos.fill(HIST("hSelectionV0s"), binValue++);} // Hardcoded hSelectionV0s histogram, as it will not change + }; + V0SelectionFlowCounter V0SelCounter{}; + + // Short inlined helper to simplify QA + inline void fillEventSelectionQA(int& bin, float& centrality){ + histos.fill(HIST("hEventSelection"), bin); + histos.fill(HIST("hEventSelectionVsCentrality"), bin, centrality); + } + + // Fill reconstructed event centrality information + // Based off fillReconstructedEventProperties, but optimized to avoid re-accessing information already present on isEventAccepted! + template + void fillCentralityProperties(TCollision const& collision, float& centrality) + { + if (qaCentrality) { + auto hRawCentrality = histos.get(HIST("hRawCentrality")); + centrality = hRawCentrality->GetBinContent(hRawCentrality->FindBin(doPPAnalysis ? collision.multFT0A() + collision.multFT0C() : collision.multFT0C())); + } + histos.fill(HIST("hEventCentrality"), centrality); + histos.fill(HIST("hCentralityVsNch"), centrality, collision.multNTracksPVeta1()); + if (doEventQA) { + histos.fill(HIST("hCentralityVsNGlobal"), centrality, collision.multNTracksGlobal()); + histos.fill(HIST("hEventCentVsMultFT0M"), collision.centFT0M(), collision.multFT0A() + collision.multFT0C()); + histos.fill(HIST("hEventCentVsMultFT0C"), collision.centFT0C(), collision.multFT0C()); + histos.fill(HIST("hEventCentVsMultNGlobal"), collision.centNGlobal(), collision.multNTracksGlobal()); + histos.fill(HIST("hEventCentVsMultFV0A"), collision.centFV0A(), collision.multFV0A()); + histos.fill(HIST("hEventMultFT0MvsMultNGlobal"), collision.multFT0A() + collision.multFT0C(), collision.multNTracksGlobal()); + histos.fill(HIST("hEventMultFT0CvsMultNGlobal"), collision.multFT0C(), collision.multNTracksGlobal()); + histos.fill(HIST("hEventMultFV0AvsMultNGlobal"), collision.multFV0A(), collision.multNTracksGlobal()); + histos.fill(HIST("hEventMultPVvsMultNGlobal"), collision.multNTracksPVeta1(), collision.multNTracksGlobal()); + histos.fill(HIST("hEventMultFT0CvsMultFV0A"), collision.multFT0C(), collision.multFV0A()); + } + return; } + + ///////////////////////////////////////////// // Computation helper functions: double computePhiMod(double phi, int sign) @@ -481,52 +927,88 @@ struct lambdajetpolarizationions { ///////////////////////////////////////////// // Helper functions for event and candidate selection: template // (TODO: add fillHists and doEventQA capabilities from derivedlambdakzeroanalysis) - bool isEventAccepted(TCollision const& collision){ // check whether the collision passes our collision selections + bool isEventAccepted(TCollision const& collision, float& centrality, 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()) return false; + if (fillHists) fillEventSelectionQA(++selectionIdx, centrality); if (eventSelections.requireTriggerTVX && !collision.selection_bit(aod::evsel::kIsTriggerTVX)) return false; + if (fillHists) fillEventSelectionQA(++selectionIdx, centrality); if (eventSelections.rejectITSROFBorder && !collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) return false; + if (fillHists) fillEventSelectionQA(++selectionIdx, centrality); if (eventSelections.rejectTFBorder && !collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) return false; + if (fillHists) fillEventSelectionQA(++selectionIdx, centrality); - if (std::abs(collision.posZ()) > eventSelections.maxZVtxPosition) return false; + const float collisionPVz = collision.posZ(); + if (std::abs(collisionPVz) > eventSelections.maxZVtxPosition) return false; + if (fillHists) fillEventSelectionQA(++selectionIdx, centrality); if (eventSelections.requireIsVertexITSTPC && !collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) return false; + if (fillHists) fillEventSelectionQA(++selectionIdx, centrality); if (eventSelections.requireIsGoodZvtxFT0VsPV && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) return false; + if (fillHists) fillEventSelectionQA(++selectionIdx, centrality); if (eventSelections.requireIsVertexTOFmatched && !collision.selection_bit(o2::aod::evsel::kIsVertexTOFmatched)) return false; + if (fillHists) fillEventSelectionQA(++selectionIdx, centrality); if (eventSelections.requireIsVertexTRDmatched && !collision.selection_bit(o2::aod::evsel::kIsVertexTRDmatched)) return false; + if (fillHists) fillEventSelectionQA(++selectionIdx, centrality); if (eventSelections.rejectSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) return false; + if (fillHists) fillEventSelectionQA(++selectionIdx, centrality); if (eventSelections.requireNoCollInTimeRangeStd && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) return false; + if (fillHists) fillEventSelectionQA(++selectionIdx, centrality); if (eventSelections.requireNoCollInTimeRangeStrict && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStrict)) return false; + if (fillHists) fillEventSelectionQA(++selectionIdx, centrality); if (eventSelections.requireNoCollInTimeRangeNarrow && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeNarrow)) return false; + if (fillHists) fillEventSelectionQA(++selectionIdx, centrality); if (eventSelections.requireNoCollInROFStd && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) return false; + if (fillHists) fillEventSelectionQA(++selectionIdx, centrality); if (eventSelections.requireNoCollInROFStrict && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStrict)) return false; + if (fillHists) fillEventSelectionQA(++selectionIdx, centrality); if (doPPAnalysis) { // we are in pp if (eventSelections.requireINEL0 && collision.multNTracksPVeta1() < 1) return false; + if (fillHists) fillEventSelectionQA(++selectionIdx, centrality); if (eventSelections.requireINEL1 && collision.multNTracksPVeta1() < 2) return false; + if (fillHists) fillEventSelectionQA(++selectionIdx, centrality); } else { // Performing selections as if in Pb-Pb: - float collisionOccupancy = eventSelections.useFT0CbasedOccupancy ? collision.ft0cOccupancyInTimeRange() : collision.trackOccupancyInTimeRange(); + const float collisionOccupancy = eventSelections.useFT0CbasedOccupancy ? collision.ft0cOccupancyInTimeRange() : collision.trackOccupancyInTimeRange(); if (eventSelections.minOccupancy >= 0 && collisionOccupancy < eventSelections.minOccupancy) return false; + if (fillHists) fillEventSelectionQA(++selectionIdx, centrality); if (eventSelections.maxOccupancy >= 0 && collisionOccupancy > eventSelections.maxOccupancy) return false; + if (fillHists) fillEventSelectionQA(++selectionIdx, centrality); // Fetch interaction rate only if required (in order to limit ccdb calls) - double interactionRate = (eventSelections.minIR >= 0 || eventSelections.maxIR >= 0) ? rateFetcher.fetch(ccdb.service, collision.timestamp(), collision.runNumber(), irSource) * 1.e-3 : -1; + const double interactionRate = (eventSelections.minIR >= 0 || eventSelections.maxIR >= 0) ? rateFetcher.fetch(ccdb.service, collision.timestamp(), collision.runNumber(), irSource) * 1.e-3 : -1; if (eventSelections.minIR >= 0 && interactionRate < eventSelections.minIR) return false; + if (fillHists) fillEventSelectionQA(++selectionIdx, centrality); if (eventSelections.maxIR >= 0 && interactionRate > eventSelections.maxIR) return false; + if (fillHists) fillEventSelectionQA(++selectionIdx, centrality); if (!rctConfigurations.cfgRCTLabel.value.empty() && !rctFlagsChecker(collision)) return false; + if (fillHists) fillEventSelectionQA(++selectionIdx, centrality); + + // Filling histograms previously filled in fillReconstructedEventProperties here, to avoid re-accessing data: + if (fillHists){ + histos.fill(HIST("hEventOccupancy"), collisionOccupancy); + histos.fill(HIST("hCentralityVsOccupancy"), centrality, collisionOccupancy); + histos.fill(HIST("hInteractionRate"), interactionRate); + histos.fill(HIST("hCentralityVsInteractionRate"), centrality, interactionRate); + histos.fill(HIST("hInteractionRateVsOccupancy"), interactionRate, collisionOccupancy); + } + } + + if (fillHists){ + histos.fill(HIST("hCentralityVsPVz"), centrality, collisionPVz); + histos.fill(HIST("hEventPVz"), collisionPVz); } return true; } - template - inline bool isCandidateForPseudojetAccepted(JetCandidate const& track){ + bool isCandidateForChargedPseudojetAccepted(JetCandidate const& track){ // (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 (!track.hasTPC()) return false; // Always demand TPC if (pseudoJetCandidateTrackSelections.minITSnCls >= 0){ if (track.itsNCls() < pseudoJetCandidateTrackSelections.minITSnCls) return false; } - if (track.tpcNClsCrossedRows() < pseudoJetCandidateTrackSelections.minNCrossedRowsTPC) return false; if (track.tpcChi2NCl() > pseudoJetCandidateTrackSelections.maxChi2TPC) return false; @@ -537,7 +1019,7 @@ struct lambdajetpolarizationions { if (pt < pseudoJetCandidateTrackSelections.minCandidatePt) return false; if (std::fabs(track.eta()) > pseudoJetCandidateTrackSelections.etaCut) return false; - // DCA pseudojet candidate selections: + // 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; @@ -548,138 +1030,498 @@ struct lambdajetpolarizationions { return true; } - // Lambda selections - // Tests the hypothesis of the V0 being a Lambda or of it being an antiLambda. - // (TODO: implement Armenteros cuts for extra quality control?) - template - bool passesLambdaLambdaBarHypothesis(TV0 const& v0, TCollision const& collision, bool Lambda_hypothesis){ + // Lambda selections: + template + bool passesGenericV0Cuts(TV0 const& v0){ // (TODO: cache the posTrackExtra and neg track objects outside the V0cuts and hypothesis dependent cuts and then pass them by reference? May be quicker!) // Base topological variables (high rejection, low cost checks) if (v0.v0radius() < v0Selections.v0radius) return false; + V0SelCounter.fill(); if (v0.v0radius() > v0Selections.v0radiusMax) return false; - - const float dcaProtonToPV = isLambda ? std::abs(v0.dcapostopv()) : std::abs(v0.dcanegtopv()); - if (std::fabs(dcaProtonToPV) < v0Selections.dcaProtonToPV) return false; - const float dcaPionToPV = isLambda ? std::abs(v0.dcanegtopv()) : std::abs(v0.dcapostopv()); - if (std::fabs(dcaPionToPV) < v0Selections.dcaPionToPV) return false; - + V0SelCounter.fill(); if (v0.v0cosPA() < v0Selections.v0cospa) return false; + V0SelCounter.fill(); if (v0.dcaV0daughters() > v0Selections.dcav0dau) return false; + V0SelCounter.fill(); - // rapidity - if (std::fabs(v0.yLambda()) > v0Selections.rapidityCut) return false; + // pseudorapidity cuts: + if (std::fabs(v0.yLambda()) > v0Selections.rapidityCut) return false; // Not using rapidity in selections for now. Just detector acceptance. + V0SelCounter.fill(); + // if (std::fabs(v0.eta()) > v0Selections.daughterEtaCut) return false; // (TODO: properly consider this in daughter selection!) - // competing mass rejection + // 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 posTrackExtra_as(); + const auto posTrackExtra = v0.template posTrackExtra_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 negTrackExtra_as(); - // For the cuts dcapostopv, dcanegtopv and 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 = isLambda ? posTrackExtra : negTrackExtra; - auto const& pionTrack = isLambda ? negTrackExtra : posTrackExtra; - // ITS quality cuts bool posIsFromAfterburner = posTrackExtra.hasITSAfterburner(); bool negIsFromAfterburner = negTrackExtra.hasITSAfterburner(); // 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(); if (posTrackExtra.itsChi2NCl() >= v0Selections.maxITSchi2PerNcls) return false; // check maximum ITS chi2 per clusters + V0SelCounter.fill(); if (v0Selections.rejectPosITSafterburner && posIsFromAfterburner) return false; // reject afterburner track or not + V0SelCounter.fill(); if (v0Selections.requirePosITSafterburnerOnly && !posIsFromAfterburner) return false; // keep afterburner track or not + V0SelCounter.fill(); if (negTrackExtra.itsNCls() < v0Selections.minITSclusters) return false; // check minimum ITS clusters + V0SelCounter.fill(); if (negTrackExtra.itsChi2NCl() >= v0Selections.maxITSchi2PerNcls) return false; // check maximum ITS chi2 per clusters + V0SelCounter.fill(); if (v0Selections.rejectNegITSafterburner && negIsFromAfterburner) return false; // reject afterburner track or not + V0SelCounter.fill(); if (v0Selections.requireNegITSafterburnerOnly && !negIsFromAfterburner) return false; // keep afterburner track or not + V0SelCounter.fill(); // TPC quality cuts if (posTrackExtra.tpcCrossedRows() < v0Selections.minTPCrows) return false; // check minimum TPC crossed rows + V0SelCounter.fill(); if (posTrackExtra.tpcChi2NCl() >= v0Selections.maxTPCchi2PerNcls) return false; // check maximum TPC chi2 per clusters + V0SelCounter.fill(); if (posTrackExtra.tpcCrossedRowsOverFindableCls() < v0Selections.minTPCrowsOverFindableClusters) return false; // check minimum fraction of TPC rows over findable + V0SelCounter.fill(); if (posTrackExtra.tpcFoundOverFindableCls() < v0Selections.minTPCfoundOverFindableClusters) return false; // check minimum fraction of found over findable TPC clusters + V0SelCounter.fill(); if (posTrackExtra.tpcFractionSharedCls() >= v0Selections.maxFractionTPCSharedClusters) return false; // check the maximum fraction of allowed shared TPC clusters + V0SelCounter.fill(); if (v0Selections.rejectTPCsectorBoundary && !isTrackFarFromTPCBoundary(v0.positivept(), v0.positivephi(), +1)) return false; // reject track far from TPC sector boundary or not + V0SelCounter.fill(); if (negTrackExtra.tpcCrossedRows() < v0Selections.minTPCrows) return false; // check minimum TPC crossed rows + V0SelCounter.fill(); if (negTrackExtra.tpcChi2NCl() >= v0Selections.maxTPCchi2PerNcls) return false; // check maximum TPC chi2 per clusters + V0SelCounter.fill(); if (negTrackExtra.tpcCrossedRowsOverFindableCls() < v0Selections.minTPCrowsOverFindableClusters) return false; // check minimum fraction of TPC rows over findable + V0SelCounter.fill(); if (negTrackExtra.tpcFoundOverFindableCls() < v0Selections.minTPCfoundOverFindableClusters) return false; // check minimum fraction of found over findable TPC clusters + V0SelCounter.fill(); if (negTrackExtra.tpcFractionSharedCls() >= v0Selections.maxFractionTPCSharedClusters) return false; // check the maximum fraction of allowed shared TPC clusters + V0SelCounter.fill(); if (v0Selections.rejectTPCsectorBoundary && !isTrackFarFromTPCBoundary(v0.negativept(), v0.negativephi(), -1)) return false; // reject track far from TPC sector boundary or not + V0SelCounter.fill(); // ITS only tag if (v0Selections.requirePosITSonly && posTrackExtra.tpcCrossedRows() > 1) return false; + V0SelCounter.fill(); if (v0Selections.requireNegITSonly && negTrackExtra.tpcCrossedRows() > 1) return false; + V0SelCounter.fill(); // TPC only tag if (v0Selections.skipTPConly && posTrackExtra.detectorMap() == o2::aod::track::TPC) return false; + V0SelCounter.fill(); if (v0Selections.skipTPConly && negTrackExtra.detectorMap() == o2::aod::track::TPC) return false; + V0SelCounter.fill(); + + return true; + } + + // (TODO: implement Armenteros cut in later function to distinguish between Lambda or antiLambda) + // Tests the hypothesis of the V0 being a Lambda or of it being an antiLambda. + template + bool passesLambdaLambdaBarHypothesis(TV0 const& v0, TCollision const& collision, bool Lambda_hypothesis){ + // 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()); + 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. + if (dcaPionToPV < v0Selections.dcaPionToPV) return false; + V0SelCounter.fill(); + + const auto posTrackExtra = v0.template posTrackExtra_as(); + const auto negTrackExtra = v0.template negTrackExtra_as(); + + // 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; ///// Expensive PID checks come last: // TPC PID if (std::fabs(protonTrack.tpcNSigmaPr()) > v0Selections.tpcPidNsigmaCut) return false; + V0SelCounter.fill(); if (std::fabs(pionTrack.tpcNSigmaPi()) > v0Selections.tpcPidNsigmaCut) return false; + V0SelCounter.fill(); // TOF PID in DeltaT (if TOF is not available, then uses the track. If is available, uses it. In this sense, TOF is optional) // const bool posHasTOF = posTrackExtra.hasTOF(); // For the older version, which worked only for Lambdas const bool protonHasTOF = protonTrack.hasTOF(); const bool pionHasTOF = pionTrack.hasTOF(); - // Positive track - if (protonHasTOF && std::abs(isLambda ? v0.posTOFDeltaTLaPr() + // Proton-like track + if (protonHasTOF && std::abs(Lambda_hypothesis ? v0.posTOFDeltaTLaPr() : v0.negTOFDeltaTLaPr()) > v0Selections.maxDeltaTimeProton) return false; - // Negative track - // if (pionHasTOF && std::fabs(v0.negTOFDeltaTLaPi()) > v0Selections.maxDeltaTimePion) return false; // Older version, for Lambda only + V0SelCounter.fill(); + // Pion-like track + if (pionHasTOF && std::abs(Lambda_hypothesis ? v0.negTOFDeltaTLaPi() + : v0.posTOFDeltaTLaPi()) > v0Selections.maxDeltaTimePion) return false; + V0SelCounter.fill(); // TOF PID in NSigma - // Positive track + // Proton-like track if (protonHasTOF && std::fabs(v0.tofNSigmaLaPr()) > v0Selections.tofPidNsigmaCutLaPr) return false; - // Negative track + V0SelCounter.fill(); + // Pion-like track if (pionHasTOF && std::fabs(v0.tofNSigmaLaPi()) > v0Selections.tofPidNsigmaCutLaPi) return false; + V0SelCounter.fill(); // proper lifetime if (v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * o2::constants::physics::MassLambda0 > v0Selections.lambdaLifetimeCut) return false; - } - - template - bool isLambdaCandidate(TV0 const& v0){ - - } - - template - bool isJetAccepted(const Jet& jet){ + V0SelCounter.fill(); + return true; } - - - - - - void processDataRing(){ - if (!isEventAccepted(collision)) return; + // (TODO: possible function to distinguish ambiguous candidates that pass both the Lambda_hypothesis true (i.e., a Lambda) or false (i.e., an AntiLambda)) + // template + // bool isCandidateLambda(TV0 const& v0){ + // + // } + void processJetsData(SelCollisions::iterator const& collision, PseudoJetTracks const& tracks){ + float centrality = -1.0f; // Just a placeholder + if (!isEventAccepted(collision, centrality, false)) return; // Uses return instead of continue, as there is no explicit loop here + const uint64_t collIdx = collision.globalIndex(); + if (v0Selections.rejectTPCsectorBoundary) initCCDB(collision); + + // Loop over reconstructed tracks: + std::vector fjParticles; + for (auto const& track : tracks){ + // Require that tracks pass selection criteria + if (!isCandidateForChargedPseudojetAccepted(track)) continue; + + // Constructing pseudojet candidates vector: + // Using pion mass as hypothesis for track energy estimate (before PID, all particles treated as if with the same invariant mass) + // (TODO: study the possibility of using identified PseudoJet candidates for this estimate) + fastjet::PseudoJet candidate(track.px(), track.py(), track.pz(), track.energy(o2::constants::physics::MassPionCharged)); + fjParticles.emplace_back(candidate); + } + // Reject empty events + if (fjParticles.size() < 1) continue; + + // Start jet clusterization: + // Cluster particles using the anti-kt algorithm + fastjet::JetDefinition jetDef(mapFJAlgorithm(jetConfigurations.jetAlgorithm), jetConfigurations.radiusJet, mapFJRecombScheme(jetConfigurations.jetRecombScheme)); + // std::vector jets_pt, jets_eta, jets_phi; // Not worth it to store 4-vectors: the tracks assume pion mass hypothesis, so energy and rapidity are not right. + if (jetConfigurations.bkgSubtraction){ + 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()) continue; + // 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; + // bool hasLeadingJet = false; // Not needed: if the event has any jet, that is the leading jet. Check is superseded by the selectedJets information + float leadingJetPt = -1.f; + 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)) continue; + + auto jetForSub = jet; + // Subtracts same background estimated for highest pt jet, but every jet might have a slightly different area + // (TODO: check possible problems with OO and physics impacts of this particular cone method and choice of single background estimator based on leading jet) + // (TODO: improve for Pb-Pb, specially central!) + fastjet::PseudoJet jetMinusBkg = backgroundSub.doRhoAreaSub(jetForSub, rhoPerp, rhoMPerp); + // Jet pt must be larger than threshold: + if (jetMinusBkg.pt() < jetConfigurations.minJetPt) continue; + selectedJets++; + + // Store jet: + tableJets(collIdx, + jet.pt(), + jet_eta, // Using eta instead of rapidity + jet.phi(), + ); + + // Finding the leading jet after subtraction (leading jet is NOT known a priori!): + if (jetMinusBkg.pt() > leadingJetPt) { + leadingJetPt = jetMinusBkg.pt(); + leadingJetSub = jetMinusBkg; + } + } + histos.fill(HIST("hJetsPerEvent"), selectedJets); + if (selectedJets == 0) continue; + histos.fill(HIST("hEventsWithJet"), 0.5); + + if (doJetKinematicsQA){ + histos.fill(HIST("JetKinematicsQA/hLeadingJetPt"), leadingJetSub.pt()); + 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; + + double cosTheta = leadingJetSub.cos_theta(jetMinusBkg); + double deltaPhi = leadingJetSub.delta_phi_to(jetMinusBkg); + double deltaEta = leadingJetSub.eta() - jetMinusBkg.eta(); + double deltaR = leadingJetSub.delta_R(jetMinusBkg); + + histos.fill(HIST("JetKinematicsQA/hCosThetaToLeadingJet"), cosTheta); + histos.fill(HIST("JetKinematicsQA/hDeltaPhiToLeadingJet"), deltaPhi); + histos.fill(HIST("JetKinematicsQA/hDeltaEtaToLeadingJet"), deltaEta); + histos.fill(HIST("JetKinematicsQA/hDeltaRToLeadingJet"), deltaR); + + // 2D correlations: + histos.fill(HIST("JetKinematicsQA/h2dJetsPerEventvsLeadJetPt"), selectedJets, leadingJetSub.pt()); + histos.fill(HIST("JetKinematicsQA/h2dJetsPerEventvsJetPt"), selectedJets, jet.pt()); + histos.fill(HIST("JetKinematicsQA/h2dCosThetaToLeadvsDeltaPhiToLead"), cosTheta, deltaPhi); + histos.fill(HIST("JetKinematicsQA/h2dCosThetaToLeadvsDeltaEtaToLead"), cosTheta, deltaEta); + histos.fill(HIST("JetKinematicsQA/h2dCosThetaToLeadvsDeltaRToLead"), cosTheta, deltaR); + histos.fill(HIST("JetKinematicsQA/h2dDeltaPhiToLeadvsDeltaEtaToLead"), deltaPhi, deltaEta); + } + } + } + else{ // Otherwise, simple jet clustering + fastjet::ClusterSequence clustSeq(fjParticles, jetDef); + // Jet pt must be larger than threshold: + std::vector jets = fastjet::sorted_by_pt(clustSeq.inclusive_jets(jetConfigurations.minJetPt)); + // INCREMENT JET COUNT + + const int jetsInEvent = jets.size(); + histos.fill(HIST("hJetsPerEvent"), jetsInEvent); // Fills even in empty events, as this is a useful number to know! + + if (jetsInEvent == 0) continue; + histos.fill(HIST("hEventsWithJet"), 0.5); + + 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)) continue; + + // Store jet: + // jets_pt.emplace_back(jet.pt()); + // jets_eta.emplace_back(jet.eta()); + // jets_phi.emplace_back(jet.phi()); // In the [0,2pi) range + tableJets(collIdx + jet.pt(), + jet_eta, // Using eta instead of rapidity + jet.phi() + ); + + if (doJetKinematicsQA){ + histos.fill(HIST("JetKinematicsQA/hJetPt"), jet.pt()); + histos.fill(HIST("JetKinematicsQA/hJetEta"), jet_eta); + histos.fill(HIST("JetKinematicsQA/hJetPhi"), jet.phi()); + + // Calculate angle to leading jet: + double cosTheta = leadingJet.cos_theta(jet); + histos.fill(HIST("JetKinematicsQA/hCosThetaToLeadingJet"), cosTheta); // Measuring the cosine, not angle, because it is faster! + + // Calculate angular separation in projected angles: + double deltaPhi = leadingJet.delta_phi_to(jet); + double deltaEta = leadingJet.eta() - jet_eta; + double deltaR = leadingJet.delta_R(jet); // 2D angular distance in the eta-phi plane + + histos.fill(HIST("JetKinematicsQA/hDeltaPhiToLeadingJet"), deltaPhi); + histos.fill(HIST("JetKinematicsQA/hDeltaEtaToLeadingJet"), deltaEta); + histos.fill(HIST("JetKinematicsQA/hDeltaRToLeadingJet"), deltaR); + + // 2D correlations: + histos.fill(HIST("JetKinematicsQA/h2dJetsPerEventvsLeadJetPt"), jetsInEvent, leadingJet.pt()); + histos.fill(HIST("JetKinematicsQA/h2dJetsPerEventvsJetPt"), jetsInEvent, jet.pt()); + histos.fill(HIST("JetKinematicsQA/h2dCosThetaToLeadvsDeltaPhiToLead"), cosTheta, deltaPhi); + histos.fill(HIST("JetKinematicsQA/h2dCosThetaToLeadvsDeltaEtaToLead"), cosTheta, deltaEta); + histos.fill(HIST("JetKinematicsQA/h2dCosThetaToLeadvsDeltaRToLead"), cosTheta, deltaR); + histos.fill(HIST("JetKinematicsQA/h2dDeltaPhiToLeadvsDeltaEtaToLead"), deltaPhi, deltaEta); + } + } + if (doJetKinematicsQA){ + histos.fill(HIST("JetKinematicsQA/hLeadingJetPt"), leadingJet.pt()); + histos.fill(HIST("JetKinematicsQA/hLeadingJetEta"), leadingJet.eta()); + histos.fill(HIST("JetKinematicsQA/hLeadingJetPhi"), leadingJet.phi()); + } + } } + void processV0sData(SelCollisions::iterator const& collision, aod::V0Datas const& fullV0s){ + const float centrality = getCentrality(collision); + if (doEventQA) { + histos.fill(HIST("hEventSelection"), 0. /* all collisions */); + histos.fill(HIST("hEventSelectionVsCentrality"), 0. /* all collisions */, centrality); + } + if (!isEventAccepted(collision, centrality, true)) return; // Uses return instead of continue, as there is no explicit loop here + const uint64_t collIdx = collision.globalIndex(); + if (v0Selections.rejectTPCsectorBoundary) initCCDB(collision); + + // Quick event QA: + fillReconstructedEventProperties(collision, centrality, collisionOccupancy, interactionRate, gapSide, selGapSide); + + for (auto const& v0 : fullV0s){ + V0SelCounter.resetForNewV0(); + V0SelCounter.fill(); // Fill for all v0 candidates + if (!passesGenericV0Cuts(v0)) continue; + + // fill AP plot for all V0s + if (doArmenterosQA) histos.fill(HIST("GeneralQA/h2dArmenterosAll"), v0.alpha(), v0.qtarm()); + + // Else, just continue the loop: + bool isLambda = false; + bool isAntiLambda = false; + if (analyseLambda) isLambda = passesLambdaLambdaBarHypothesis(v0, collision, true); + if (analyseAntiLambda) isAntiLambda = passesLambdaLambdaBarHypothesis(v0, collision, false); + + if (!isLambda && !isAntiLambda) continue; // Candidate is not considered to be a Lambda (TODO: expand this to a full if block with QA about rejections) + + if (doArmenterosQA) histos.fill(HIST("GeneralQA/h2dArmenterosSelected"), v0.alpha(), v0.qtarm()); // cross-check + if (isLambda && isAntiLambda) histos.fill(HIST("hAmbiguousLambdaCandidates"), 0); + + // // Extra competing mass rejection of Lambdas // (TODO: test competing mass cuts) + // v0.mLambda() + + // Dealing with ambiguous tracks: // (TODO: for now, a simple QA plot to understand how many enter this stage is enough) + + // Saving the Lambdas into a derived data column: + tableV0s(collIdx + centrality, + v0.pt(), + v0.eta(), // Using eta instead of rapidity + v0.phi(), + isLambda, + isAntiLambda, + v0.mLambda(), + v0.mAntiLambda(), + v0.positivept(), + v0.positiveeta(), + v0.positivephi(), + v0.negativept(), + v0.negativeeta(), + v0.negativephi() + ); + + if (doCompleteTopoQA){ + // Remaking these variables outside of the passesLambdaLambdaBarHypothesis. Loses performance, but that should be OK for QA + const auto posTrackExtra = v0.template posTrackExtra_as(); + const auto negTrackExtra = v0.template negTrackExtra_as(); + if (isLambda && analyseLambda) { + histos.fill(HIST("h3dMassLambda"), centrality, pt, v0.mLambda()); + histos.fill(HIST("hMassLambda"), v0.mLambda()); + if (doPlainTopoQA) { + histos.fill(HIST("Lambda/hPosDCAToPV"), v0.dcapostopv()); + histos.fill(HIST("Lambda/hNegDCAToPV"), v0.dcanegtopv()); + histos.fill(HIST("Lambda/hDCADaughters"), v0.dcaV0daughters()); + histos.fill(HIST("Lambda/hPointingAngle"), std::acos(v0.v0cosPA())); + histos.fill(HIST("Lambda/hV0Radius"), v0.v0radius()); + histos.fill(HIST("Lambda/h2dPositiveITSvsTPCpts"), posTrackExtra.tpcCrossedRows(), posTrackExtra.itsNCls()); + histos.fill(HIST("Lambda/h2dNegativeITSvsTPCpts"), negTrackExtra.tpcCrossedRows(), negTrackExtra.itsNCls()); + histos.fill(HIST("Lambda/h2dPositivePtVsPhi"), v0.positivept(), computePhiMod(v0.positivephi(), 1)); + histos.fill(HIST("Lambda/h2dNegativePtVsPhi"), v0.negativept(), computePhiMod(v0.negativephi(), -1)); + } + if (doTPCQA) { + histos.fill(HIST("Lambda/h3dPosNsigmaTPC"), centrality, pt, posTrackExtra.tpcNSigmaPr()); + histos.fill(HIST("Lambda/h3dNegNsigmaTPC"), centrality, pt, negTrackExtra.tpcNSigmaPi()); + histos.fill(HIST("Lambda/h3dPosTPCsignal"), centrality, pt, posTrackExtra.tpcSignal()); + histos.fill(HIST("Lambda/h3dNegTPCsignal"), centrality, pt, negTrackExtra.tpcSignal()); + histos.fill(HIST("Lambda/h3dPosNsigmaTPCvsTrackPtot"), centrality, v0.pfracpos() * v0.p(), posTrackExtra.tpcNSigmaPr()); + histos.fill(HIST("Lambda/h3dNegNsigmaTPCvsTrackPtot"), centrality, v0.pfracneg() * v0.p(), negTrackExtra.tpcNSigmaPi()); + histos.fill(HIST("Lambda/h3dPosTPCsignalVsTrackPtot"), centrality, v0.pfracpos() * v0.p(), posTrackExtra.tpcSignal()); + histos.fill(HIST("Lambda/h3dNegTPCsignalVsTrackPtot"), centrality, v0.pfracneg() * v0.p(), negTrackExtra.tpcSignal()); + histos.fill(HIST("Lambda/h3dPosNsigmaTPCvsTrackPt"), centrality, v0.positivept(), posTrackExtra.tpcNSigmaPr()); + histos.fill(HIST("Lambda/h3dNegNsigmaTPCvsTrackPt"), centrality, v0.negativept(), negTrackExtra.tpcNSigmaPi()); + histos.fill(HIST("Lambda/h3dPosTPCsignalVsTrackPt"), centrality, v0.positivept(), posTrackExtra.tpcSignal()); + histos.fill(HIST("Lambda/h3dNegTPCsignalVsTrackPt"), centrality, v0.negativept(), negTrackExtra.tpcSignal()); + } + if (doTOFQA) { + histos.fill(HIST("Lambda/h3dPosNsigmaTOF"), centrality, pt, v0.tofNSigmaLaPr()); + histos.fill(HIST("Lambda/h3dNegNsigmaTOF"), centrality, pt, v0.tofNSigmaLaPi()); + histos.fill(HIST("Lambda/h3dPosTOFdeltaT"), centrality, pt, v0.posTOFDeltaTLaPr()); + histos.fill(HIST("Lambda/h3dNegTOFdeltaT"), centrality, pt, v0.negTOFDeltaTLaPi()); + histos.fill(HIST("Lambda/h3dPosNsigmaTOFvsTrackPtot"), centrality, v0.pfracpos() * v0.p(), v0.tofNSigmaLaPr()); + histos.fill(HIST("Lambda/h3dNegNsigmaTOFvsTrackPtot"), centrality, v0.pfracneg() * v0.p(), v0.tofNSigmaLaPi()); + histos.fill(HIST("Lambda/h3dPosTOFdeltaTvsTrackPtot"), centrality, v0.pfracpos() * v0.p(), v0.posTOFDeltaTLaPr()); + histos.fill(HIST("Lambda/h3dNegTOFdeltaTvsTrackPtot"), centrality, v0.pfracneg() * v0.p(), v0.negTOFDeltaTLaPi()); + histos.fill(HIST("Lambda/h3dPosNsigmaTOFvsTrackPt"), centrality, v0.positivept(), v0.tofNSigmaLaPr()); + histos.fill(HIST("Lambda/h3dNegNsigmaTOFvsTrackPt"), centrality, v0.negativept(), v0.tofNSigmaLaPi()); + histos.fill(HIST("Lambda/h3dPosTOFdeltaTvsTrackPt"), centrality, v0.positivept(), v0.posTOFDeltaTLaPr()); + histos.fill(HIST("Lambda/h3dNegTOFdeltaTvsTrackPt"), centrality, v0.negativept(), v0.negTOFDeltaTLaPi()); + } + if (doEtaPhiQA) { + histos.fill(HIST("Lambda/h5dV0PhiVsEta"), centrality, pt, 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("h3dMassAntiLambda"), centrality, pt, v0.mAntiLambda()); + histos.fill(HIST("hMassAntiLambda"), v0.mAntiLambda()); + if (doPlainTopoQA) { + histos.fill(HIST("AntiLambda/hPosDCAToPV"), v0.dcapostopv()); + histos.fill(HIST("AntiLambda/hNegDCAToPV"), v0.dcanegtopv()); + histos.fill(HIST("AntiLambda/hDCADaughters"), v0.dcaV0daughters()); + histos.fill(HIST("AntiLambda/hPointingAngle"), std::acos(v0.v0cosPA())); + histos.fill(HIST("AntiLambda/hV0Radius"), v0.v0radius()); + histos.fill(HIST("AntiLambda/h2dPositiveITSvsTPCpts"), posTrackExtra.tpcCrossedRows(), posTrackExtra.itsNCls()); + histos.fill(HIST("AntiLambda/h2dNegativeITSvsTPCpts"), negTrackExtra.tpcCrossedRows(), negTrackExtra.itsNCls()); + histos.fill(HIST("AntiLambda/h2dPositivePtVsPhi"), v0.positivept(), computePhiMod(v0.positivephi(), 1)); + histos.fill(HIST("AntiLambda/h2dNegativePtVsPhi"), v0.negativept(), computePhiMod(v0.negativephi(), -1)); + } + if (doTPCQA) { + histos.fill(HIST("AntiLambda/h3dPosNsigmaTPC"), centrality, pt, posTrackExtra.tpcNSigmaPi()); + histos.fill(HIST("AntiLambda/h3dNegNsigmaTPC"), centrality, pt, negTrackExtra.tpcNSigmaPr()); + histos.fill(HIST("AntiLambda/h3dPosTPCsignal"), centrality, pt, posTrackExtra.tpcSignal()); + histos.fill(HIST("AntiLambda/h3dNegTPCsignal"), centrality, pt, negTrackExtra.tpcSignal()); + histos.fill(HIST("AntiLambda/h3dPosNsigmaTPCvsTrackPtot"), centrality, v0.pfracpos() * v0.p(), posTrackExtra.tpcNSigmaPi()); + histos.fill(HIST("AntiLambda/h3dNegNsigmaTPCvsTrackPtot"), centrality, v0.pfracneg() * v0.p(), negTrackExtra.tpcNSigmaPr()); + histos.fill(HIST("AntiLambda/h3dPosTPCsignalVsTrackPtot"), centrality, v0.pfracpos() * v0.p(), posTrackExtra.tpcSignal()); + histos.fill(HIST("AntiLambda/h3dNegTPCsignalVsTrackPtot"), centrality, v0.pfracneg() * v0.p(), negTrackExtra.tpcSignal()); + histos.fill(HIST("AntiLambda/h3dPosNsigmaTPCvsTrackPt"), centrality, v0.positivept(), posTrackExtra.tpcNSigmaPi()); + histos.fill(HIST("AntiLambda/h3dNegNsigmaTPCvsTrackPt"), centrality, v0.negativept(), negTrackExtra.tpcNSigmaPr()); + histos.fill(HIST("AntiLambda/h3dPosTPCsignalVsTrackPt"), centrality, v0.positivept(), posTrackExtra.tpcSignal()); + histos.fill(HIST("AntiLambda/h3dNegTPCsignalVsTrackPt"), centrality, v0.negativept(), negTrackExtra.tpcSignal()); + } + if (doTOFQA) { + histos.fill(HIST("AntiLambda/h3dPosNsigmaTOF"), centrality, pt, v0.tofNSigmaALaPi()); + histos.fill(HIST("AntiLambda/h3dNegNsigmaTOF"), centrality, pt, v0.tofNSigmaALaPr()); + histos.fill(HIST("AntiLambda/h3dPosTOFdeltaT"), centrality, pt, v0.posTOFDeltaTLaPi()); + histos.fill(HIST("AntiLambda/h3dNegTOFdeltaT"), centrality, pt, v0.negTOFDeltaTLaPr()); + histos.fill(HIST("AntiLambda/h3dPosNsigmaTOFvsTrackPtot"), centrality, v0.pfracpos() * v0.p(), v0.tofNSigmaALaPi()); + histos.fill(HIST("AntiLambda/h3dNegNsigmaTOFvsTrackPtot"), centrality, v0.pfracneg() * v0.p(), v0.tofNSigmaALaPr()); + histos.fill(HIST("AntiLambda/h3dPosTOFdeltaTvsTrackPtot"), centrality, v0.pfracpos() * v0.p(), v0.posTOFDeltaTLaPi()); + histos.fill(HIST("AntiLambda/h3dNegTOFdeltaTvsTrackPtot"), centrality, v0.pfracneg() * v0.p(), v0.negTOFDeltaTLaPr()); + histos.fill(HIST("AntiLambda/h3dPosNsigmaTOFvsTrackPt"), centrality, v0.positivept(), v0.tofNSigmaALaPi()); + histos.fill(HIST("AntiLambda/h3dNegNsigmaTOFvsTrackPt"), centrality, v0.negativept(), v0.tofNSigmaALaPr()); + histos.fill(HIST("AntiLambda/h3dPosTOFdeltaTvsTrackPt"), centrality, v0.positivept(), v0.posTOFDeltaTLaPi()); + histos.fill(HIST("AntiLambda/h3dNegTOFdeltaTvsTrackPt"), centrality, v0.negativept(), v0.negTOFDeltaTLaPr()); + } + if (doEtaPhiQA) { + histos.fill(HIST("AntiLambda/h5dV0PhiVsEta"), centrality, pt, 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 + } + } - - - - PROCESS_SWITCH(lambdajetpolarizationions, processDataRing, "Process Ring polarization in Run 3 Data", true); - PROCESS_SWITCH(lambdajetpolarizationions, processMCRing, "Process Ring polarization in Run 3 MC", false); - PROCESS_SWITCH(lambdajetpolarizationions, processJetQAData, "Process Jet QA in Run 3 Data", false); + PROCESS_SWITCH(lambdajetpolarizationions, processJetsData, "Process jets and produced derived data in Run 3 Data", true); + PROCESS_SWITCH(lambdajetpolarizationions, processV0sData, "Process V0s and produce derived data in Run 3 Data", true); + // PROCESS_SWITCH(lambdajetpolarizationions, processJetsMC, "Process jets and produced derived data in Run 3 MC", true); + // PROCESS_SWITCH(lambdajetpolarizationions, processV0sMC, "Process V0s and produce derived data in Run 3 MC", true); }; - - - - - - - +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc)}; +} \ No newline at end of file From c5ed51a7f388f4c058c2a72168e8763148cc5fed Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Sun, 8 Feb 2026 21:52:00 -0300 Subject: [PATCH 08/40] Fixing CMakeLists --- PWGLF/Tasks/Strangeness/CMakeLists.txt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/PWGLF/Tasks/Strangeness/CMakeLists.txt b/PWGLF/Tasks/Strangeness/CMakeLists.txt index 3b65c3cf5d5..ca2c5ac2187 100644 --- a/PWGLF/Tasks/Strangeness/CMakeLists.txt +++ b/PWGLF/Tasks/Strangeness/CMakeLists.txt @@ -99,11 +99,6 @@ o2physics_add_dpl_workflow(lambdapolarization PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) -o2physics_add_dpl_workflow(lambdainvmasstest - SOURCES lambdaInvMassTest.cxx - PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore O2Physics::AnalysisCCDB - COMPONENT_NAME Analysis) - o2physics_add_dpl_workflow(lambdapolsp SOURCES lambdapolsp.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore @@ -171,6 +166,11 @@ o2physics_add_dpl_workflow(lambdajetpolarization PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGJECore FastJet::FastJet FastJet::Contrib O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(lambdajetpolarizationions +SOURCES lambdaJetPolarizationIons.cxx +PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGJECore FastJet::FastJet FastJet::Contrib O2Physics::EventFilteringUtils O2Physics::AnalysisCCDB +COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(lambdaspincorrderived SOURCES lambdaspincorrderived.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore From 9bb3538541a152d47b831bb4a85e96693ef69521 Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Mon, 9 Feb 2026 15:37:05 -0300 Subject: [PATCH 09/40] Fixing compilation errors --- .../Strangeness/lambdaJetPolarizationIons.cxx | 214 +++++++++--------- 1 file changed, 111 insertions(+), 103 deletions(-) diff --git a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIons.cxx b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIons.cxx index 355d351faf1..100358b0c99 100644 --- a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIons.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIons.cxx @@ -32,6 +32,7 @@ #include "PWGJE/DataModel/Jet.h" #include "PWGJE/DataModel/JetReducedData.h" #include "PWGLF/DataModel/LFStrangenessTables.h" +#include "PWGLF/DataModel/LFStrangenessPIDTables.h" // For V0TOFPIDs and NSigmas getters #include "PWGLF/DataModel/mcCentrality.h" #include "Common/DataModel/Centrality.h" @@ -39,8 +40,8 @@ #include "Common/Core/TrackSelection.h" #include "Common/Core/trackUtilities.h" #include "Common/DataModel/EventSelection.h" -// #include "Common/DataModel/Multiplicity.h" -// #include "Common/DataModel/PIDResponseTOF.h" // Not using right now +#include "Common/DataModel/Multiplicity.h" // for pp +// #include "Common/DataModel/PIDResponseTOF.h" // Maybe switch this around with LFStrangenessPIDTables? #include "Common/DataModel/McCollisionExtra.h" #include "Common/DataModel/PIDResponseTPC.h" @@ -49,6 +50,7 @@ #include #include +#include "DataFormatsParameters/GRPMagField.h" #include #include #include @@ -108,11 +110,13 @@ using std::array; using namespace o2::aod::rctsel; // Aliases for joined tables: -using SelCollisions = soa::Join; +using SelCollisions = soa::Join; // Added PVMults to get MultNTracksPVeta1 as centrality estimator // using DauTracks = soa::Join; // Actually used subscriptions (smaller memory usage): -using DauTracks = soa::Join; +using DauTracks = soa::Join; +using V0Candidates = soa::Join; using PseudoJetTracks = soa::Join; // Simpler tracks access. (TODO: Should I include TracksIU and TracksCovIU? I don't use any getters from them!) // , aod::pidTOFFullPi, aod::pidTOFFullKa, aod::pidTOFFullPr>; // Not using TOF right now due to some possible mismatches @@ -231,7 +235,7 @@ struct lambdajetpolarizationions { 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 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)"}; // invariant mass selection Configurable compMassRejection{"compMassRejection", -1, "Competing mass rejection (GeV/#it{c}^{2})"}; // This was creating some bumps in Youpeng's inv mass spectra. Turned off for now. @@ -318,8 +322,8 @@ struct lambdajetpolarizationions { 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"}; // (TODO) - // ConfigurableAxis axisMultFV0A{"axisMultFV0A", {500, 0.0f, +100000.0f}, "Multiplicity FV0A"}; + 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 @@ -359,7 +363,7 @@ struct lambdajetpolarizationions { ConfigurableAxis axisMonteCarloNch{"axisMonteCarloNch", {300, 0.0f, 3000.0f}, "N_{ch} MC"}; // Jet QA axes: - ConfigurableAxis JetsPerEvent{"JetsPerEvent", {20, 0f, 20f}, "Jets per event"}; + ConfigurableAxis JetsPerEvent{"JetsPerEvent", {20, 0, 20}, "Jets per event"}; ConfigurableAxis axisCosTheta{"axisCosTheta", {50, -1.f, 1.f}, "cos(#Delta #theta_{jet})"}; ConfigurableAxis axisDeltaPhi{"axisDeltaPhi", {50, -constants::math::PI, constants::math::PI}, "#Delta #phi"}; @@ -376,10 +380,10 @@ struct lambdajetpolarizationions { // 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", 2, "jet clustering algorithm. 0 = kT, 1 = C/A, 2 = Anti-kT"}; - Configurable jetRecombScheme{"jetRecombScheme", 0, "Jet recombination scheme: E_scheme (0), pT-scheme (1), pt2-scheme (2), WTA_pt_scheme (7)"} // See PWGJE/JetFinders/jetFinder.h for more info. + Configurable jetRecombScheme{"jetRecombScheme", 0, "Jet recombination scheme: E_scheme (0), pT-scheme (1), pt2-scheme (2), WTA_pt_scheme (7)"}; // See PWGJE/JetFinders/jetFinder.h for more info. Configurable bkgSubtraction{"bkgSubtraction", false, "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 jetType{"jetType", 0, "Jet type: Charged Jet (0), Full Jet (1), Photon-tagged (2), Z-tagged (3)"} // (TODO: create a reasonable track selection for full jets and photon/Z-tagged jet tracks, including detector angular acceptance parameters for EMCal) + 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 jetType{"jetType", 0, "Jet type: Charged Jet (0), Full Jet (1), Photon-tagged (2), Z-tagged (3)"}; // (TODO: create a reasonable track selection for full jets and photon/Z-tagged jet tracks, including detector angular acceptance parameters for EMCal) // // Configurables from JE PWG: // // (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!) @@ -453,7 +457,7 @@ struct lambdajetpolarizationions { // Configurable minTPCfoundOverFindableClusters{"minTPCfoundOverFindableClusters", 0.8f, "minimum nbr of found over findable TPC clusters"}; // 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 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)"}; // Configurable maxDCAxy{"maxDCAxy", 2.4f,"Max DCAxy to primary vertex [cm]"}; @@ -483,7 +487,7 @@ struct lambdajetpolarizationions { // Preslice perMCCollision = o2::aod::mcparticle::mcCollisionId; Preslice perCollisionTrk = o2::aod::track::collisionId; - Service pdg; + // Service pdg; // std::vector genLambda; // std::vector genAntiLambda; @@ -725,7 +729,7 @@ struct lambdajetpolarizationions { if (analyseLambda) histos.add("hMassLambda", "hMassLambda", kTH1D, {axisConfigurations.axisLambdaMass}); if (analyseAntiLambda) histos.add("hMassAntiLambda", "hMassAntiLambda", kTH1D, {axisConfigurations.axisLambdaMass}); - if (analyseLambda && analyseAntiLambda) histos.add("hAmbiguousLambdaCandidates", "hAmbiguousLambdaCandidates", kTH1D, {1, 0.f, 1.f}) + if (analyseLambda && analyseAntiLambda) histos.add("hAmbiguousLambdaCandidates", "hAmbiguousLambdaCandidates", kTH1D, {{1, 0, 1}}); // QA histograms if requested if (doCompleteTopoQA) { @@ -784,7 +788,7 @@ struct lambdajetpolarizationions { // Jets histograms: // Histogram that needs to be present even out of QA: - histos.add("hEventsWithJet", "hEventsWithJet", kTH1D, {{1, 0f, 1f}}); + 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 @@ -828,7 +832,7 @@ struct lambdajetpolarizationions { } template - void initCCDB(const& TCollision collision) + void initCCDB(TCollision const& collision) { if (mRunNumber == collision.runNumber()) { return; @@ -838,18 +842,19 @@ struct lambdajetpolarizationions { // Fetching magnetic field if requested if (v0Selections.rejectTPCsectorBoundary) { - // In case override, don't proceed, please - no CCDB access required - if (ccdbConfigurations.useCustomMagField) { - magField = ccdbConfigurations.customMagField; - } else { - grpmag = ccdb->getForRun(ccdbConfigurations.grpmagPath, mRunNumber); - if (!grpmag) { - LOG(fatal) << "Got nullptr from CCDB for path " << ccdbConfigurations.grpmagPath << " of object GRPMagField and " << ccdbConfigurations.grpPath << " of object GRPObject for run " << mRunNumber; + // In case override, don't proceed, please - no CCDB access required + if (ccdbConfigurations.useCustomMagField) { + magField = ccdbConfigurations.customMagField; + } + else { + grpmag = ccdb->getForRun(ccdbConfigurations.grpmagPath, mRunNumber); + if (!grpmag) { + LOG(fatal) << "Got nullptr from CCDB for path " << ccdbConfigurations.grpmagPath << " of object GRPMagField and " << ccdbConfigurations.grpPath << " of object GRPObject for run " << mRunNumber; + } + // Fetch magnetic field from ccdb for current collision + magField = std::lround(5.f * grpmag->getL3Current() / 30000.f); + LOG(info) << "Retrieved GRP for run " << mRunNumber << " with magnetic field of " << magField << " kZG"; } - // Fetch magnetic field from ccdb for current collision - magField = std::lround(5.f * grpmag->getL3Current() / 30000.f); - LOG(info) << "Retrieved GRP for run " << mRunNumber << " with magnetic field of " << magField << " kZG"; - } } } @@ -857,14 +862,15 @@ struct lambdajetpolarizationions { // (CAUTION! If you change selection order, change this too!) struct V0SelectionFlowCounter{ // Using struct to keep internal bin counter over different functions int binValue = 0; // Starts at x=0, which is bin 1 in the definition of hSelectionV0s + 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 = 0;} - void fill(){histos.fill(HIST("hSelectionV0s"), binValue++);} // Hardcoded hSelectionV0s histogram, as it will not change + void fill(){histos->fill(HIST("GeneralQA/hSelectionV0s"), ++binValue);} // Hardcoded hSelectionV0s histogram, as it will not change. Increments before filling, by default }; - V0SelectionFlowCounter V0SelCounter{}; + V0SelectionFlowCounter V0SelCounter{0, &histos}; // Short inlined helper to simplify QA - inline void fillEventSelectionQA(int& bin, float& centrality){ + inline void fillEventSelectionQA(int bin, float centrality){ histos.fill(HIST("hEventSelection"), bin); histos.fill(HIST("hEventSelectionVsCentrality"), bin, centrality); } @@ -872,7 +878,7 @@ struct lambdajetpolarizationions { // Fill reconstructed event centrality information // Based off fillReconstructedEventProperties, but optimized to avoid re-accessing information already present on isEventAccepted! template - void fillCentralityProperties(TCollision const& collision, float& centrality) + void fillCentralityProperties(TCollision const& collision, float centrality) { if (qaCentrality) { auto hRawCentrality = histos.get(HIST("hRawCentrality")); @@ -924,10 +930,17 @@ struct lambdajetpolarizationions { return false; // reject track } + inline double cosThetaJets(const fastjet::PseudoJet& a, const fastjet::PseudoJet& b){ + const double dot = a.px() * b.px() + a.py() * b.py() + a.pz() * b.pz(); + const double magA = std::sqrt(a.px()*a.px() + a.py()*a.py() + a.pz()*a.pz()); + const double magB = std::sqrt(b.px()*b.px() + b.py()*b.py() + b.pz()*b.pz()); + return dot / (magA * magB); + } + ///////////////////////////////////////////// // Helper functions for event and candidate selection: template // (TODO: add fillHists and doEventQA capabilities from derivedlambdakzeroanalysis) - bool isEventAccepted(TCollision const& collision, float& centrality, bool& fillHists){ // check whether the collision passes our collision selections + bool isEventAccepted(TCollision const& collision, float centrality, 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()) return false; if (fillHists) fillEventSelectionQA(++selectionIdx, centrality); @@ -1056,8 +1069,8 @@ struct lambdajetpolarizationions { const auto negTrackExtra = v0.template negTrackExtra_as(); // ITS quality cuts - bool posIsFromAfterburner = posTrackExtra.hasITSAfterburner(); - bool negIsFromAfterburner = negTrackExtra.hasITSAfterburner(); + bool posIsFromAfterburner = posTrackExtra.isITSAfterburner(); + bool negIsFromAfterburner = negTrackExtra.isITSAfterburner(); // 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 @@ -1079,7 +1092,7 @@ struct lambdajetpolarizationions { V0SelCounter.fill(); // TPC quality cuts - if (posTrackExtra.tpcCrossedRows() < v0Selections.minTPCrows) return false; // check minimum TPC crossed rows + if (posTrackExtra.tpcNClsCrossedRows() < v0Selections.minTPCrows) return false; // check minimum TPC crossed rows V0SelCounter.fill(); if (posTrackExtra.tpcChi2NCl() >= v0Selections.maxTPCchi2PerNcls) return false; // check maximum TPC chi2 per clusters V0SelCounter.fill(); @@ -1092,7 +1105,7 @@ struct lambdajetpolarizationions { if (v0Selections.rejectTPCsectorBoundary && !isTrackFarFromTPCBoundary(v0.positivept(), v0.positivephi(), +1)) return false; // reject track far from TPC sector boundary or not V0SelCounter.fill(); - if (negTrackExtra.tpcCrossedRows() < v0Selections.minTPCrows) return false; // check minimum TPC crossed rows + if (negTrackExtra.tpcNClsCrossedRows() < v0Selections.minTPCrows) return false; // check minimum TPC crossed rows V0SelCounter.fill(); if (negTrackExtra.tpcChi2NCl() >= v0Selections.maxTPCchi2PerNcls) return false; // check maximum TPC chi2 per clusters V0SelCounter.fill(); @@ -1106,9 +1119,9 @@ struct lambdajetpolarizationions { V0SelCounter.fill(); // ITS only tag - if (v0Selections.requirePosITSonly && posTrackExtra.tpcCrossedRows() > 1) return false; + if (v0Selections.requirePosITSonly && posTrackExtra.tpcNClsCrossedRows() > 1) return false; V0SelCounter.fill(); - if (v0Selections.requireNegITSonly && negTrackExtra.tpcCrossedRows() > 1) return false; + if (v0Selections.requireNegITSonly && negTrackExtra.tpcNClsCrossedRows() > 1) return false; V0SelCounter.fill(); // TPC only tag @@ -1208,7 +1221,7 @@ struct lambdajetpolarizationions { fjParticles.emplace_back(candidate); } // Reject empty events - if (fjParticles.size() < 1) continue; + if (fjParticles.size() < 1) return; // Start jet clusterization: // Cluster particles using the anti-kt algorithm @@ -1218,7 +1231,7 @@ struct lambdajetpolarizationions { 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()) continue; + if (jets.empty()) return; // 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! @@ -1244,9 +1257,9 @@ struct lambdajetpolarizationions { // Store jet: tableJets(collIdx, - jet.pt(), - jet_eta, // Using eta instead of rapidity - jet.phi(), + jetMinusBkg.pt(), + jetMinusBkg.eta(), // Using eta instead of rapidity + jetMinusBkg.phi() ); // Finding the leading jet after subtraction (leading jet is NOT known a priori!): @@ -1256,7 +1269,7 @@ struct lambdajetpolarizationions { } } histos.fill(HIST("hJetsPerEvent"), selectedJets); - if (selectedJets == 0) continue; + if (selectedJets == 0) return; histos.fill(HIST("hEventsWithJet"), 0.5); if (doJetKinematicsQA){ @@ -1272,10 +1285,10 @@ struct lambdajetpolarizationions { if (jetMinusBkg.pt() < jetConfigurations.minJetPt) continue; - double cosTheta = leadingJetSub.cos_theta(jetMinusBkg); - double deltaPhi = leadingJetSub.delta_phi_to(jetMinusBkg); + double cosTheta = cosThetaJets(leadingJetSub, jetMinusBkg); + double deltaPhi = leadingJetSub.phi() - jetMinusBkg.phi(); double deltaEta = leadingJetSub.eta() - jetMinusBkg.eta(); - double deltaR = leadingJetSub.delta_R(jetMinusBkg); + double deltaR = std::sqrt(deltaPhi*deltaPhi + deltaEta*deltaEta); histos.fill(HIST("JetKinematicsQA/hCosThetaToLeadingJet"), cosTheta); histos.fill(HIST("JetKinematicsQA/hDeltaPhiToLeadingJet"), deltaPhi); @@ -1301,7 +1314,7 @@ struct lambdajetpolarizationions { const int jetsInEvent = jets.size(); histos.fill(HIST("hJetsPerEvent"), jetsInEvent); // Fills even in empty events, as this is a useful number to know! - if (jetsInEvent == 0) continue; + if (jetsInEvent == 0) return; histos.fill(HIST("hEventsWithJet"), 0.5); const auto& leadingJet = jets[0]; @@ -1314,7 +1327,7 @@ struct lambdajetpolarizationions { // jets_pt.emplace_back(jet.pt()); // jets_eta.emplace_back(jet.eta()); // jets_phi.emplace_back(jet.phi()); // In the [0,2pi) range - tableJets(collIdx + tableJets(collIdx, jet.pt(), jet_eta, // Using eta instead of rapidity jet.phi() @@ -1326,14 +1339,14 @@ struct lambdajetpolarizationions { histos.fill(HIST("JetKinematicsQA/hJetPhi"), jet.phi()); // Calculate angle to leading jet: - double cosTheta = leadingJet.cos_theta(jet); - histos.fill(HIST("JetKinematicsQA/hCosThetaToLeadingJet"), cosTheta); // Measuring the cosine, not angle, because it is faster! + double cosTheta = cosThetaJets(leadingJet, jet); // Calculate angular separation in projected angles: - double deltaPhi = leadingJet.delta_phi_to(jet); + double deltaPhi = leadingJet.phi() - jet.phi(); double deltaEta = leadingJet.eta() - jet_eta; - double deltaR = leadingJet.delta_R(jet); // 2D angular distance in the eta-phi plane + double 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! histos.fill(HIST("JetKinematicsQA/hDeltaPhiToLeadingJet"), deltaPhi); histos.fill(HIST("JetKinematicsQA/hDeltaEtaToLeadingJet"), deltaEta); histos.fill(HIST("JetKinematicsQA/hDeltaRToLeadingJet"), deltaR); @@ -1355,19 +1368,17 @@ struct lambdajetpolarizationions { } } - void processV0sData(SelCollisions::iterator const& collision, aod::V0Datas const& fullV0s){ + void processV0sData(SelCollisions::iterator const& collision, V0Candidates const& fullV0s){ const float centrality = getCentrality(collision); if (doEventQA) { histos.fill(HIST("hEventSelection"), 0. /* all collisions */); histos.fill(HIST("hEventSelectionVsCentrality"), 0. /* all collisions */, centrality); } - if (!isEventAccepted(collision, centrality, true)) return; // Uses return instead of continue, as there is no explicit loop here + if (!isEventAccepted(collision, centrality, doEventQA)) return; // Uses return instead of continue, as there is no explicit loop here + if (doEventQA) fillCentralityProperties(collision, centrality); const uint64_t collIdx = collision.globalIndex(); if (v0Selections.rejectTPCsectorBoundary) initCCDB(collision); - // Quick event QA: - fillReconstructedEventProperties(collision, centrality, collisionOccupancy, interactionRate, gapSide, selGapSide); - for (auto const& v0 : fullV0s){ V0SelCounter.resetForNewV0(); V0SelCounter.fill(); // Fill for all v0 candidates @@ -1393,9 +1404,10 @@ struct lambdajetpolarizationions { // Dealing with ambiguous tracks: // (TODO: for now, a simple QA plot to understand how many enter this stage is enough) // Saving the Lambdas into a derived data column: - tableV0s(collIdx + auto const v0pt = v0.pt(); + tableV0s(collIdx, centrality, - v0.pt(), + v0pt, v0.eta(), // Using eta instead of rapidity v0.phi(), isLambda, @@ -1415,24 +1427,22 @@ struct lambdajetpolarizationions { const auto posTrackExtra = v0.template posTrackExtra_as(); const auto negTrackExtra = v0.template negTrackExtra_as(); if (isLambda && analyseLambda) { - histos.fill(HIST("h3dMassLambda"), centrality, pt, v0.mLambda()); + histos.fill(HIST("h3dMassLambda"), centrality, v0pt, v0.mLambda()); histos.fill(HIST("hMassLambda"), v0.mLambda()); - if (doPlainTopoQA) { - histos.fill(HIST("Lambda/hPosDCAToPV"), v0.dcapostopv()); - histos.fill(HIST("Lambda/hNegDCAToPV"), v0.dcanegtopv()); - histos.fill(HIST("Lambda/hDCADaughters"), v0.dcaV0daughters()); - histos.fill(HIST("Lambda/hPointingAngle"), std::acos(v0.v0cosPA())); - histos.fill(HIST("Lambda/hV0Radius"), v0.v0radius()); - histos.fill(HIST("Lambda/h2dPositiveITSvsTPCpts"), posTrackExtra.tpcCrossedRows(), posTrackExtra.itsNCls()); - histos.fill(HIST("Lambda/h2dNegativeITSvsTPCpts"), negTrackExtra.tpcCrossedRows(), negTrackExtra.itsNCls()); - histos.fill(HIST("Lambda/h2dPositivePtVsPhi"), v0.positivept(), computePhiMod(v0.positivephi(), 1)); - histos.fill(HIST("Lambda/h2dNegativePtVsPhi"), v0.negativept(), computePhiMod(v0.negativephi(), -1)); - } + histos.fill(HIST("Lambda/hPosDCAToPV"), v0.dcapostopv()); + histos.fill(HIST("Lambda/hNegDCAToPV"), v0.dcanegtopv()); + histos.fill(HIST("Lambda/hDCADaughters"), v0.dcaV0daughters()); + histos.fill(HIST("Lambda/hPointingAngle"), std::acos(v0.v0cosPA())); + histos.fill(HIST("Lambda/hV0Radius"), v0.v0radius()); + histos.fill(HIST("Lambda/h2dPositiveITSvsTPCpts"), posTrackExtra.tpcNClsCrossedRows(), posTrackExtra.itsNCls()); + histos.fill(HIST("Lambda/h2dNegativeITSvsTPCpts"), negTrackExtra.tpcNClsCrossedRows(), negTrackExtra.itsNCls()); + histos.fill(HIST("Lambda/h2dPositivePtVsPhi"), v0.positivept(), computePhiMod(v0.positivephi(), 1)); + histos.fill(HIST("Lambda/h2dNegativePtVsPhi"), v0.negativept(), computePhiMod(v0.negativephi(), -1)); if (doTPCQA) { - histos.fill(HIST("Lambda/h3dPosNsigmaTPC"), centrality, pt, posTrackExtra.tpcNSigmaPr()); - histos.fill(HIST("Lambda/h3dNegNsigmaTPC"), centrality, pt, negTrackExtra.tpcNSigmaPi()); - histos.fill(HIST("Lambda/h3dPosTPCsignal"), centrality, pt, posTrackExtra.tpcSignal()); - histos.fill(HIST("Lambda/h3dNegTPCsignal"), centrality, pt, negTrackExtra.tpcSignal()); + histos.fill(HIST("Lambda/h3dPosNsigmaTPC"), centrality, v0pt, posTrackExtra.tpcNSigmaPr()); + histos.fill(HIST("Lambda/h3dNegNsigmaTPC"), centrality, v0pt, negTrackExtra.tpcNSigmaPi()); + histos.fill(HIST("Lambda/h3dPosTPCsignal"), centrality, v0pt, posTrackExtra.tpcSignal()); + histos.fill(HIST("Lambda/h3dNegTPCsignal"), centrality, v0pt, negTrackExtra.tpcSignal()); histos.fill(HIST("Lambda/h3dPosNsigmaTPCvsTrackPtot"), centrality, v0.pfracpos() * v0.p(), posTrackExtra.tpcNSigmaPr()); histos.fill(HIST("Lambda/h3dNegNsigmaTPCvsTrackPtot"), centrality, v0.pfracneg() * v0.p(), negTrackExtra.tpcNSigmaPi()); histos.fill(HIST("Lambda/h3dPosTPCsignalVsTrackPtot"), centrality, v0.pfracpos() * v0.p(), posTrackExtra.tpcSignal()); @@ -1443,10 +1453,10 @@ struct lambdajetpolarizationions { histos.fill(HIST("Lambda/h3dNegTPCsignalVsTrackPt"), centrality, v0.negativept(), negTrackExtra.tpcSignal()); } if (doTOFQA) { - histos.fill(HIST("Lambda/h3dPosNsigmaTOF"), centrality, pt, v0.tofNSigmaLaPr()); - histos.fill(HIST("Lambda/h3dNegNsigmaTOF"), centrality, pt, v0.tofNSigmaLaPi()); - histos.fill(HIST("Lambda/h3dPosTOFdeltaT"), centrality, pt, v0.posTOFDeltaTLaPr()); - histos.fill(HIST("Lambda/h3dNegTOFdeltaT"), centrality, pt, v0.negTOFDeltaTLaPi()); + histos.fill(HIST("Lambda/h3dPosNsigmaTOF"), centrality, v0pt, v0.tofNSigmaLaPr()); + histos.fill(HIST("Lambda/h3dNegNsigmaTOF"), centrality, v0pt, v0.tofNSigmaLaPi()); + histos.fill(HIST("Lambda/h3dPosTOFdeltaT"), centrality, v0pt, v0.posTOFDeltaTLaPr()); + histos.fill(HIST("Lambda/h3dNegTOFdeltaT"), centrality, v0pt, v0.negTOFDeltaTLaPi()); histos.fill(HIST("Lambda/h3dPosNsigmaTOFvsTrackPtot"), centrality, v0.pfracpos() * v0.p(), v0.tofNSigmaLaPr()); histos.fill(HIST("Lambda/h3dNegNsigmaTOFvsTrackPtot"), centrality, v0.pfracneg() * v0.p(), v0.tofNSigmaLaPi()); histos.fill(HIST("Lambda/h3dPosTOFdeltaTvsTrackPtot"), centrality, v0.pfracpos() * v0.p(), v0.posTOFDeltaTLaPr()); @@ -1457,30 +1467,28 @@ struct lambdajetpolarizationions { histos.fill(HIST("Lambda/h3dNegTOFdeltaTvsTrackPt"), centrality, v0.negativept(), v0.negTOFDeltaTLaPi()); } if (doEtaPhiQA) { - histos.fill(HIST("Lambda/h5dV0PhiVsEta"), centrality, pt, v0.mLambda(), v0.phi(), v0.eta()); + 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("h3dMassAntiLambda"), centrality, pt, v0.mAntiLambda()); - histos.fill(HIST("hMassAntiLambda"), v0.mAntiLambda()); - if (doPlainTopoQA) { - histos.fill(HIST("AntiLambda/hPosDCAToPV"), v0.dcapostopv()); - histos.fill(HIST("AntiLambda/hNegDCAToPV"), v0.dcanegtopv()); - histos.fill(HIST("AntiLambda/hDCADaughters"), v0.dcaV0daughters()); - histos.fill(HIST("AntiLambda/hPointingAngle"), std::acos(v0.v0cosPA())); - histos.fill(HIST("AntiLambda/hV0Radius"), v0.v0radius()); - histos.fill(HIST("AntiLambda/h2dPositiveITSvsTPCpts"), posTrackExtra.tpcCrossedRows(), posTrackExtra.itsNCls()); - histos.fill(HIST("AntiLambda/h2dNegativeITSvsTPCpts"), negTrackExtra.tpcCrossedRows(), negTrackExtra.itsNCls()); - histos.fill(HIST("AntiLambda/h2dPositivePtVsPhi"), v0.positivept(), computePhiMod(v0.positivephi(), 1)); - histos.fill(HIST("AntiLambda/h2dNegativePtVsPhi"), v0.negativept(), computePhiMod(v0.negativephi(), -1)); - } + histos.fill(HIST("h3dMassAntiLambda"), centrality, v0pt, v0.mAntiLambda()); + histos.fill(HIST("hMassAntiLambda"), v0.mAntiLambda()); + histos.fill(HIST("AntiLambda/hPosDCAToPV"), v0.dcapostopv()); + histos.fill(HIST("AntiLambda/hNegDCAToPV"), v0.dcanegtopv()); + histos.fill(HIST("AntiLambda/hDCADaughters"), v0.dcaV0daughters()); + histos.fill(HIST("AntiLambda/hPointingAngle"), std::acos(v0.v0cosPA())); + histos.fill(HIST("AntiLambda/hV0Radius"), v0.v0radius()); + histos.fill(HIST("AntiLambda/h2dPositiveITSvsTPCpts"), posTrackExtra.tpcNClsCrossedRows(), posTrackExtra.itsNCls()); + histos.fill(HIST("AntiLambda/h2dNegativeITSvsTPCpts"), negTrackExtra.tpcNClsCrossedRows(), negTrackExtra.itsNCls()); + histos.fill(HIST("AntiLambda/h2dPositivePtVsPhi"), v0.positivept(), computePhiMod(v0.positivephi(), 1)); + histos.fill(HIST("AntiLambda/h2dNegativePtVsPhi"), v0.negativept(), computePhiMod(v0.negativephi(), -1)); if (doTPCQA) { - histos.fill(HIST("AntiLambda/h3dPosNsigmaTPC"), centrality, pt, posTrackExtra.tpcNSigmaPi()); - histos.fill(HIST("AntiLambda/h3dNegNsigmaTPC"), centrality, pt, negTrackExtra.tpcNSigmaPr()); - histos.fill(HIST("AntiLambda/h3dPosTPCsignal"), centrality, pt, posTrackExtra.tpcSignal()); - histos.fill(HIST("AntiLambda/h3dNegTPCsignal"), centrality, pt, negTrackExtra.tpcSignal()); + histos.fill(HIST("AntiLambda/h3dPosNsigmaTPC"), centrality, v0pt, posTrackExtra.tpcNSigmaPi()); + histos.fill(HIST("AntiLambda/h3dNegNsigmaTPC"), centrality, v0pt, negTrackExtra.tpcNSigmaPr()); + histos.fill(HIST("AntiLambda/h3dPosTPCsignal"), centrality, v0pt, posTrackExtra.tpcSignal()); + histos.fill(HIST("AntiLambda/h3dNegTPCsignal"), centrality, v0pt, negTrackExtra.tpcSignal()); histos.fill(HIST("AntiLambda/h3dPosNsigmaTPCvsTrackPtot"), centrality, v0.pfracpos() * v0.p(), posTrackExtra.tpcNSigmaPi()); histos.fill(HIST("AntiLambda/h3dNegNsigmaTPCvsTrackPtot"), centrality, v0.pfracneg() * v0.p(), negTrackExtra.tpcNSigmaPr()); histos.fill(HIST("AntiLambda/h3dPosTPCsignalVsTrackPtot"), centrality, v0.pfracpos() * v0.p(), posTrackExtra.tpcSignal()); @@ -1491,10 +1499,10 @@ struct lambdajetpolarizationions { histos.fill(HIST("AntiLambda/h3dNegTPCsignalVsTrackPt"), centrality, v0.negativept(), negTrackExtra.tpcSignal()); } if (doTOFQA) { - histos.fill(HIST("AntiLambda/h3dPosNsigmaTOF"), centrality, pt, v0.tofNSigmaALaPi()); - histos.fill(HIST("AntiLambda/h3dNegNsigmaTOF"), centrality, pt, v0.tofNSigmaALaPr()); - histos.fill(HIST("AntiLambda/h3dPosTOFdeltaT"), centrality, pt, v0.posTOFDeltaTLaPi()); - histos.fill(HIST("AntiLambda/h3dNegTOFdeltaT"), centrality, pt, v0.negTOFDeltaTLaPr()); + histos.fill(HIST("AntiLambda/h3dPosNsigmaTOF"), centrality, v0pt, v0.tofNSigmaALaPi()); + histos.fill(HIST("AntiLambda/h3dNegNsigmaTOF"), centrality, v0pt, v0.tofNSigmaALaPr()); + histos.fill(HIST("AntiLambda/h3dPosTOFdeltaT"), centrality, v0pt, v0.posTOFDeltaTLaPi()); + histos.fill(HIST("AntiLambda/h3dNegTOFdeltaT"), centrality, v0pt, v0.negTOFDeltaTLaPr()); histos.fill(HIST("AntiLambda/h3dPosNsigmaTOFvsTrackPtot"), centrality, v0.pfracpos() * v0.p(), v0.tofNSigmaALaPi()); histos.fill(HIST("AntiLambda/h3dNegNsigmaTOFvsTrackPtot"), centrality, v0.pfracneg() * v0.p(), v0.tofNSigmaALaPr()); histos.fill(HIST("AntiLambda/h3dPosTOFdeltaTvsTrackPtot"), centrality, v0.pfracpos() * v0.p(), v0.posTOFDeltaTLaPi()); @@ -1505,7 +1513,7 @@ struct lambdajetpolarizationions { histos.fill(HIST("AntiLambda/h3dNegTOFdeltaTvsTrackPt"), centrality, v0.negativept(), v0.negTOFDeltaTLaPr()); } if (doEtaPhiQA) { - histos.fill(HIST("AntiLambda/h5dV0PhiVsEta"), centrality, pt, v0.mAntiLambda(), v0.phi(), v0.eta()); + 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()); } From ccf6fccc920fa22686ddace65a14e50391ffe251 Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Tue, 10 Feb 2026 15:25:07 -0300 Subject: [PATCH 10/40] First version that runs on raw data (still has errors in event selection and some histograms are empty!) --- .../Strangeness/lambdaJetPolarizationIons.cxx | 413 +++++++++++++----- 1 file changed, 314 insertions(+), 99 deletions(-) diff --git a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIons.cxx b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIons.cxx index 100358b0c99..f4d00a30c6a 100644 --- a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIons.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIons.cxx @@ -32,7 +32,7 @@ #include "PWGJE/DataModel/Jet.h" #include "PWGJE/DataModel/JetReducedData.h" #include "PWGLF/DataModel/LFStrangenessTables.h" -#include "PWGLF/DataModel/LFStrangenessPIDTables.h" // For V0TOFPIDs and NSigmas getters +// #include "PWGLF/DataModel/LFStrangenessPIDTables.h" // For V0TOFPIDs and NSigmas getters #include "PWGLF/DataModel/mcCentrality.h" #include "Common/DataModel/Centrality.h" @@ -41,7 +41,9 @@ #include "Common/Core/trackUtilities.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" // for pp -// #include "Common/DataModel/PIDResponseTOF.h" // Maybe switch this around with LFStrangenessPIDTables? + +// For PID in raw data: +#include "Common/DataModel/PIDResponseTOF.h" // Maybe switch this around with LFStrangenessPIDTables? #include "Common/DataModel/McCollisionExtra.h" #include "Common/DataModel/PIDResponseTPC.h" @@ -110,19 +112,26 @@ using std::array; using namespace o2::aod::rctsel; // Aliases for joined tables: -using SelCollisions = soa::Join; // Added PVMults to get MultNTracksPVeta1 as centrality estimator +using SelCollisions = soa::Join; // Added PVMults to get MultNTracksPVeta1 as centrality estimator +using SelCollisionsSimple = soa::Join; // Simpler, for jets // using DauTracks = soa::Join; // Actually used subscriptions (smaller memory usage): using DauTracks = soa::Join; -using V0Candidates = soa::Join; +// using V0Candidates = soa::Join; +// using V0CandidatesSimple = soa::Join; // No TOF using PseudoJetTracks = soa::Join; // Simpler tracks access. (TODO: Should I include TracksIU and TracksCovIU? I don't use any getters from them!) // , aod::pidTOFFullPi, aod::pidTOFFullKa, aod::pidTOFFullPr>; // Not using TOF right now due to some possible mismatches // using SimCollisions = soa::Join; // using DauTracksMC = soa::Join; +// To run in RAW data: +using V0Candidates = aod::V0Datas; + + + enum CentEstimator { kCentFT0C = 0, kCentFT0M, @@ -157,7 +166,8 @@ struct lambdajetpolarizationions { ///////////////////////////////////////////// Configurable doEventQA{"doEventQA", false, "do event QA histograms"}; Configurable qaCentrality{"qaCentrality", false, "qa centrality flag: check base raw values"}; - Configurable doCompleteTopoQA{"doCompleteTopoQA", false, "do topological variable QA histograms"}; // Includes doPlainTopoQA from derivedlambdakzeroanalysis + Configurable doCompleteTopoQA{"doCompleteTopoQA", false, "do topological variables QA histograms"}; // Includes doPlainTopoQA from derivedlambdakzeroanalysis + Configurable doV0KinematicQA{"doV0KinematicQA", false, "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"}; @@ -341,6 +351,7 @@ struct lambdajetpolarizationions { ConfigurableAxis axisPhi{"axisPhi", {20, 0.0f, constants::math::TwoPI}, "Azimuth angle (rad)"}; ConfigurableAxis axisPhiMod{"axisPhiMod", {100, 0.0f, constants::math::TwoPI / 18}, "Azimuth angle wrt TPC sector (rad.)"}; ConfigurableAxis axisEta{"axisEta", {20, -1.0f, 1.0f}, "#eta"}; + ConfigurableAxis axisRapidity{"axisRapidity", {20, -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"}; @@ -369,6 +380,7 @@ struct lambdajetpolarizationions { ConfigurableAxis axisDeltaPhi{"axisDeltaPhi", {50, -constants::math::PI, constants::math::PI}, "#Delta #phi"}; ConfigurableAxis axisDeltaEta{"axisDeltaEta", {50, -1.5f, 1.5f}, "#Delta #phi"}; // Calculated as twice the subtraction "eta_max=0.9 - R_min=0.2", with a margin ConfigurableAxis axisDeltaR{"axisDeltaR", {50, 0, 3.5f}, "#Delta R"}; // From 0 to about the maximum Delta R possible with R = 0.2 + ConfigurableAxis axisEnergy{"axisEnergy",{200, 0.f, 200.f},"E_{jet} (GeV) (#pi mass hypothesis)"}; // Jet energy is not that well defined here, due to track mass hypothesis being of pions! This is just to include px,py,pz in full! } axisConfigurations; // Jet selection configuration: @@ -626,10 +638,10 @@ struct lambdajetpolarizationions { {p + "DCA_{#pi} to PV", true}, {p + "TPC PID p", v0Selections.tpcPidNsigmaCut > 0}, {p + "TPC PID #pi", v0Selections.tpcPidNsigmaCut > 0}, - {p + "TOF #Delta t p", v0Selections.maxDeltaTimeProton < 1e8}, - {p + "TOF #Delta t #pi", v0Selections.maxDeltaTimePion < 1e8}, - {p + "TOF PID p", v0Selections.tofPidNsigmaCutLaPr < 1e8}, - {p + "TOF PID #pi", v0Selections.tofPidNsigmaCutLaPi < 1e8}, + // {p + "TOF #Delta t p", v0Selections.maxDeltaTimeProton < 1e8}, + // {p + "TOF #Delta t #pi", v0Selections.maxDeltaTimePion < 1e8}, + // {p + "TOF PID p", v0Selections.tofPidNsigmaCutLaPr < 1e8}, + // {p + "TOF PID #pi", v0Selections.tofPidNsigmaCutLaPi < 1e8}, {p + "c#tau", v0Selections.lambdaLifetimeCut > 0} }); }; @@ -732,6 +744,35 @@ struct lambdajetpolarizationions { if (analyseLambda && analyseAntiLambda) histos.add("hAmbiguousLambdaCandidates", "hAmbiguousLambdaCandidates", kTH1D, {{1, 0, 1}}); // 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}); + // --- 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}); + // --- 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}); + } + 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}); + // --- 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}); + // --- 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}); + } + } + if (doCompleteTopoQA) { if (analyseLambda) { histos.add("Lambda/h4dPosDCAToPV", "h4dPosDCAToPV", kTHnD, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisLambdaMass, axisConfigurations.axisDCAtoPV}); @@ -813,6 +854,44 @@ struct lambdajetpolarizationions { 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 + + // Comparisons to jet energy: + histos.add("JetKinematicsQA/h2dJetPtvsDeltaPhiToLead","h2dJetPtvsDeltaPhiToLead",kTH2D,{axisConfigurations.axisPt, axisConfigurations.axisDeltaPhi}); + histos.add("JetKinematicsQA/h2dJetEnergyvsDeltaPhiToLead","h2dJetEnergyvsDeltaPhiToLead",kTH2D,{axisConfigurations.axisEnergy, axisConfigurations.axisDeltaPhi}); + 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}); + + //////////////////////////// + // Leading particle 1D QA: + histos.add("JetVsLeadingParticleQA/hLeadingParticlePt","hLeadingParticlePt",kTH1D,{axisConfigurations.axisPt}); + histos.add("JetVsLeadingParticleQA/hLeadingParticleEta","hLeadingParticleEta",kTH1D,{axisConfigurations.axisEta}); + histos.add("JetVsLeadingParticleQA/hLeadingParticlePhi","hLeadingParticlePhi",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}); + + // Leading particle correlations: + 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}); + + // Main "Leading jet vs leading particle" correlations: + histos.add("JetVsLeadingParticleQA/h2dJetsPerEventvsLeadParticlePt","h2dJetsPerEventvsLeadParticlePt",kTH2D,{axisConfigurations.JetsPerEvent, axisConfigurations.axisPt}); + histos.add("JetVsLeadingParticleQA/h2dLeadJetPtvsLeadParticlePt","h2dLeadJetPtvsLeadParticlePt",kTH2D,{axisConfigurations.axisPt, axisConfigurations.axisPt}); + histos.add("JetVsLeadingParticleQA/h2dLeadJetPtvsCosThetaParticleToLead","h2dLeadJetPtvsCosThetaParticleToLead",kTH2D,{axisConfigurations.axisPt, axisConfigurations.axisCosTheta}); + histos.add("JetVsLeadingParticleQA/h2dLeadParticlePtvsCosThetaParticleToLead","h2dLeadParticlePtvsCosThetaParticleToLead",kTH2D,{axisConfigurations.axisPt, axisConfigurations.axisCosTheta}); + histos.add("JetVsLeadingParticleQA/h2dLeadJetPtvsDeltaPhiParticleToLead","h2dLeadJetPtvsDeltaPhiParticleToLead",kTH2D,{axisConfigurations.axisPt, axisConfigurations.axisDeltaPhi}); + histos.add("JetVsLeadingParticleQA/h2dLeadParticlePtvsDeltaPhiParticleToLead","h2dLeadParticlePtvsDeltaPhiParticleToLead",kTH2D,{axisConfigurations.axisPt, axisConfigurations.axisDeltaPhi}); + } // inspect histogram sizes, please @@ -831,30 +910,27 @@ struct lambdajetpolarizationions { return -1.f; } - template - void initCCDB(TCollision const& collision) - { - if (mRunNumber == collision.runNumber()) { + template + void initCCDB(TBC const& bc) { + if (mRunNumber == bc.runNumber()) { return; } - mRunNumber = collision.runNumber(); + mRunNumber = bc.runNumber(); - // Fetching magnetic field if requested - if (v0Selections.rejectTPCsectorBoundary) { - // In case override, don't proceed, please - no CCDB access required - if (ccdbConfigurations.useCustomMagField) { - magField = ccdbConfigurations.customMagField; - } - else { - grpmag = ccdb->getForRun(ccdbConfigurations.grpmagPath, mRunNumber); - if (!grpmag) { - LOG(fatal) << "Got nullptr from CCDB for path " << ccdbConfigurations.grpmagPath << " of object GRPMagField and " << ccdbConfigurations.grpPath << " of object GRPObject for run " << mRunNumber; - } - // Fetch magnetic field from ccdb for current collision - magField = std::lround(5.f * grpmag->getL3Current() / 30000.f); - LOG(info) << "Retrieved GRP for run " << mRunNumber << " with magnetic field of " << magField << " kZG"; + // Fetching magnetic field as requested + // In case override, don't proceed, please - no CCDB access required + if (ccdbConfigurations.useCustomMagField) { + magField = ccdbConfigurations.customMagField; + } + else { + grpmag = ccdb->getForRun(ccdbConfigurations.grpmagPath, mRunNumber); + if (!grpmag) { + LOG(fatal) << "Got nullptr from CCDB for path " << ccdbConfigurations.grpmagPath << " of object GRPMagField and " << ccdbConfigurations.grpPath << " of object GRPObject for run " << mRunNumber; } + // Fetch magnetic field from ccdb for current bc + magField = std::lround(5.f * grpmag->getL3Current() / 30000.f); + LOG(info) << "Retrieved GRP for run " << mRunNumber << " with magnetic field of " << magField << " kZG"; } } @@ -939,8 +1015,8 @@ struct lambdajetpolarizationions { ///////////////////////////////////////////// // Helper functions for event and candidate selection: - template // (TODO: add fillHists and doEventQA capabilities from derivedlambdakzeroanalysis) - bool isEventAccepted(TCollision const& collision, float centrality, bool fillHists){ // check whether the collision passes our collision selections + template // (TODO: add fillHists and doEventQA capabilities from derivedlambdakzeroanalysis) + bool isEventAccepted(TCollision const& collision, TBC const& bc, float centrality, 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()) return false; if (fillHists) fillEventSelectionQA(++selectionIdx, centrality); @@ -977,10 +1053,18 @@ struct lambdajetpolarizationions { if (fillHists) fillEventSelectionQA(++selectionIdx, centrality); if (doPPAnalysis) { // we are in pp - if (eventSelections.requireINEL0 && collision.multNTracksPVeta1() < 1) return false; - if (fillHists) fillEventSelectionQA(++selectionIdx, centrality); - if (eventSelections.requireINEL1 && collision.multNTracksPVeta1() < 2) return false; - if (fillHists) fillEventSelectionQA(++selectionIdx, centrality); + if constexpr (requires { collision.multNTracksPVeta1(); }){ + // Only considers compiling this block when the collision type actually + // has multNTracksPVeta1(). This is done to reduce collision-table + // subscriptions in the jet processing function. + // This is a compile-time check: since the function is templated, it + // is instantiated separately for Jets and V0s, and this block will be + // properly compiled for each use case and table subscription automatically! + if (eventSelections.requireINEL0 && collision.multNTracksPVeta1() < 1) return false; + if (fillHists) fillEventSelectionQA(++selectionIdx, centrality); + if (eventSelections.requireINEL1 && collision.multNTracksPVeta1() < 2) return false; + if (fillHists) fillEventSelectionQA(++selectionIdx, centrality); + } } else { // Performing selections as if in Pb-Pb: const float collisionOccupancy = eventSelections.useFT0CbasedOccupancy ? collision.ft0cOccupancyInTimeRange() : collision.trackOccupancyInTimeRange(); @@ -990,7 +1074,7 @@ 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, collision.timestamp(), collision.runNumber(), irSource) * 1.e-3 : -1; + 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) fillEventSelectionQA(++selectionIdx, centrality); if (eventSelections.maxIR >= 0 && interactionRate > eventSelections.maxIR) return false; @@ -1057,7 +1141,7 @@ struct lambdajetpolarizationions { V0SelCounter.fill(); // pseudorapidity cuts: - if (std::fabs(v0.yLambda()) > v0Selections.rapidityCut) return false; // Not using rapidity in selections for now. Just detector acceptance. + if (std::fabs(v0.yLambda()) > v0Selections.rapidityCut) return false; V0SelCounter.fill(); // if (std::fabs(v0.eta()) > v0Selections.daughterEtaCut) return false; // (TODO: properly consider this in daughter selection!) @@ -1065,8 +1149,8 @@ struct lambdajetpolarizationions { if (std::fabs(v0.mK0Short() - o2::constants::physics::MassK0Short) < v0Selections.compMassRejection) return false; V0SelCounter.fill(); - const auto posTrackExtra = v0.template posTrackExtra_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 negTrackExtra_as(); + 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(); // ITS quality cuts bool posIsFromAfterburner = posTrackExtra.isITSAfterburner(); @@ -1149,8 +1233,8 @@ struct lambdajetpolarizationions { if (dcaPionToPV < v0Selections.dcaPionToPV) return false; V0SelCounter.fill(); - const auto posTrackExtra = v0.template posTrackExtra_as(); - const auto negTrackExtra = v0.template negTrackExtra_as(); + const auto posTrackExtra = v0.template posTrack_as(); + const auto negTrackExtra = v0.template negTrack_as(); // For the PID cuts to be properly applied while also keeping this function // general enough for Lambdas and AntiLambdas, we identify the roles of @@ -1165,27 +1249,37 @@ struct lambdajetpolarizationions { if (std::fabs(pionTrack.tpcNSigmaPi()) > v0Selections.tpcPidNsigmaCut) return false; V0SelCounter.fill(); - // TOF PID in DeltaT (if TOF is not available, then uses the track. If is available, uses it. In this sense, TOF is optional) - // const bool posHasTOF = posTrackExtra.hasTOF(); // For the older version, which worked only for Lambdas - const bool protonHasTOF = protonTrack.hasTOF(); - const bool pionHasTOF = pionTrack.hasTOF(); - - // Proton-like track - if (protonHasTOF && std::abs(Lambda_hypothesis ? v0.posTOFDeltaTLaPr() - : v0.negTOFDeltaTLaPr()) > v0Selections.maxDeltaTimeProton) return false; - V0SelCounter.fill(); - // Pion-like track - if (pionHasTOF && std::abs(Lambda_hypothesis ? v0.negTOFDeltaTLaPi() - : v0.posTOFDeltaTLaPi()) > v0Selections.maxDeltaTimePion) return false; - V0SelCounter.fill(); - - // TOF PID in NSigma - // Proton-like track - if (protonHasTOF && std::fabs(v0.tofNSigmaLaPr()) > v0Selections.tofPidNsigmaCutLaPr) return false; - V0SelCounter.fill(); - // Pion-like track - if (pionHasTOF && std::fabs(v0.tofNSigmaLaPi()) > v0Selections.tofPidNsigmaCutLaPi) return false; - V0SelCounter.fill(); + // // TOF PID in DeltaT (if TOF is not available, then uses the track. If is available, uses it. In this sense, TOF is optional) + // // const bool posHasTOF = posTrackExtra.hasTOF(); // For the older version, which worked only for Lambdas + // const bool protonHasTOF = protonTrack.hasTOF(); + // const bool pionHasTOF = pionTrack.hasTOF(); + + // // Proton-like track + // if (protonHasTOF && std::abs(Lambda_hypothesis ? v0.posTOFDeltaTLaPr() + // : v0.negTOFDeltaTLaPr()) > v0Selections.maxDeltaTimeProton) return false; + // V0SelCounter.fill(); + // // Pion-like track + // if (pionHasTOF && std::abs(Lambda_hypothesis ? v0.negTOFDeltaTLaPi() + // : v0.posTOFDeltaTLaPi()) > v0Selections.maxDeltaTimePion) return false; + // V0SelCounter.fill(); + + // // TOF PID in NSigma (TODO: add asymmetric NSigma windows for purity tuning?) + // // Proton-like track + // // if (protonHasTOF && std::fabs(v0.tofNSigmaLaPr()) > v0Selections.tofPidNsigmaCutLaPr) return false; + // // Getter for raw data's PIDResponseTOF.h instead of LFStrangenessPIDTables.h: + // if (protonHasTOF && std::fabs(v0.tofNSigmaLaPr()) > v0Selections.tofPidNsigmaCutLaPr) return false; + // V0SelCounter.fill(); + // // Pion-like track + // if (pionHasTOF && std::fabs(v0.tofNSigmaLaPi()) > v0Selections.tofPidNsigmaCutLaPi) return false; + // V0SelCounter.fill(); + + // // // PID Selections (TOF) + // // if (requireTOF) { + // // if (ptrack.tofNSigmaPr() < nsigmaTOFmin || ptrack.tofNSigmaPr() > nsigmaTOFmax) + // // return false; + // // if (ntrack.tofNSigmaPi() < nsigmaTOFmin || ntrack.tofNSigmaPi() > nsigmaTOFmax) + // // return false; + // // } // proper lifetime if (v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * o2::constants::physics::MassLambda0 > v0Selections.lambdaLifetimeCut) return false; @@ -1201,15 +1295,17 @@ struct lambdajetpolarizationions { // } - - void processJetsData(SelCollisions::iterator const& collision, PseudoJetTracks const& tracks){ + void processJetsData(SelCollisionsSimple::iterator const& collision, PseudoJetTracks const& tracks, aod::BCsWithTimestamps const& bcs){ // Uses BCsWithTimestamps to get timestamps for rejectTPCsectorBoundary float centrality = -1.0f; // Just a placeholder - if (!isEventAccepted(collision, centrality, false)) return; // Uses return instead of continue, as there is no explicit loop here + + auto bc = bcs.iteratorAt(collision.bcId()); // Got the iteratorAt() idea from O2Physics/PWGUD/Core/UDHelpers.h + if (!isEventAccepted(collision, bc, centrality, false)) return; // Uses return instead of continue, as there is no explicit loop here const uint64_t collIdx = collision.globalIndex(); - if (v0Selections.rejectTPCsectorBoundary) initCCDB(collision); // Loop over reconstructed tracks: std::vector fjParticles; + int leadingParticleIdx; // An index inside fjParticles + float leadingParticlePt = 0; for (auto const& track : tracks){ // Require that tracks pass selection criteria if (!isCandidateForChargedPseudojetAccepted(track)) continue; @@ -1219,6 +1315,12 @@ struct lambdajetpolarizationions { // (TODO: study the possibility of using identified PseudoJet candidates for this estimate) fastjet::PseudoJet candidate(track.px(), track.py(), track.pz(), track.energy(o2::constants::physics::MassPionCharged)); fjParticles.emplace_back(candidate); + + // Calculating leading particle + if (track.pt() > leadingParticlePt){ + leadingParticlePt = track.pt(); + leadingParticleIdx = fjParticles.size() - 1; + } } // Reject empty events if (fjParticles.size() < 1) return; @@ -1302,7 +1404,44 @@ struct lambdajetpolarizationions { histos.fill(HIST("JetKinematicsQA/h2dCosThetaToLeadvsDeltaEtaToLead"), cosTheta, deltaEta); histos.fill(HIST("JetKinematicsQA/h2dCosThetaToLeadvsDeltaRToLead"), cosTheta, deltaR); histos.fill(HIST("JetKinematicsQA/h2dDeltaPhiToLeadvsDeltaEtaToLead"), deltaPhi, deltaEta); + + histos.fill(HIST("JetKinematicsQA/h2dJetPtvsDeltaPhiToLead"), jet.pt(), deltaPhi); // Can't really get the energy of the jet, just the pt to make this comparison + histos.fill(HIST("JetKinematicsQA/h2dJetEnergyvsDeltaPhiToLead"), jet.E(), deltaPhi); // Just a different scale + histos.fill(HIST("JetKinematicsQA/h2dJetEnergyvsCosThetaToLead"), jet.E(), cosTheta); + + histos.fill(HIST("JetKinematicsQA/h2dJetsPerEventvsDeltaPhiToLead"), selectedJets, deltaPhi); + histos.fill(HIST("JetKinematicsQA/h2dJetsPerEventvsDeltaEtaToLead"), selectedJets, deltaEta); + histos.fill(HIST("JetKinematicsQA/h2dJetsPerEventvsCosThetaToLead"), selectedJets, cosTheta); } + // Leading particle comparisons: + auto const& leadingParticle = fjParticles[leadingParticleIdx]; + + histos.fill(HIST("JetVsLeadingParticleQA/hLeadingParticlePt"), leadingParticle.pt()); + histos.fill(HIST("JetVsLeadingParticleQA/hLeadingParticleEta"), leadingParticle.eta()); + histos.fill(HIST("JetVsLeadingParticleQA/hLeadingParticlePhi"), leadingParticle.phi()); + + double deltaPhiParticleToJet = leadingJetSub.phi() - leadingParticle.phi(); + double deltaEtaParticleToJet = leadingJetSub.eta() - leadingParticle.eta(); + double 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("JetVsLeadingParticleQA/h2dDeltaPhiParticleToLeadvsDeltaEtaParticleToLead"), deltaPhiParticleToJet, deltaEtaParticleToJet); + + histos.fill(HIST("JetVsLeadingParticleQA/h2dJetsPerEventvsDeltaPhiParticleToLead"), selectedJets, deltaPhiParticleToJet); + histos.fill(HIST("JetVsLeadingParticleQA/h2dJetsPerEventvsDeltaEtaParticleToLead"), selectedJets, deltaEtaParticleToJet); + histos.fill(HIST("JetVsLeadingParticleQA/h2dJetsPerEventvsCosThetaParticleToLead"), selectedJets, cosThetaParticleToJet); + + histos.fill(HIST("JetVsLeadingParticleQA/h2dJetsPerEventvsLeadParticlePt"), selectedJets, leadingParticle.pt()); + histos.fill(HIST("JetVsLeadingParticleQA/h2dLeadJetPtvsLeadParticlePt"), leadingJetSub.pt(), leadingParticle.pt()); + + histos.fill(HIST("JetVsLeadingParticleQA/h2dLeadJetPtvsCosThetaParticleToLead"), leadingJetSub.pt(), cosThetaParticleToJet); + histos.fill(HIST("JetVsLeadingParticleQA/h2dLeadParticlePtvsCosThetaParticleToLead"), leadingParticle.pt(), cosThetaParticleToJet); + + 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); } } else{ // Otherwise, simple jet clustering @@ -1358,26 +1497,68 @@ struct lambdajetpolarizationions { histos.fill(HIST("JetKinematicsQA/h2dCosThetaToLeadvsDeltaEtaToLead"), cosTheta, deltaEta); histos.fill(HIST("JetKinematicsQA/h2dCosThetaToLeadvsDeltaRToLead"), cosTheta, deltaR); histos.fill(HIST("JetKinematicsQA/h2dDeltaPhiToLeadvsDeltaEtaToLead"), deltaPhi, deltaEta); + + histos.fill(HIST("JetKinematicsQA/h2dJetPtvsDeltaPhiToLead"), jet.pt(), deltaPhi); // Can't really get the energy of the jet, just the pt to make this comparison + histos.fill(HIST("JetKinematicsQA/h2dJetEnergyvsDeltaPhiToLead"), jet.E(), deltaPhi); // Just a different scale + histos.fill(HIST("JetKinematicsQA/h2dJetEnergyvsCosThetaToLead"), jet.E(), cosTheta); + + histos.fill(HIST("JetKinematicsQA/h2dJetsPerEventvsDeltaPhiToLead"), jetsInEvent, deltaPhi); + histos.fill(HIST("JetKinematicsQA/h2dJetsPerEventvsDeltaEtaToLead"), jetsInEvent, deltaEta); + histos.fill(HIST("JetKinematicsQA/h2dJetsPerEventvsCosThetaToLead"), jetsInEvent, cosTheta); } } if (doJetKinematicsQA){ histos.fill(HIST("JetKinematicsQA/hLeadingJetPt"), leadingJet.pt()); histos.fill(HIST("JetKinematicsQA/hLeadingJetEta"), leadingJet.eta()); histos.fill(HIST("JetKinematicsQA/hLeadingJetPhi"), leadingJet.phi()); + + // Leading particle comparisons: + auto const& leadingParticle = fjParticles[leadingParticleIdx]; + + histos.fill(HIST("JetVsLeadingParticleQA/hLeadingParticlePt"), leadingParticle.pt()); + histos.fill(HIST("JetVsLeadingParticleQA/hLeadingParticleEta"), leadingParticle.eta()); + histos.fill(HIST("JetVsLeadingParticleQA/hLeadingParticlePhi"), leadingParticle.phi()); + + double deltaPhiParticleToJet = leadingJet.phi() - leadingParticle.phi(); + 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("JetVsLeadingParticleQA/h2dDeltaPhiParticleToLeadvsDeltaEtaParticleToLead"), deltaPhiParticleToJet, deltaEtaParticleToJet); + + histos.fill(HIST("JetVsLeadingParticleQA/h2dJetsPerEventvsDeltaPhiParticleToLead"), jetsInEvent, deltaPhiParticleToJet); + histos.fill(HIST("JetVsLeadingParticleQA/h2dJetsPerEventvsDeltaEtaParticleToLead"), jetsInEvent, deltaEtaParticleToJet); + histos.fill(HIST("JetVsLeadingParticleQA/h2dJetsPerEventvsCosThetaParticleToLead"), jetsInEvent, cosThetaParticleToJet); + + histos.fill(HIST("JetVsLeadingParticleQA/h2dJetsPerEventvsLeadParticlePt"), jetsInEvent, leadingParticle.pt()); + histos.fill(HIST("JetVsLeadingParticleQA/h2dLeadJetPtvsLeadParticlePt"), leadingJet.pt(), leadingParticle.pt()); + + histos.fill(HIST("JetVsLeadingParticleQA/h2dLeadJetPtvsCosThetaParticleToLead"), leadingJet.pt(), cosThetaParticleToJet); + histos.fill(HIST("JetVsLeadingParticleQA/h2dLeadParticlePtvsCosThetaParticleToLead"), leadingParticle.pt(), cosThetaParticleToJet); + + 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); } } } - void processV0sData(SelCollisions::iterator const& collision, V0Candidates const& fullV0s){ + // Had to include DauTracks in subscription, even though I don't loop in it, for the indices to resolve, avoiding " Exception while running: Index pointing to Tracks is not bound!" + void processV0sData(SelCollisions::iterator const& collision, V0Candidates const& fullV0s, aod::BCsWithTimestamps const& bcs, DauTracks const& V0DauTracks){ const float centrality = getCentrality(collision); if (doEventQA) { histos.fill(HIST("hEventSelection"), 0. /* all collisions */); histos.fill(HIST("hEventSelectionVsCentrality"), 0. /* all collisions */, centrality); } - if (!isEventAccepted(collision, centrality, doEventQA)) return; // Uses return instead of continue, as there is no explicit loop here + + auto bc = bcs.iteratorAt(collision.bcId()); + if (!isEventAccepted(collision, bc, centrality, doEventQA)) return; // Uses return instead of continue, as there is no explicit loop here + if (doEventQA) fillCentralityProperties(collision, centrality); const uint64_t collIdx = collision.globalIndex(); - if (v0Selections.rejectTPCsectorBoundary) initCCDB(collision); + if (v0Selections.rejectTPCsectorBoundary) initCCDB(bc); // Substituted call from collision to bc for raw data for (auto const& v0 : fullV0s){ V0SelCounter.resetForNewV0(); @@ -1422,10 +1603,44 @@ struct lambdajetpolarizationions { v0.negativephi() ); + if (doV0KinematicQA){ + // Cache kinematics once + const float v0y = v0.yLambda(); + const float v0phi = v0.phi(); + const float mLambda = v0.mLambda(); + const float mAntiLambda = v0.mAntiLambda(); + if (analyseLambda && isLambda){ + // --- Basic kinematics --- + histos.fill(HIST("V0KinematicQA/Lambda/hPt"), v0pt); + histos.fill(HIST("V0KinematicQA/Lambda/hY"), v0y); + histos.fill(HIST("V0KinematicQA/Lambda/hPhi"), v0phi); + // --- Mass correlations --- + histos.fill(HIST("V0KinematicQA/Lambda/hMassVsPt"), v0pt, mLambda); + histos.fill(HIST("V0KinematicQA/Lambda/hMassVsY"), v0y, mLambda); + histos.fill(HIST("V0KinematicQA/Lambda/hMassVsPhi"), v0phi, mLambda); + // --- Kinematic correlations --- + histos.fill(HIST("V0KinematicQA/Lambda/hYVsPt"), v0pt, v0y); + histos.fill(HIST("V0KinematicQA/Lambda/hPhiVsPt"), v0pt, v0phi); + } + if (analyseAntiLambda && isAntiLambda){ + // --- Basic kinematics --- + histos.fill(HIST("V0KinematicQA/AntiLambda/hPt"), v0pt); + histos.fill(HIST("V0KinematicQA/AntiLambda/hY"), v0y); + histos.fill(HIST("V0KinematicQA/AntiLambda/hPhi"), v0phi); + // --- Mass correlations --- + histos.fill(HIST("V0KinematicQA/AntiLambda/hMassVsPt"), v0pt, mAntiLambda); + histos.fill(HIST("V0KinematicQA/AntiLambda/hMassVsY"), v0y, mAntiLambda); + histos.fill(HIST("V0KinematicQA/AntiLambda/hMassVsPhi"), v0phi, mAntiLambda); + // --- Kinematic correlations --- + histos.fill(HIST("V0KinematicQA/AntiLambda/hYVsPt"), v0pt, v0y); + histos.fill(HIST("V0KinematicQA/AntiLambda/hPhiVsPt"), v0pt, v0phi); + } + } + if (doCompleteTopoQA){ // Remaking these variables outside of the passesLambdaLambdaBarHypothesis. Loses performance, but that should be OK for QA - const auto posTrackExtra = v0.template posTrackExtra_as(); - const auto negTrackExtra = v0.template negTrackExtra_as(); + const auto posTrackExtra = v0.template posTrack_as(); + const auto negTrackExtra = v0.template negTrack_as(); if (isLambda && analyseLambda) { histos.fill(HIST("h3dMassLambda"), centrality, v0pt, v0.mLambda()); histos.fill(HIST("hMassLambda"), v0.mLambda()); @@ -1452,20 +1667,20 @@ struct lambdajetpolarizationions { histos.fill(HIST("Lambda/h3dPosTPCsignalVsTrackPt"), centrality, v0.positivept(), posTrackExtra.tpcSignal()); histos.fill(HIST("Lambda/h3dNegTPCsignalVsTrackPt"), centrality, v0.negativept(), negTrackExtra.tpcSignal()); } - if (doTOFQA) { - histos.fill(HIST("Lambda/h3dPosNsigmaTOF"), centrality, v0pt, v0.tofNSigmaLaPr()); - histos.fill(HIST("Lambda/h3dNegNsigmaTOF"), centrality, v0pt, v0.tofNSigmaLaPi()); - histos.fill(HIST("Lambda/h3dPosTOFdeltaT"), centrality, v0pt, v0.posTOFDeltaTLaPr()); - histos.fill(HIST("Lambda/h3dNegTOFdeltaT"), centrality, v0pt, v0.negTOFDeltaTLaPi()); - histos.fill(HIST("Lambda/h3dPosNsigmaTOFvsTrackPtot"), centrality, v0.pfracpos() * v0.p(), v0.tofNSigmaLaPr()); - histos.fill(HIST("Lambda/h3dNegNsigmaTOFvsTrackPtot"), centrality, v0.pfracneg() * v0.p(), v0.tofNSigmaLaPi()); - histos.fill(HIST("Lambda/h3dPosTOFdeltaTvsTrackPtot"), centrality, v0.pfracpos() * v0.p(), v0.posTOFDeltaTLaPr()); - histos.fill(HIST("Lambda/h3dNegTOFdeltaTvsTrackPtot"), centrality, v0.pfracneg() * v0.p(), v0.negTOFDeltaTLaPi()); - histos.fill(HIST("Lambda/h3dPosNsigmaTOFvsTrackPt"), centrality, v0.positivept(), v0.tofNSigmaLaPr()); - histos.fill(HIST("Lambda/h3dNegNsigmaTOFvsTrackPt"), centrality, v0.negativept(), v0.tofNSigmaLaPi()); - histos.fill(HIST("Lambda/h3dPosTOFdeltaTvsTrackPt"), centrality, v0.positivept(), v0.posTOFDeltaTLaPr()); - histos.fill(HIST("Lambda/h3dNegTOFdeltaTvsTrackPt"), centrality, v0.negativept(), v0.negTOFDeltaTLaPi()); - } + // if (doTOFQA) { + // histos.fill(HIST("Lambda/h3dPosNsigmaTOF"), centrality, v0pt, v0.tofNSigmaLaPr()); + // histos.fill(HIST("Lambda/h3dNegNsigmaTOF"), centrality, v0pt, v0.tofNSigmaLaPi()); + // histos.fill(HIST("Lambda/h3dPosTOFdeltaT"), centrality, v0pt, v0.posTOFDeltaTLaPr()); + // histos.fill(HIST("Lambda/h3dNegTOFdeltaT"), centrality, v0pt, v0.negTOFDeltaTLaPi()); + // histos.fill(HIST("Lambda/h3dPosNsigmaTOFvsTrackPtot"), centrality, v0.pfracpos() * v0.p(), v0.tofNSigmaLaPr()); + // histos.fill(HIST("Lambda/h3dNegNsigmaTOFvsTrackPtot"), centrality, v0.pfracneg() * v0.p(), v0.tofNSigmaLaPi()); + // histos.fill(HIST("Lambda/h3dPosTOFdeltaTvsTrackPtot"), centrality, v0.pfracpos() * v0.p(), v0.posTOFDeltaTLaPr()); + // histos.fill(HIST("Lambda/h3dNegTOFdeltaTvsTrackPtot"), centrality, v0.pfracneg() * v0.p(), v0.negTOFDeltaTLaPi()); + // histos.fill(HIST("Lambda/h3dPosNsigmaTOFvsTrackPt"), centrality, v0.positivept(), v0.tofNSigmaLaPr()); + // histos.fill(HIST("Lambda/h3dNegNsigmaTOFvsTrackPt"), centrality, v0.negativept(), v0.tofNSigmaLaPi()); + // histos.fill(HIST("Lambda/h3dPosTOFdeltaTvsTrackPt"), centrality, v0.positivept(), v0.posTOFDeltaTLaPr()); + // 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()); @@ -1498,20 +1713,20 @@ struct lambdajetpolarizationions { histos.fill(HIST("AntiLambda/h3dPosTPCsignalVsTrackPt"), centrality, v0.positivept(), posTrackExtra.tpcSignal()); histos.fill(HIST("AntiLambda/h3dNegTPCsignalVsTrackPt"), centrality, v0.negativept(), negTrackExtra.tpcSignal()); } - if (doTOFQA) { - histos.fill(HIST("AntiLambda/h3dPosNsigmaTOF"), centrality, v0pt, v0.tofNSigmaALaPi()); - histos.fill(HIST("AntiLambda/h3dNegNsigmaTOF"), centrality, v0pt, v0.tofNSigmaALaPr()); - histos.fill(HIST("AntiLambda/h3dPosTOFdeltaT"), centrality, v0pt, v0.posTOFDeltaTLaPi()); - histos.fill(HIST("AntiLambda/h3dNegTOFdeltaT"), centrality, v0pt, v0.negTOFDeltaTLaPr()); - histos.fill(HIST("AntiLambda/h3dPosNsigmaTOFvsTrackPtot"), centrality, v0.pfracpos() * v0.p(), v0.tofNSigmaALaPi()); - histos.fill(HIST("AntiLambda/h3dNegNsigmaTOFvsTrackPtot"), centrality, v0.pfracneg() * v0.p(), v0.tofNSigmaALaPr()); - histos.fill(HIST("AntiLambda/h3dPosTOFdeltaTvsTrackPtot"), centrality, v0.pfracpos() * v0.p(), v0.posTOFDeltaTLaPi()); - histos.fill(HIST("AntiLambda/h3dNegTOFdeltaTvsTrackPtot"), centrality, v0.pfracneg() * v0.p(), v0.negTOFDeltaTLaPr()); - histos.fill(HIST("AntiLambda/h3dPosNsigmaTOFvsTrackPt"), centrality, v0.positivept(), v0.tofNSigmaALaPi()); - histos.fill(HIST("AntiLambda/h3dNegNsigmaTOFvsTrackPt"), centrality, v0.negativept(), v0.tofNSigmaALaPr()); - histos.fill(HIST("AntiLambda/h3dPosTOFdeltaTvsTrackPt"), centrality, v0.positivept(), v0.posTOFDeltaTLaPi()); - histos.fill(HIST("AntiLambda/h3dNegTOFdeltaTvsTrackPt"), centrality, v0.negativept(), v0.negTOFDeltaTLaPr()); - } + // if (doTOFQA) { + // histos.fill(HIST("AntiLambda/h3dPosNsigmaTOF"), centrality, v0pt, v0.tofNSigmaALaPi()); + // histos.fill(HIST("AntiLambda/h3dNegNsigmaTOF"), centrality, v0pt, v0.tofNSigmaALaPr()); + // histos.fill(HIST("AntiLambda/h3dPosTOFdeltaT"), centrality, v0pt, v0.posTOFDeltaTLaPi()); + // histos.fill(HIST("AntiLambda/h3dNegTOFdeltaT"), centrality, v0pt, v0.negTOFDeltaTLaPr()); + // histos.fill(HIST("AntiLambda/h3dPosNsigmaTOFvsTrackPtot"), centrality, v0.pfracpos() * v0.p(), v0.tofNSigmaALaPi()); + // histos.fill(HIST("AntiLambda/h3dNegNsigmaTOFvsTrackPtot"), centrality, v0.pfracneg() * v0.p(), v0.tofNSigmaALaPr()); + // histos.fill(HIST("AntiLambda/h3dPosTOFdeltaTvsTrackPtot"), centrality, v0.pfracpos() * v0.p(), v0.posTOFDeltaTLaPi()); + // histos.fill(HIST("AntiLambda/h3dNegTOFdeltaTvsTrackPtot"), centrality, v0.pfracneg() * v0.p(), v0.negTOFDeltaTLaPr()); + // histos.fill(HIST("AntiLambda/h3dPosNsigmaTOFvsTrackPt"), centrality, v0.positivept(), v0.tofNSigmaALaPi()); + // histos.fill(HIST("AntiLambda/h3dNegNsigmaTOFvsTrackPt"), centrality, v0.negativept(), v0.tofNSigmaALaPr()); + // histos.fill(HIST("AntiLambda/h3dPosTOFdeltaTvsTrackPt"), centrality, v0.positivept(), v0.posTOFDeltaTLaPi()); + // 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()); From 802bde6a1d0a1a41ff418761e2ec7229082224ca Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Wed, 11 Feb 2026 11:16:36 -0300 Subject: [PATCH 11/40] Updating TOF usage and axes --- .../Strangeness/lambdaJetPolarizationIons.cxx | 176 +++++++++--------- 1 file changed, 90 insertions(+), 86 deletions(-) diff --git a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIons.cxx b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIons.cxx index f4d00a30c6a..2ed010a2f83 100644 --- a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIons.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIons.cxx @@ -32,7 +32,7 @@ #include "PWGJE/DataModel/Jet.h" #include "PWGJE/DataModel/JetReducedData.h" #include "PWGLF/DataModel/LFStrangenessTables.h" -// #include "PWGLF/DataModel/LFStrangenessPIDTables.h" // For V0TOFPIDs and NSigmas getters +#include "PWGLF/DataModel/LFStrangenessPIDTables.h" // For V0TOFPIDs and NSigmas getters. Better for considering the daughters as coming from V0s instead of from PV? #include "PWGLF/DataModel/mcCentrality.h" #include "Common/DataModel/Centrality.h" @@ -43,7 +43,7 @@ #include "Common/DataModel/Multiplicity.h" // for pp // For PID in raw data: -#include "Common/DataModel/PIDResponseTOF.h" // Maybe switch this around with LFStrangenessPIDTables? +// #include "Common/DataModel/PIDResponseTOF.h" // Maybe switch this around with LFStrangenessPIDTables? #include "Common/DataModel/McCollisionExtra.h" #include "Common/DataModel/PIDResponseTPC.h" @@ -129,6 +129,7 @@ using PseudoJetTracks = soa::Join; // Tables created by o2-analysis-lf-strangenesstofpid @@ -348,10 +349,10 @@ struct lambdajetpolarizationions { ConfigurableAxis axisTPCsignal{"axisTPCsignal", {200, 0.0f, 200.0f}, "TPC signal"}; ConfigurableAxis axisNsigmaTOF{"axisNsigmaTOF", {200, -10.0f, 10.0f}, "N sigma TOF"}; ConfigurableAxis axisTOFdeltaT{"axisTOFdeltaT", {200, -5000.0f, 5000.0f}, "TOF Delta T (ps)"}; - ConfigurableAxis axisPhi{"axisPhi", {20, 0.0f, constants::math::TwoPI}, "Azimuth angle (rad)"}; + ConfigurableAxis axisPhi{"axisPhi", {50, 0.0f, constants::math::TwoPI}, "Azimuth angle (rad)"}; ConfigurableAxis axisPhiMod{"axisPhiMod", {100, 0.0f, constants::math::TwoPI / 18}, "Azimuth angle wrt TPC sector (rad.)"}; - ConfigurableAxis axisEta{"axisEta", {20, -1.0f, 1.0f}, "#eta"}; - ConfigurableAxis axisRapidity{"axisRapidity", {20, -1.0f, 1.0f}, "y"}; + 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"}; @@ -376,6 +377,15 @@ struct lambdajetpolarizationions { // Jet QA axes: ConfigurableAxis JetsPerEvent{"JetsPerEvent", {20, 0, 20}, "Jets per event"}; + // ConfigurableAxis axisLeadingParticlePt{"axisLeadingParticlePt",{VARIABLE_WIDTH, + // 0.0f, 0.5f, 1.0f, 1.5f, 2.0f, // very low pT (coarse on purpose) + // 3.0f, 4.0f, 5.0f, 6.0f, 8.0f, 10.0f, // trigger / intermediate region + // 12.0f, 15.0f, 20.0f, 25.0f, 30.0f, // high pT leading particles + // 40.0f, 50.0f, 60.0f, 80.0f, 100.0f, + // 120.0f, 150.0f, 200.0f}, // ultra-high pT safety net + // "Leading particle p_{T} (GeV/c)"}; + 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"}; ConfigurableAxis axisDeltaEta{"axisDeltaEta", {50, -1.5f, 1.5f}, "#Delta #phi"}; // Calculated as twice the subtraction "eta_max=0.9 - R_min=0.2", with a margin @@ -638,10 +648,10 @@ struct lambdajetpolarizationions { {p + "DCA_{#pi} to PV", true}, {p + "TPC PID p", v0Selections.tpcPidNsigmaCut > 0}, {p + "TPC PID #pi", v0Selections.tpcPidNsigmaCut > 0}, - // {p + "TOF #Delta t p", v0Selections.maxDeltaTimeProton < 1e8}, - // {p + "TOF #Delta t #pi", v0Selections.maxDeltaTimePion < 1e8}, - // {p + "TOF PID p", v0Selections.tofPidNsigmaCutLaPr < 1e8}, - // {p + "TOF PID #pi", v0Selections.tofPidNsigmaCutLaPi < 1e8}, + {p + "TOF #Delta t p", v0Selections.maxDeltaTimeProton < 1e8}, + {p + "TOF #Delta t #pi", v0Selections.maxDeltaTimePion < 1e8}, + {p + "TOF PID p", v0Selections.tofPidNsigmaCutLaPr < 1e8}, + {p + "TOF PID #pi", v0Selections.tofPidNsigmaCutLaPi < 1e8}, {p + "c#tau", v0Selections.lambdaLifetimeCut > 0} }); }; @@ -834,7 +844,7 @@ struct lambdajetpolarizationions { // 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 if (doJetKinematicsQA){ - histos.add("JetKinematicsQA/hJetPt", "hJetPt", kTH1D, {axisConfigurations.axisPt}); + histos.add("JetKinematicsQA/hJetPt", "hJetPt", kTH1D, {axisConfigurations.axisJetPt}); histos.add("JetKinematicsQA/hJetEta", "hJetEta", kTH1D, {axisConfigurations.axisEta}); histos.add("JetKinematicsQA/hJetPhi", "hJetPhi", kTH1D, {axisConfigurations.axisPhi}); @@ -843,20 +853,20 @@ struct lambdajetpolarizationions { histos.add("JetKinematicsQA/hDeltaEtaToLeadingJet", "hDeltaEtaToLeadingJet", kTH1D, {axisConfigurations.axisDeltaEta}); histos.add("JetKinematicsQA/hDeltaRToLeadingJet", "hDeltaRToLeadingJet", kTH1D, {axisConfigurations.axisDeltaR}); - histos.add("JetKinematicsQA/hLeadingJetPt", "hLeadingJetPt", kTH1D, {axisConfigurations.axisPt}); + histos.add("JetKinematicsQA/hLeadingJetPt", "hLeadingJetPt", kTH1D, {axisConfigurations.axisJetPt}); histos.add("JetKinematicsQA/hLeadingJetEta", "hLeadingJetEta", kTH1D, {axisConfigurations.axisEta}); histos.add("JetKinematicsQA/hLeadingJetPhi", "hLeadingJetPhi", kTH1D, {axisConfigurations.axisPhi}); // 2D correlations: - histos.add("JetKinematicsQA/h2dJetsPerEventvsLeadJetPt", "h2dJetsPerEventvsLeadJetPt", kTH2D, {axisConfigurations.JetsPerEvent, axisConfigurations.axisPt}); - histos.add("JetKinematicsQA/h2dJetsPerEventvsJetPt", "h2dJetsPerEventvsJetPt", kTH2D, {axisConfigurations.JetsPerEvent, axisConfigurations.axisPt}); + histos.add("JetKinematicsQA/h2dJetsPerEventvsLeadJetPt", "h2dJetsPerEventvsLeadJetPt", kTH2D, {axisConfigurations.JetsPerEvent, axisConfigurations.axisJetPt}); + histos.add("JetKinematicsQA/h2dJetsPerEventvsJetPt", "h2dJetsPerEventvsJetPt", kTH2D, {axisConfigurations.JetsPerEvent, 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 // Comparisons to jet energy: - histos.add("JetKinematicsQA/h2dJetPtvsDeltaPhiToLead","h2dJetPtvsDeltaPhiToLead",kTH2D,{axisConfigurations.axisPt, axisConfigurations.axisDeltaPhi}); + 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}); @@ -867,7 +877,7 @@ struct lambdajetpolarizationions { //////////////////////////// // Leading particle 1D QA: - histos.add("JetVsLeadingParticleQA/hLeadingParticlePt","hLeadingParticlePt",kTH1D,{axisConfigurations.axisPt}); + histos.add("JetVsLeadingParticleQA/hLeadingParticlePt","hLeadingParticlePt",kTH1D,{axisConfigurations.axisLeadingParticlePt}); histos.add("JetVsLeadingParticleQA/hLeadingParticleEta","hLeadingParticleEta",kTH1D,{axisConfigurations.axisEta}); histos.add("JetVsLeadingParticleQA/hLeadingParticlePhi","hLeadingParticlePhi",kTH1D,{axisConfigurations.axisPhi}); @@ -885,13 +895,12 @@ struct lambdajetpolarizationions { histos.add("JetVsLeadingParticleQA/h2dJetsPerEventvsCosThetaParticleToLead","h2dJetsPerEventvsCosThetaParticleToLead",kTH2D,{axisConfigurations.JetsPerEvent, axisConfigurations.axisCosTheta}); // Main "Leading jet vs leading particle" correlations: - histos.add("JetVsLeadingParticleQA/h2dJetsPerEventvsLeadParticlePt","h2dJetsPerEventvsLeadParticlePt",kTH2D,{axisConfigurations.JetsPerEvent, axisConfigurations.axisPt}); - histos.add("JetVsLeadingParticleQA/h2dLeadJetPtvsLeadParticlePt","h2dLeadJetPtvsLeadParticlePt",kTH2D,{axisConfigurations.axisPt, axisConfigurations.axisPt}); - histos.add("JetVsLeadingParticleQA/h2dLeadJetPtvsCosThetaParticleToLead","h2dLeadJetPtvsCosThetaParticleToLead",kTH2D,{axisConfigurations.axisPt, axisConfigurations.axisCosTheta}); - histos.add("JetVsLeadingParticleQA/h2dLeadParticlePtvsCosThetaParticleToLead","h2dLeadParticlePtvsCosThetaParticleToLead",kTH2D,{axisConfigurations.axisPt, axisConfigurations.axisCosTheta}); - histos.add("JetVsLeadingParticleQA/h2dLeadJetPtvsDeltaPhiParticleToLead","h2dLeadJetPtvsDeltaPhiParticleToLead",kTH2D,{axisConfigurations.axisPt, axisConfigurations.axisDeltaPhi}); - histos.add("JetVsLeadingParticleQA/h2dLeadParticlePtvsDeltaPhiParticleToLead","h2dLeadParticlePtvsDeltaPhiParticleToLead",kTH2D,{axisConfigurations.axisPt, axisConfigurations.axisDeltaPhi}); - + histos.add("JetVsLeadingParticleQA/h2dJetsPerEventvsLeadParticlePt","h2dJetsPerEventvsLeadParticlePt",kTH2D,{axisConfigurations.JetsPerEvent, 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}); } // inspect histogram sizes, please @@ -1249,37 +1258,34 @@ struct lambdajetpolarizationions { if (std::fabs(pionTrack.tpcNSigmaPi()) > v0Selections.tpcPidNsigmaCut) return false; V0SelCounter.fill(); - // // TOF PID in DeltaT (if TOF is not available, then uses the track. If is available, uses it. In this sense, TOF is optional) - // // const bool posHasTOF = posTrackExtra.hasTOF(); // For the older version, which worked only for Lambdas - // const bool protonHasTOF = protonTrack.hasTOF(); - // const bool pionHasTOF = pionTrack.hasTOF(); - - // // Proton-like track - // if (protonHasTOF && std::abs(Lambda_hypothesis ? v0.posTOFDeltaTLaPr() - // : v0.negTOFDeltaTLaPr()) > v0Selections.maxDeltaTimeProton) return false; - // V0SelCounter.fill(); - // // Pion-like track - // if (pionHasTOF && std::abs(Lambda_hypothesis ? v0.negTOFDeltaTLaPi() - // : v0.posTOFDeltaTLaPi()) > v0Selections.maxDeltaTimePion) return false; - // V0SelCounter.fill(); - - // // TOF PID in NSigma (TODO: add asymmetric NSigma windows for purity tuning?) - // // Proton-like track - // // if (protonHasTOF && std::fabs(v0.tofNSigmaLaPr()) > v0Selections.tofPidNsigmaCutLaPr) return false; - // // Getter for raw data's PIDResponseTOF.h instead of LFStrangenessPIDTables.h: - // if (protonHasTOF && std::fabs(v0.tofNSigmaLaPr()) > v0Selections.tofPidNsigmaCutLaPr) return false; - // V0SelCounter.fill(); - // // Pion-like track - // if (pionHasTOF && std::fabs(v0.tofNSigmaLaPi()) > v0Selections.tofPidNsigmaCutLaPi) return false; - // V0SelCounter.fill(); - - // // // PID Selections (TOF) - // // if (requireTOF) { - // // if (ptrack.tofNSigmaPr() < nsigmaTOFmin || ptrack.tofNSigmaPr() > nsigmaTOFmax) - // // return false; - // // if (ntrack.tofNSigmaPi() < nsigmaTOFmin || ntrack.tofNSigmaPi() > nsigmaTOFmax) - // // return false; - // // } + // TOF PID in DeltaT (if TOF is not available, then uses the track. If is available, uses it. In this sense, TOF is optional) + // const bool posHasTOF = posTrackExtra.hasTOF(); // For the older version, which worked only for Lambdas + const bool protonHasTOF = protonTrack.hasTOF(); // Should work even without PIDResponseTOF.h, as it is a TracksExtra property + const bool pionHasTOF = pionTrack.hasTOF(); + + // Proton-like track + if (protonHasTOF && std::abs(Lambda_hypothesis ? v0.posTOFDeltaTLaPr() : v0.negTOFDeltaTLaPr()) + > v0Selections.maxDeltaTimeProton) return false; + V0SelCounter.fill(); + // Pion-like track + if (pionHasTOF && std::abs(Lambda_hypothesis ? v0.negTOFDeltaTLaPi() : v0.posTOFDeltaTLaPi()) + > v0Selections.maxDeltaTimePion) return false; + V0SelCounter.fill(); + + // TOF PID in NSigma (TODO: add asymmetric NSigma windows for purity tuning?) + // Proton-like track + if (protonHasTOF && std::fabs(v0.tofNSigmaLaPr()) > v0Selections.tofPidNsigmaCutLaPr) return false; // (No need to select which candidate is which with the Lambda_hypothesis. Automatically done already!) + V0SelCounter.fill(); + // Pion-like track + if (pionHasTOF && std::fabs(v0.tofNSigmaLaPi()) > v0Selections.tofPidNsigmaCutLaPi) return false; + 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, instead of using the correct path length from the + // V0s PV-DCA and the such! In other words, it is a naive estimator of TOF PID, because it does not correct for the V0 + // mother's travel time and considers all tracks as if they came from the PV! + // 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. // proper lifetime if (v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * o2::constants::physics::MassLambda0 > v0Selections.lambdaLifetimeCut) return false; @@ -1546,12 +1552,10 @@ struct lambdajetpolarizationions { } // Had to include DauTracks in subscription, even though I don't loop in it, for the indices to resolve, avoiding " Exception while running: Index pointing to Tracks is not bound!" - void processV0sData(SelCollisions::iterator const& collision, V0Candidates const& fullV0s, aod::BCsWithTimestamps const& bcs, DauTracks const& V0DauTracks){ + void processV0sData(SelCollisions::iterator const& collision, V0CandidatesWithTOF const& fullV0s, aod::BCsWithTimestamps const& bcs, DauTracks const& V0DauTracks){ const float centrality = getCentrality(collision); - if (doEventQA) { - histos.fill(HIST("hEventSelection"), 0. /* all collisions */); - histos.fill(HIST("hEventSelectionVsCentrality"), 0. /* all collisions */, centrality); - } + histos.fill(HIST("hEventSelection"), 0. /* all collisions */); + histos.fill(HIST("hEventSelectionVsCentrality"), 0. /* all collisions */, centrality); auto bc = bcs.iteratorAt(collision.bcId()); if (!isEventAccepted(collision, bc, centrality, doEventQA)) return; // Uses return instead of continue, as there is no explicit loop here @@ -1667,20 +1671,20 @@ struct lambdajetpolarizationions { histos.fill(HIST("Lambda/h3dPosTPCsignalVsTrackPt"), centrality, v0.positivept(), posTrackExtra.tpcSignal()); histos.fill(HIST("Lambda/h3dNegTPCsignalVsTrackPt"), centrality, v0.negativept(), negTrackExtra.tpcSignal()); } - // if (doTOFQA) { - // histos.fill(HIST("Lambda/h3dPosNsigmaTOF"), centrality, v0pt, v0.tofNSigmaLaPr()); - // histos.fill(HIST("Lambda/h3dNegNsigmaTOF"), centrality, v0pt, v0.tofNSigmaLaPi()); - // histos.fill(HIST("Lambda/h3dPosTOFdeltaT"), centrality, v0pt, v0.posTOFDeltaTLaPr()); - // histos.fill(HIST("Lambda/h3dNegTOFdeltaT"), centrality, v0pt, v0.negTOFDeltaTLaPi()); - // histos.fill(HIST("Lambda/h3dPosNsigmaTOFvsTrackPtot"), centrality, v0.pfracpos() * v0.p(), v0.tofNSigmaLaPr()); - // histos.fill(HIST("Lambda/h3dNegNsigmaTOFvsTrackPtot"), centrality, v0.pfracneg() * v0.p(), v0.tofNSigmaLaPi()); - // histos.fill(HIST("Lambda/h3dPosTOFdeltaTvsTrackPtot"), centrality, v0.pfracpos() * v0.p(), v0.posTOFDeltaTLaPr()); - // histos.fill(HIST("Lambda/h3dNegTOFdeltaTvsTrackPtot"), centrality, v0.pfracneg() * v0.p(), v0.negTOFDeltaTLaPi()); - // histos.fill(HIST("Lambda/h3dPosNsigmaTOFvsTrackPt"), centrality, v0.positivept(), v0.tofNSigmaLaPr()); - // histos.fill(HIST("Lambda/h3dNegNsigmaTOFvsTrackPt"), centrality, v0.negativept(), v0.tofNSigmaLaPi()); - // histos.fill(HIST("Lambda/h3dPosTOFdeltaTvsTrackPt"), centrality, v0.positivept(), v0.posTOFDeltaTLaPr()); - // histos.fill(HIST("Lambda/h3dNegTOFdeltaTvsTrackPt"), centrality, v0.negativept(), v0.negTOFDeltaTLaPi()); - // } + if (doTOFQA) { + histos.fill(HIST("Lambda/h3dPosNsigmaTOF"), centrality, v0pt, v0.tofNSigmaLaPr()); + histos.fill(HIST("Lambda/h3dNegNsigmaTOF"), centrality, v0pt, v0.tofNSigmaLaPi()); + histos.fill(HIST("Lambda/h3dPosTOFdeltaT"), centrality, v0pt, v0.posTOFDeltaTLaPr()); + histos.fill(HIST("Lambda/h3dNegTOFdeltaT"), centrality, v0pt, v0.negTOFDeltaTLaPi()); + histos.fill(HIST("Lambda/h3dPosNsigmaTOFvsTrackPtot"), centrality, v0.pfracpos() * v0.p(), v0.tofNSigmaLaPr()); + histos.fill(HIST("Lambda/h3dNegNsigmaTOFvsTrackPtot"), centrality, v0.pfracneg() * v0.p(), v0.tofNSigmaLaPi()); + histos.fill(HIST("Lambda/h3dPosTOFdeltaTvsTrackPtot"), centrality, v0.pfracpos() * v0.p(), v0.posTOFDeltaTLaPr()); + histos.fill(HIST("Lambda/h3dNegTOFdeltaTvsTrackPtot"), centrality, v0.pfracneg() * v0.p(), v0.negTOFDeltaTLaPi()); + histos.fill(HIST("Lambda/h3dPosNsigmaTOFvsTrackPt"), centrality, v0.positivept(), v0.tofNSigmaLaPr()); + histos.fill(HIST("Lambda/h3dNegNsigmaTOFvsTrackPt"), centrality, v0.negativept(), v0.tofNSigmaLaPi()); + histos.fill(HIST("Lambda/h3dPosTOFdeltaTvsTrackPt"), centrality, v0.positivept(), v0.posTOFDeltaTLaPr()); + 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()); @@ -1713,20 +1717,20 @@ struct lambdajetpolarizationions { histos.fill(HIST("AntiLambda/h3dPosTPCsignalVsTrackPt"), centrality, v0.positivept(), posTrackExtra.tpcSignal()); histos.fill(HIST("AntiLambda/h3dNegTPCsignalVsTrackPt"), centrality, v0.negativept(), negTrackExtra.tpcSignal()); } - // if (doTOFQA) { - // histos.fill(HIST("AntiLambda/h3dPosNsigmaTOF"), centrality, v0pt, v0.tofNSigmaALaPi()); - // histos.fill(HIST("AntiLambda/h3dNegNsigmaTOF"), centrality, v0pt, v0.tofNSigmaALaPr()); - // histos.fill(HIST("AntiLambda/h3dPosTOFdeltaT"), centrality, v0pt, v0.posTOFDeltaTLaPi()); - // histos.fill(HIST("AntiLambda/h3dNegTOFdeltaT"), centrality, v0pt, v0.negTOFDeltaTLaPr()); - // histos.fill(HIST("AntiLambda/h3dPosNsigmaTOFvsTrackPtot"), centrality, v0.pfracpos() * v0.p(), v0.tofNSigmaALaPi()); - // histos.fill(HIST("AntiLambda/h3dNegNsigmaTOFvsTrackPtot"), centrality, v0.pfracneg() * v0.p(), v0.tofNSigmaALaPr()); - // histos.fill(HIST("AntiLambda/h3dPosTOFdeltaTvsTrackPtot"), centrality, v0.pfracpos() * v0.p(), v0.posTOFDeltaTLaPi()); - // histos.fill(HIST("AntiLambda/h3dNegTOFdeltaTvsTrackPtot"), centrality, v0.pfracneg() * v0.p(), v0.negTOFDeltaTLaPr()); - // histos.fill(HIST("AntiLambda/h3dPosNsigmaTOFvsTrackPt"), centrality, v0.positivept(), v0.tofNSigmaALaPi()); - // histos.fill(HIST("AntiLambda/h3dNegNsigmaTOFvsTrackPt"), centrality, v0.negativept(), v0.tofNSigmaALaPr()); - // histos.fill(HIST("AntiLambda/h3dPosTOFdeltaTvsTrackPt"), centrality, v0.positivept(), v0.posTOFDeltaTLaPi()); - // histos.fill(HIST("AntiLambda/h3dNegTOFdeltaTvsTrackPt"), centrality, v0.negativept(), v0.negTOFDeltaTLaPr()); - // } + if (doTOFQA) { + histos.fill(HIST("AntiLambda/h3dPosNsigmaTOF"), centrality, v0pt, v0.tofNSigmaALaPi()); + histos.fill(HIST("AntiLambda/h3dNegNsigmaTOF"), centrality, v0pt, v0.tofNSigmaALaPr()); + histos.fill(HIST("AntiLambda/h3dPosTOFdeltaT"), centrality, v0pt, v0.posTOFDeltaTLaPi()); + histos.fill(HIST("AntiLambda/h3dNegTOFdeltaT"), centrality, v0pt, v0.negTOFDeltaTLaPr()); + histos.fill(HIST("AntiLambda/h3dPosNsigmaTOFvsTrackPtot"), centrality, v0.pfracpos() * v0.p(), v0.tofNSigmaALaPi()); + histos.fill(HIST("AntiLambda/h3dNegNsigmaTOFvsTrackPtot"), centrality, v0.pfracneg() * v0.p(), v0.tofNSigmaALaPr()); + histos.fill(HIST("AntiLambda/h3dPosTOFdeltaTvsTrackPtot"), centrality, v0.pfracpos() * v0.p(), v0.posTOFDeltaTLaPi()); + histos.fill(HIST("AntiLambda/h3dNegTOFdeltaTvsTrackPtot"), centrality, v0.pfracneg() * v0.p(), v0.negTOFDeltaTLaPr()); + histos.fill(HIST("AntiLambda/h3dPosNsigmaTOFvsTrackPt"), centrality, v0.positivept(), v0.tofNSigmaALaPi()); + histos.fill(HIST("AntiLambda/h3dNegNsigmaTOFvsTrackPt"), centrality, v0.negativept(), v0.tofNSigmaALaPr()); + histos.fill(HIST("AntiLambda/h3dPosTOFdeltaTvsTrackPt"), centrality, v0.positivept(), v0.posTOFDeltaTLaPi()); + 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()); From 5692752897d6d5d7db03cb4bfe0635b06bf84265 Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Wed, 11 Feb 2026 19:12:36 -0300 Subject: [PATCH 12/40] Adding collision column to derived data to reduce saves of centrality, adding N Jet constituents column. Renaming tables to keep the "s" convention. --- PWGLF/DataModel/lambdaJetPolarizationIons.h | 17 +++++--- .../Strangeness/lambdaJetPolarizationIons.cxx | 42 +++++++++++-------- 2 files changed, 37 insertions(+), 22 deletions(-) diff --git a/PWGLF/DataModel/lambdaJetPolarizationIons.h b/PWGLF/DataModel/lambdaJetPolarizationIons.h index 3d109c4910a..21867387b98 100644 --- a/PWGLF/DataModel/lambdaJetPolarizationIons.h +++ b/PWGLF/DataModel/lambdaJetPolarizationIons.h @@ -29,11 +29,12 @@ namespace lambdajetpol { DECLARE_SOA_COLUMN(CollIdx, collIdx, uint64_t); -DECLARE_SOA_COLUMN(CentFT0M, centFT0M, float); +DECLARE_SOA_COLUMN(Centrality, centrality, float); DECLARE_SOA_COLUMN(JetPt, jetPt, float); DECLARE_SOA_COLUMN(JetEta, jetEta, float); DECLARE_SOA_COLUMN(JetPhi, jetPhi, float); +DECLARE_SOA_COLUMN(JetNConstituents, jetNConstituents, uint64_t); DECLARE_SOA_COLUMN(V0Pt, v0Pt, float); DECLARE_SOA_COLUMN(V0Eta, v0Eta, float); @@ -51,17 +52,19 @@ DECLARE_SOA_COLUMN(NegPt, negPt, float); DECLARE_SOA_COLUMN(NegEta, negEta, float); DECLARE_SOA_COLUMN(NegPhi, negPhi, float); +// (TODO: add dynamic columns with jet px, py, pz) + } // namespace lambdajetpol -DECLARE_SOA_TABLE(JetsRing, "AOD", "JETSRING", +DECLARE_SOA_TABLE(RingJets, "AOD", "RINGJETS", // Renamed to follow convention on "s" at the end of table name. lambdajetpol::CollIdx, lambdajetpol::JetPt, lambdajetpol::JetEta, - lambdajetpol::JetPhi); + lambdajetpol::JetPhi, + lambdajetpol::JetNConstituents); -DECLARE_SOA_TABLE(LambdaLikeV0sRing, "AOD", "LAMBDALIKEV0SRING", +DECLARE_SOA_TABLE(RingLambdaLikeV0s, "AOD", "RINGLAMBDALIKEV0S", lambdajetpol::CollIdx, - lambdajetpol::CentFT0M, lambdajetpol::V0Pt, lambdajetpol::V0Eta, lambdajetpol::V0Phi, @@ -75,6 +78,10 @@ DECLARE_SOA_TABLE(LambdaLikeV0sRing, "AOD", "LAMBDALIKEV0SRING", lambdajetpol::NegPt, lambdajetpol::NegEta, lambdajetpol::NegPhi); + +DECLARE_SOA_TABLE(RingCollisions, "AOD", "RINGCOLLISIONS", + lambdajetpol::CollIdx, + lambdajetpol::Centrality); } // namespace o2::aod #endif // PWGLF_DATAMODEL_lambdajetpol_H_ \ No newline at end of file diff --git a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIons.cxx b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIons.cxx index 2ed010a2f83..33afdc2ce8e 100644 --- a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIons.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIons.cxx @@ -147,8 +147,9 @@ struct lambdajetpolarizationions { // struct : ProducesGroup { // } products; - Produces tableV0s; - Produces tableJets; + Produces tableV0s; + Produces tableJets; + Produces tableCollisions; // Define histogram registries: HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; @@ -1015,7 +1016,7 @@ struct lambdajetpolarizationions { return false; // reject track } - inline double cosThetaJets(const fastjet::PseudoJet& a, const fastjet::PseudoJet& b){ + inline float cosThetaJets(const fastjet::PseudoJet& a, const fastjet::PseudoJet& b){ const double dot = a.px() * b.px() + a.py() * b.py() + a.pz() * b.pz(); const double magA = std::sqrt(a.px()*a.px() + a.py()*a.py() + a.pz()*a.pz()); const double magB = std::sqrt(b.px()*b.px() + b.py()*b.py() + b.pz()*b.pz()); @@ -1367,7 +1368,8 @@ struct lambdajetpolarizationions { tableJets(collIdx, jetMinusBkg.pt(), jetMinusBkg.eta(), // Using eta instead of rapidity - jetMinusBkg.phi() + jetMinusBkg.phi(), + jetMinusBkg.constituents().size() ); // Finding the leading jet after subtraction (leading jet is NOT known a priori!): @@ -1393,10 +1395,10 @@ struct lambdajetpolarizationions { if (jetMinusBkg.pt() < jetConfigurations.minJetPt) continue; - double cosTheta = cosThetaJets(leadingJetSub, jetMinusBkg); - double deltaPhi = leadingJetSub.phi() - jetMinusBkg.phi(); - double deltaEta = leadingJetSub.eta() - jetMinusBkg.eta(); - double deltaR = std::sqrt(deltaPhi*deltaPhi + deltaEta*deltaEta); + float cosTheta = cosThetaJets(leadingJetSub, jetMinusBkg); + float deltaPhi = leadingJetSub.phi() - jetMinusBkg.phi(); + float deltaEta = leadingJetSub.eta() - jetMinusBkg.eta(); + float deltaR = std::sqrt(deltaPhi*deltaPhi + deltaEta*deltaEta); histos.fill(HIST("JetKinematicsQA/hCosThetaToLeadingJet"), cosTheta); histos.fill(HIST("JetKinematicsQA/hDeltaPhiToLeadingJet"), deltaPhi); @@ -1426,9 +1428,9 @@ struct lambdajetpolarizationions { histos.fill(HIST("JetVsLeadingParticleQA/hLeadingParticleEta"), leadingParticle.eta()); histos.fill(HIST("JetVsLeadingParticleQA/hLeadingParticlePhi"), leadingParticle.phi()); - double deltaPhiParticleToJet = leadingJetSub.phi() - leadingParticle.phi(); - double deltaEtaParticleToJet = leadingJetSub.eta() - leadingParticle.eta(); - double cosThetaParticleToJet = cosThetaJets(leadingJetSub, leadingParticle); // Takes advantage of the fact that this leading particle is a PseudoJet object + float deltaPhiParticleToJet = leadingJetSub.phi() - leadingParticle.phi(); + 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); @@ -1475,7 +1477,8 @@ struct lambdajetpolarizationions { tableJets(collIdx, jet.pt(), jet_eta, // Using eta instead of rapidity - jet.phi() + jet.phi(), + jet.constituents().size() ); if (doJetKinematicsQA){ @@ -1484,12 +1487,12 @@ struct lambdajetpolarizationions { histos.fill(HIST("JetKinematicsQA/hJetPhi"), jet.phi()); // Calculate angle to leading jet: - double cosTheta = cosThetaJets(leadingJet, jet); + float cosTheta = cosThetaJets(leadingJet, jet); // Calculate angular separation in projected angles: - double deltaPhi = leadingJet.phi() - jet.phi(); - double deltaEta = leadingJet.eta() - jet_eta; - double deltaR = std::sqrt(deltaPhi*deltaPhi + deltaEta*deltaEta); // 2D angular distance in the eta-phi plane + float deltaPhi = leadingJet.phi() - jet.phi(); + float deltaEta = leadingJet.eta() - jet_eta; + 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! histos.fill(HIST("JetKinematicsQA/hDeltaPhiToLeadingJet"), deltaPhi); @@ -1564,6 +1567,7 @@ struct lambdajetpolarizationions { const uint64_t collIdx = collision.globalIndex(); if (v0Selections.rejectTPCsectorBoundary) initCCDB(bc); // Substituted call from collision to bc for raw data + bool hasValidV0 = false; // Bool to know if event information can be saved. for (auto const& v0 : fullV0s){ V0SelCounter.resetForNewV0(); V0SelCounter.fill(); // Fill for all v0 candidates @@ -1579,6 +1583,7 @@ struct lambdajetpolarizationions { if (analyseAntiLambda) isAntiLambda = passesLambdaLambdaBarHypothesis(v0, collision, false); if (!isLambda && !isAntiLambda) continue; // Candidate is not considered to be a Lambda (TODO: expand this to a full if block with QA about rejections) + hasValidV0 = true; if (doArmenterosQA) histos.fill(HIST("GeneralQA/h2dArmenterosSelected"), v0.alpha(), v0.qtarm()); // cross-check if (isLambda && isAntiLambda) histos.fill(HIST("hAmbiguousLambdaCandidates"), 0); @@ -1591,7 +1596,6 @@ struct lambdajetpolarizationions { // Saving the Lambdas into a derived data column: auto const v0pt = v0.pt(); tableV0s(collIdx, - centrality, v0pt, v0.eta(), // Using eta instead of rapidity v0.phi(), @@ -1739,6 +1743,10 @@ struct lambdajetpolarizationions { } } // end CompleteTopoQA } + if (hasValidV0){ + tableCollisions(collIdx, + centrality); // (TODO: add InteractionRate info and other useful cuts for later on in the analysis!) + } } PROCESS_SWITCH(lambdajetpolarizationions, processJetsData, "Process jets and produced derived data in Run 3 Data", true); From 98bf92cda93bb16eec9f5f9a2057e214695c96a6 Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Thu, 12 Feb 2026 10:37:20 -0300 Subject: [PATCH 13/40] First version of polarization code, with traditional ROOT vectors (INCOMPLETE CODE, just bookkeeping!) --- .../lambdaJetPolarizationIonsDerived.cxx | 200 ++++++++++++++++++ 1 file changed, 200 insertions(+) create mode 100644 PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx diff --git a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx new file mode 100644 index 00000000000..7667044ef13 --- /dev/null +++ b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx @@ -0,0 +1,200 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +/// \file 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 +// ================ +// +// This code loops over custom derived data tables defined on +// lambdaJetPolarizationIons.h (JetsRing, LambdaLikeV0sRing). +// From this derived data, calculates polarization on an EbE +// basis. +// +// +// Comments, questions, complaints, suggestions? +// Please write to: +// cicero.domenico.muncinelli@cern.ch +// + +#include +#include +#include +#include +#include +#include + +// Custom data model: +#include "PWGLF/DataModel/lambdaJetPolarizationIons.h" + +#include +#include +#include +#include + +// #include +// #include +// New recommended format: +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace ROOT::Math; +// using namespace o2::aod::lambdajetpol; // Used it explicitly along the code for clarity +struct lambdaJetPolarizationIonsDerived { + const double protonMass = o2::constants::physics::MassProton; // Assumes particle identification for daughter is perfect + const double lambdaWeakDecayConstant = 0.747; // Fixed as a constant for this analysis. + + + + + + + void processPolarizationData(aod::RingCollisions const& collisions, aod::RingJets const& jets, aod::RingLambdaLikeV0s const& v0s){ + + for (auto const& collision : collisions) { + const auto collId = collision.collIdx(); + const double centrality = collision.centrality(); + + // Slice jets and V0s belonging to this collision + auto jetsInColl = jets.sliceBy(o2::aod::lambdajetpol::collIdx, collId); + auto v0sInColl = v0s.sliceBy(o2::aod::lambdajetpol::collIdx, 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!) + if (jetsInColl.empty() || v0sInColl.empty()) continue; + + // Get leading jet: + double leadingJetPt = -1; + o2::aod::RingJets::iterator leadingJet; + for (auto const& jet : jetsInColl) { + const auto jetpt = jet.jetPt(); + if (jetpt > leadingJetPt){ + leadingJetPt = jetpt; + leadingJet = jet; + } + } + + // Now you can use: + const double leadingJetEta = leadingJet.jetEta(); + const double leadingJetPhi = leadingJet.jetPhi(); + + // Convert to 3-vector components for inner product: + const double jetPx = leadingJetPt * std::cos(leadingJetPhi); + const double jetPy = leadingJetPt * std::sin(leadingJetPhi); + const double jetPz = leadingJetPt * std::sinh(leadingJetEta); + + // TODO: add centrality selection procedure and options (one configurable for no centrality separation at all too!) + // TODO: add Lambda candidate selection. Think of a statistical method like signal extraction (if possible) for ring polarization + // TODO: add calculations with second to leading jet too. + // TODO: Add calculations with leading particle + for (auto const& v0 : v0sInColl) { + const double v0pt = v0.V0Pt(); + const double v0eta = v0.v0Eta(); + const double v0phi = v0.Phi(); + + double v0LambdaMass; + double protonLikePt; + double protonLikeEta; + double protonLikePhi; + if (isLambda){ + v0LambdaMass = v0.MassLambda(); + protonLikePt = v0.PosPt(); + protonLikeEta = v0.PosEta(); + protonLikePhi = v0.PosPhi(); + } + // (TODO: implement AntiLambda polarization) + // if (isAntiLambda){ + // } + + const double lambdaPx = v0pt * std::cos(v0phi); + const double lambdaPy = v0pt * std::sin(v0phi); + const double lambdaPz = v0pt * std::sinh(v0eta); + const double lambdaMomentumSquared = lambdaPx*lambdaPx + lambdaPy*lambdaPy + lambdaPz*lambdaPz; + const double lambdaE = std::sqrt(v0LambdaMass*v0LambdaMass + lambdaMomentumSquared); + // const double lambdaRapidity = 0.5 * std::log((lambdaE + lambdaPz) / (lambdaE - lambdaPz)); // For extra selection criteria later + const double lambdaRapidity = std::atanh(lambdaPz / lambdaE); // More numerically stable + + const double protonPx = protonLikePt * std::cos(protonLikePhi); + const double protonPy = protonLikePt * std::sin(protonLikePhi); + const double protonPz = protonLikePt * std::sinh(protonLikeEta); + const double protonMomentumSquared = protonPx*protonPx + protonPy*protonPy + protonPz*protonPz; + const double protonE = std::sqrt(protonMass*protonMass + protonMomentumSquared); + + TLorentzVector lambdaLike4Vec(lambdaPx, lambdaPy, lambdaPz, lambdaE); + TLorentzVector protonLike4Vec(protonPx, protonPy, protonPz, protonE); + + // Boosting proton into lambda frame: + TVector3 betaInverse = -lambdaLike4Vec.BoostVector(); // Boost trivector that goes from laboratory frame to the rest frame + TLorentzVector protonLike4VecStar = protonLike4Vec; + protonLike4VecStar.Boost(betaInverse); + + TVector3 protonLikeStarUnitVec = (protonLike4VecStar.Vect()).Unit(); + TVector3 jetUnitVec = (TVector3(jetPx, jetPy, jetPz)).Unit(); + TVector3 lambda3Vec = TVector3(lambdaPx, lambdaPy, lambdaPz); + + // Calculating inner product: + double crossX = jetUnitVec.Y()*lambdaPz - jetUnitVec.Z()*lambdaPy; + double crossY = jetUnitVec.Z()*lambdaPx - jetUnitVec.X()*lambdaPz; + double crossZ = jetUnitVec.X()*lambdaPy - jetUnitVec.Y()*lambdaPx; + double crossProductNorm = std::sqrt(crossX*crossX + crossY*crossY + crossZ*crossZ); + + double ringObservable = 3./(lambdaWeakDecayConstant) * ((protonLikeStarUnitVec.X()*crossX + + protonLikeStarUnitVec.Y()*crossY + protonLikeStarUnitVec.Z()*cross_z)/crossProductNorm); + + // Calculating error bars: + double ringObservableSquared = ringObservable*ringObservable; + + // Angular variables: + double deltaPhiJet = v0phi - leadingJetPhi; + double deltaThetaJet = ; // 3D angular separation + + // Fill ring histograms: (1D, lambda 2D correlations and jet 2D correlations): (TODO) + histos.fill(HIST("Ring/hRingObservableDeltaPhi"), 0); + histos.fill(HIST("Ring/hRingObservableIntegrated"), 0); + + histos.fill(HIST("Ring/hRingObservableDeltaPhi"), 0); + histos.fill(HIST("Ring/hRingObservableIntegrated"), 0); + + + // 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){ + histos.fill(HIST("RingKinematicCuts/hRingObservableDeltaPhi"), 0); + histos.fill(HIST("RingKinematicCuts/hRingObservableIntegrated"), 0); + } + + + // Extra selection criteria on jet candidates: + const bool kinematicJetCheck = std::abs(leadingJetEta) < 0.5; + if (kinematicJetCheck){ // This is redundant for jets with R=0.4, but for jets with R<0.4 the leading jet may be farther in eta. + + } + + + // Extra selection criteria on Lambda and jet candidates: + if (kinematicLambdaCheck && kinematicJetCheck){ + + } + + + } // end v0s loop + } // end collisions + } +}; \ No newline at end of file From 5a6c37b46dd2262d194f3ea499006c51e11ecca3 Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Fri, 13 Feb 2026 13:35:23 -0300 Subject: [PATCH 14/40] First full code version (still has problems with histogram filling logic. Will switch to a regular compile-time histogram naming, even though that adds a lot of repeated logic --- .../lambdaJetPolarizationIonsDerived.cxx | 323 ++++++++++++++---- 1 file changed, 255 insertions(+), 68 deletions(-) diff --git a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx index 7667044ef13..d572a6f1c39 100644 --- a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx @@ -9,7 +9,7 @@ // 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 @@ -20,7 +20,9 @@ // This code loops over custom derived data tables defined on // lambdaJetPolarizationIons.h (JetsRing, LambdaLikeV0sRing). // From this derived data, calculates polarization on an EbE -// basis. +// basis (see TProfiles). +// Signal extraction is done out of the framework, based on +// the AnalysisResults of this code. // // // Comments, questions, complaints, suggestions? @@ -48,30 +50,218 @@ // New recommended format: #include #include +#include using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; -using namespace ROOT::Math; +using ROOT::Math::XYZVector; +using ROOT::Math::PtEtaPhiMVector; +using namespace ROOT::Math::VectorUtil; // using namespace o2::aod::lambdajetpol; // Used it explicitly along the code for clarity -struct lambdaJetPolarizationIonsDerived { - const double protonMass = o2::constants::physics::MassProton; // Assumes particle identification for daughter is perfect - const double lambdaWeakDecayConstant = 0.747; // Fixed as a constant for this analysis. + // 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 + +struct lambdajetpolarizationionsderived { + + // Define histogram registries: + HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + // Master analysis switches + Configurable analyseLambda{"analyseLambda", true, "process Lambda-like candidates"}; + Configurable analyseAntiLambda{"analyseAntiLambda", false, "process AntiLambda-like candidates"}; + Configurable doPPAnalysis{"doPPAnalysis", false, "if in pp, set to true. Default is HI"}; + + ///////////////////////// + // Configurable blocks: + // Histogram axes configuration: + 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 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}, "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",{100, 0.f, 200.f},"Jet p_{t} (GeV)"}; + ConfigurableAxis axisCosTheta{"axisDeltaTheta", {100, 0, constants::math::PI}, "#Delta #theta_{jet}"}; + ConfigurableAxis axisDeltaPhi{"axisDeltaPhi", {100, -constants::math::PI, constants::math::PI}, "#Delta #phi_{jet}"}; + } axisConfigurations; + + + // Helper functions: + // Fast wrapping into [-PI, PI) (restricted to this interval for function speed) + inline double wrapToPiFast(double phi){ + constexpr double TwoPi = o2::constants::math::TwoPI; + constexpr double Pi = o2::constants::math::PI; + if (phi >= Pi) phi -= TwoPi; + else if (phi < -Pi) phi += TwoPi; + return phi; + } + void init(InitContext const&){ + // Ring observable histograms: + // Helper to register one full histogram family (kinematic cut variation of ring observable) + auto addRingObservableFamily = [&](const std::string& folder){ + // =============================== + // 1D observable histograms + // =============================== + histos.add((folder + "/hRingObservableDeltaPhi").c_str(), "hRingObservableDeltaPhi", kTH1D, {axisConfigurations.axisDeltaPhi}); + histos.add((folder + "/hRingObservableDeltaTheta").c_str(), "hRingObservableDeltaTheta", kTH1D, {axisConfigurations.axisCosTheta}); + histos.add((folder + "/hRingObservableIntegrated").c_str(), "hRingObservableIntegrated", kTH1D, {{1, -0.5, 0.5}}); + // Counters (denominators) + histos.add((folder + "/hDeltaPhi").c_str(), "hDeltaPhi", kTH1D, {axisConfigurations.axisDeltaPhi}); + histos.add((folder + "/hDeltaTheta").c_str(), "hDeltaTheta", kTH1D, {axisConfigurations.axisCosTheta}); + histos.add((folder + "/hIntegrated").c_str(), "hIntegrated", kTH1D, {{1, -0.5, 0.5}}); + // =============================== + // Lambda pT dependence + // =============================== + histos.add((folder + "/hRingObservableLambdaPt").c_str(), "hRingObservableLambdaPt", kTH1D, {axisConfigurations.axisPt}); + // =============================== + // 2D Lambda correlations + // =============================== + histos.add((folder + "/h2dRingObservableDeltaPhiVsLambdaPt").c_str(), "h2dRingObservableDeltaPhiVsLambdaPt", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisPt}); + histos.add((folder + "/h2dRingObservableDeltaThetaVsLambdaPt").c_str(), "h2dRingObservableDeltaThetaVsLambdaPt", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisPt}); + // Counters + histos.add((folder + "/h2dDeltaPhiVsLambdaPt").c_str(), "h2dDeltaPhiVsLambdaPt", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisPt}); + histos.add((folder + "/h2dDeltaThetaVsLambdaPt").c_str(), "h2dDeltaThetaVsLambdaPt", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisPt}); + // =============================== + // 2D Jet correlations + // =============================== + histos.add((folder + "/h2dRingObservableDeltaPhiVsLeadJetPt").c_str(), "h2dRingObservableDeltaPhiVsLeadJetPt", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisJetPt}); + histos.add((folder + "/h2dRingObservableDeltaThetaVsLeadJetPt").c_str(), "h2dRingObservableDeltaThetaVsLeadJetPt", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisJetPt}); + // Counters + histos.add((folder + "/h2dDeltaPhiVsLeadJetPt").c_str(), "h2dDeltaPhiVsLeadJetPt", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisJetPt}); + histos.add((folder + "/h2dDeltaThetaVsLeadJetPt").c_str(), "h2dDeltaThetaVsLeadJetPt", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisJetPt}); + // =============================== + // Squared observable (error propagation) + // =============================== + histos.add((folder + "/hRingObservableSquaredDeltaPhi").c_str(), "hRingObservableSquaredDeltaPhi", kTH1D, {axisConfigurations.axisDeltaPhi}); + histos.add((folder + "/hRingObservableSquaredDeltaTheta").c_str(), "hRingObservableSquaredDeltaTheta", kTH1D, {axisConfigurations.axisCosTheta}); + histos.add((folder + "/hRingObservableSquaredIntegrated").c_str(), "hRingObservableSquaredIntegrated", kTH1D, {{1, -0.5, 0.5}}); + histos.add((folder + "/hRingObservableSquaredLambdaPt").c_str(), "hRingObservableSquaredLambdaPt", kTH1D, {axisConfigurations.axisPt}); + histos.add((folder + "/h2dRingObservableSquaredDeltaPhiVsLambdaPt").c_str(), "h2dRingObservableSquaredDeltaPhiVsLambdaPt", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisPt}); + histos.add((folder + "/h2dRingObservableSquaredDeltaThetaVsLambdaPt").c_str(), "h2dRingObservableSquaredDeltaThetaVsLambdaPt", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisPt}); + histos.add((folder + "/h2dRingObservableSquaredDeltaPhiVsLeadJetPt").c_str(), "h2dRingObservableSquaredDeltaPhiVsLeadJetPt", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisJetPt}); + histos.add((folder + "/h2dRingObservableSquaredDeltaThetaVsLeadJetPt").c_str(), "h2dRingObservableSquaredDeltaThetaVsLeadJetPt", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisJetPt}); + + // Additional plots for instant gratification: + // -- 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 + // -- If any efficiency corrections arise, you can fill with the kTH1D as (deltaPhiJet, ringObservable, weight) + // instead of the simple (deltaPhiJet, ringObservable) --> Notice TProfile knows how to accept 3 entries + // for a TH1D-like object! + // -- CAUTION! The TProfile does not utilize unbiased variance estimators with N-1 instead of N in the denominator, + // so you might get biased errors when counts are too low in higher-dimensional profiles (i.e., kTProfile2Ds) + // =============================== + // 1D TProfiles + // =============================== + histos.add((folder + "/pRingObservableDeltaPhi").c_str(), "pRingObservableDeltaPhi;#Delta#varphi_{jet};<#it{R}>", kTProfile, {axisConfigurations.axisDeltaPhi}); + histos.add((folder + "/pRingObservableDeltaTheta").c_str(), "pRingObservableDeltaTheta;cos#theta_{jet};<#it{R}>", kTProfile, {axisConfigurations.axisCosTheta}); + 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}); + // =============================== + // 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;cos#theta_{jet};#it{p}_{T}^{#Lambda};<#it{R}>", kTProfile2D, {axisConfigurations.axisCosTheta, 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;cos#theta_{jet};#it{p}_{T}^{lead jet};<#it{R}>", kTProfile2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisJetPt}); + }; + // Execute local lambda to register histogram families: + addRingObservableFamily("Ring"); + addRingObservableFamily("RingKinematicCuts"); + addRingObservableFamily("JetKinematicCuts"); + addRingObservableFamily("JetAndLambdaKinematicCuts"); + } + //// Fill Ring Observable histograms: + // Notice that this implementation does not use the HIST() optimization for searching histograms + // (yes, we have a small overhead for looking up the histogram in the names table, but from + // AliceO2/Framework/Core/include/Framework/HistogramSpec.h, the framework seems to support + // runtime lookup of a histogram name instead of that hash lookup optimized way) + void fillRingObservableHists(const char* histFolderName, double deltaPhiJet, double deltaThetaJet, double v0pt, double leadingJetPt, double ringObservable){ + // 1D observable histograms + histos.fill(HIST((std::string(histFolderName) + "/hRingObservableDeltaPhi").c_str()), deltaPhiJet, ringObservable); + histos.fill(HIST((std::string(histFolderName) + "/hRingObservableDeltaTheta").c_str()), deltaThetaJet, ringObservable); + histos.fill(HIST((std::string(histFolderName) + "/hRingObservableIntegrated").c_str()), 0., ringObservable); + // Counters + histos.fill(HIST((std::string(histFolderName) + "/hDeltaPhi").c_str()), deltaPhiJet); + histos.fill(HIST((std::string(histFolderName) + "/hDeltaTheta").c_str()), deltaThetaJet); + histos.fill(HIST((std::string(histFolderName) + "/hIntegrated").c_str()), 0.); + + // Lambda pT variation -- Youpeng's proposal + histos.fill(HIST((std::string(histFolderName) + "/hRingObservableLambdaPt").c_str()), v0pt, ringObservable); + histos.fill(HIST((std::string(histFolderName) + "/hRingObservableLambdaPt").c_str()), v0pt); + + // 2D Lambda correlations + histos.fill(HIST((std::string(histFolderName) + "/h2dRingObservableDeltaPhiVsLambdaPt").c_str()), deltaPhiJet, v0pt, ringObservable); + histos.fill(HIST((std::string(histFolderName) + "/h2dRingObservableDeltaThetaVsLambdaPt").c_str()), deltaThetaJet, v0pt, ringObservable); + // Counters + histos.fill(HIST((std::string(histFolderName) + "/h2dDeltaPhiVsLambdaPt").c_str()), deltaPhiJet, v0pt); + histos.fill(HIST((std::string(histFolderName) + "/h2dDeltaThetaVsLambdaPt").c_str()), deltaThetaJet, v0pt); + + // 2D Jet correlations + histos.fill(HIST((std::string(histFolderName) + "/h2dRingObservableDeltaPhiVsLeadJetPt").c_str()), deltaPhiJet, leadingJetPt, ringObservable); + histos.fill(HIST((std::string(histFolderName) + "/h2dRingObservableDeltaThetaVsLeadJetPt").c_str()), deltaThetaJet, leadingJetPt, ringObservable); + // Counters + histos.fill(HIST((std::string(histFolderName) + "/h2dDeltaPhiVsLeadJetPt").c_str()), deltaPhiJet, leadingJetPt); + histos.fill(HIST((std::string(histFolderName) + "/h2dDeltaThetaVsLeadJetPt").c_str()), deltaThetaJet, leadingJetPt); + + // Additional plots for instant gratification: + // 1D Profiles + histos.fill(HIST((std::string(histFolderName) + "/pRingObservableDeltaPhi").c_str()), deltaPhiJet, ringObservable); + histos.fill(HIST((std::string(histFolderName) + "/pRingObservableDeltaTheta").c_str()), deltaThetaJet, ringObservable); + histos.fill(HIST((std::string(histFolderName) + "/pRingObservableIntegrated").c_str()), 0., ringObservable); + histos.fill(HIST((std::string(histFolderName) + "/pRingObservableLambdaPt").c_str()), v0pt, ringObservable); + // 2D Profiles + histos.fill(HIST((std::string(histFolderName) + "/p2dRingObservableDeltaPhiVsLambdaPt").c_str()), deltaPhiJet, v0pt, ringObservable); + histos.fill(HIST((std::string(histFolderName) + "/p2dRingObservableDeltaThetaVsLambdaPt").c_str()), deltaThetaJet, v0pt, ringObservable); + histos.fill(HIST((std::string(histFolderName) + "/p2dRingObservableDeltaPhiVsLeadJetPt").c_str()), deltaPhiJet, leadingJetPt, ringObservable); + histos.fill(HIST((std::string(histFolderName) + "/p2dRingObservableDeltaThetaVsLeadJetPt").c_str()), deltaThetaJet, leadingJetPt, ringObservable); + + // Lambda mass correlations (1D + 1D and 2D + 1D): (TODO: signal extraction attempts) - void processPolarizationData(aod::RingCollisions const& collisions, aod::RingJets const& jets, aod::RingLambdaLikeV0s const& v0s){ + } + + // Fills histograms needed for error propagation: + void fillRingObservableSquaredHists(const char* folder, double deltaPhiJet, double deltaThetaJet, double v0pt, double leadingJetPt, double ringObservableSquared){ + // 1D observable histograms + histos.fill((std::string(folder) + "/hRingObservableSquaredDeltaPhi").c_str(), deltaPhiJet, ringObservableSquared); + histos.fill((std::string(folder) + "/hRingObservableSquaredDeltaTheta").c_str(), deltaThetaJet, ringObservableSquared); + histos.fill((std::string(folder) + "/hRingObservableSquaredIntegrated").c_str(), 0., ringObservableSquared); + + // Lambda pT variation + histos.fill((std::string(folder) + "/hRingObservableSquaredLambdaPt").c_str(), v0pt, ringObservableSquared); + + // 2D Lambda correlations + histos.fill((std::string(folder) + "/h2dRingObservableSquaredDeltaPhiVsLambdaPt").c_str(), deltaPhiJet, v0pt, ringObservableSquared); + histos.fill((std::string(folder) + "/h2dRingObservableSquaredDeltaThetaVsLambdaPt").c_str(), deltaThetaJet, v0pt, ringObservableSquared); + + // 2D Jet correlations + histos.fill((std::string(folder) + "/h2dRingObservableSquaredDeltaPhiVsLeadJetPt").c_str(), deltaPhiJet, leadingJetPt, ringObservableSquared); + histos.fill((std::string(folder) + "/h2dRingObservableSquaredDeltaThetaVsLeadJetPt").c_str(), deltaThetaJet, leadingJetPt, ringObservableSquared); + } + // Preslices for correct collisions association: + Preslice perColJets = o2::aod::lambdajetpol::collIdx; + Preslice perColV0s = o2::aod::lambdajetpol::collIdx; + void processPolarizationData(aod::RingCollisions const& collisions, aod::RingJets const& jets, aod::RingLambdaLikeV0s const& v0s){ for (auto const& collision : collisions) { const auto collId = collision.collIdx(); - const double centrality = collision.centrality(); + // const double centrality = collision.centrality(); // (TODO: implement centrality!) // Slice jets and V0s belonging to this collision - auto jetsInColl = jets.sliceBy(o2::aod::lambdajetpol::collIdx, collId); - auto v0sInColl = v0s.sliceBy(o2::aod::lambdajetpol::collIdx, collId); + auto jetsInColl = jets.sliceBy(perColJets, collId); + auto v0sInColl = v0s.sliceBy(perColV0s, 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 @@ -99,102 +289,99 @@ struct lambdaJetPolarizationIonsDerived { const double jetPx = leadingJetPt * std::cos(leadingJetPhi); const double jetPy = leadingJetPt * std::sin(leadingJetPhi); const double jetPz = leadingJetPt * std::sinh(leadingJetEta); + XYZVector leadingJetVec(jetPx, jetPy, jetPz); + XYZVector leadingJetUnitVec = leadingJetVec.Unit(); // TODO: add centrality selection procedure and options (one configurable for no centrality separation at all too!) // TODO: add Lambda candidate selection. Think of a statistical method like signal extraction (if possible) for ring polarization // TODO: add calculations with second to leading jet too. // TODO: Add calculations with leading particle for (auto const& v0 : v0sInColl) { - const double v0pt = v0.V0Pt(); + const bool isLambda = v0.isLambda(); + const bool isAntiLambda = v0.isAntiLambda(); + if (isLambda && isAntiLambda) continue; // For now, removing the ambiguous candidates from the analysis. Derived data permits handling both. + const double v0pt = v0.v0Pt(); const double v0eta = v0.v0Eta(); - const double v0phi = v0.Phi(); + const double v0phi = v0.v0Phi(); - double v0LambdaMass; + double v0LambdaLikeMass; double protonLikePt; double protonLikeEta; double protonLikePhi; if (isLambda){ - v0LambdaMass = v0.MassLambda(); - protonLikePt = v0.PosPt(); - protonLikeEta = v0.PosEta(); - protonLikePhi = v0.PosPhi(); + if (!analyseLambda) continue; + v0LambdaLikeMass = v0.massLambda(); + protonLikePt = v0.posPt(); + protonLikeEta = v0.posEta(); + protonLikePhi = v0.posPhi(); + } + else if (isAntiLambda){ // (TODO: add a split histogram where you consider Lambda and AntiLambda polarization separately) + if (!analyseAntiLambda) continue; + v0LambdaLikeMass = v0.massAntiLambda(); + protonLikePt = v0.negPt(); + protonLikeEta = v0.negEta(); + protonLikePhi = v0.negPhi(); } - // (TODO: implement AntiLambda polarization) - // if (isAntiLambda){ - // } - const double lambdaPx = v0pt * std::cos(v0phi); - const double lambdaPy = v0pt * std::sin(v0phi); - const double lambdaPz = v0pt * std::sinh(v0eta); - const double lambdaMomentumSquared = lambdaPx*lambdaPx + lambdaPy*lambdaPy + lambdaPz*lambdaPz; - const double lambdaE = std::sqrt(v0LambdaMass*v0LambdaMass + lambdaMomentumSquared); - // const double lambdaRapidity = 0.5 * std::log((lambdaE + lambdaPz) / (lambdaE - lambdaPz)); // For extra selection criteria later - const double lambdaRapidity = std::atanh(lambdaPz / lambdaE); // More numerically stable - - const double protonPx = protonLikePt * std::cos(protonLikePhi); - const double protonPy = protonLikePt * std::sin(protonLikePhi); - const double protonPz = protonLikePt * std::sinh(protonLikeEta); - const double protonMomentumSquared = protonPx*protonPx + protonPy*protonPy + protonPz*protonPz; - const double protonE = std::sqrt(protonMass*protonMass + protonMomentumSquared); - - TLorentzVector lambdaLike4Vec(lambdaPx, lambdaPy, lambdaPz, lambdaE); - TLorentzVector protonLike4Vec(protonPx, protonPy, protonPz, protonE); + PtEtaPhiMVector lambdaLike4Vec(v0pt, v0eta, v0phi, v0LambdaLikeMass); + PtEtaPhiMVector protonLike4Vec(protonLikePt, protonLikeEta, protonLikePhi, protonMass); + double lambdaRapidity = lambdaLike4Vec.Rapidity(); // For further kinematic selections // Boosting proton into lambda frame: - TVector3 betaInverse = -lambdaLike4Vec.BoostVector(); // Boost trivector that goes from laboratory frame to the rest frame - TLorentzVector protonLike4VecStar = protonLike4Vec; - protonLike4VecStar.Boost(betaInverse); + XYZVector beta = -lambdaLike4Vec.BoostToCM(); // Boost trivector that goes from laboratory frame to the rest frame + auto protonLike4VecStar = boost(protonLike4Vec, beta); - TVector3 protonLikeStarUnitVec = (protonLike4VecStar.Vect()).Unit(); - TVector3 jetUnitVec = (TVector3(jetPx, jetPy, jetPz)).Unit(); - TVector3 lambda3Vec = TVector3(lambdaPx, lambdaPy, lambdaPz); + // Getting unit vectors and 3-components: + XYZVector lambdaLike3Vec = lambdaLike4Vec.Vect(); + XYZVector protonLikeStarUnit3Vec = protonLike4VecStar.Vect().Unit(); - // Calculating inner product: - double crossX = jetUnitVec.Y()*lambdaPz - jetUnitVec.Z()*lambdaPy; - double crossY = jetUnitVec.Z()*lambdaPx - jetUnitVec.X()*lambdaPz; - double crossZ = jetUnitVec.X()*lambdaPy - jetUnitVec.Y()*lambdaPx; - double crossProductNorm = std::sqrt(crossX*crossX + crossY*crossY + crossZ*crossZ); + // Calculating cross product: + XYZVector cross = leadingJetUnitVec.Cross(lambdaLike3Vec); + double crossProductNorm = cross.R(); - double ringObservable = 3./(lambdaWeakDecayConstant) * ((protonLikeStarUnitVec.X()*crossX - + protonLikeStarUnitVec.Y()*crossY + protonLikeStarUnitVec.Z()*cross_z)/crossProductNorm); + double ringObservable = protonLikeStarUnit3Vec.Dot(cross) / crossProductNorm; + // Adding the prefactor related to the CP-violating decay (decay constants have different signs) + ringObservable *= (isLambda) ? 3./lambdaWeakDecayConstant : 3./antiLambdaWeakDecayConstant; // Calculating error bars: double ringObservableSquared = ringObservable*ringObservable; // Angular variables: - double deltaPhiJet = v0phi - leadingJetPhi; - double deltaThetaJet = ; // 3D angular separation + double deltaPhiJet = wrapToPiFast(v0phi - leadingJetPhi); // Wrapped to [-PI, pi), for convenience + double deltaThetaJet = Angle(leadingJetUnitVec, lambdaLike3Vec); // 3D angular separation - // Fill ring histograms: (1D, lambda 2D correlations and jet 2D correlations): (TODO) - histos.fill(HIST("Ring/hRingObservableDeltaPhi"), 0); - histos.fill(HIST("Ring/hRingObservableIntegrated"), 0); + // Fill ring histograms: (1D, lambda 2D correlations and jet 2D correlations): + fillRingObservableHists("Ring", deltaPhiJet, deltaThetaJet, v0pt, leadingJetPt, ringObservable); + fillRingObservableSquaredHists("Ring", deltaPhiJet, deltaThetaJet, v0pt, leadingJetPt, ringObservableSquared); - histos.fill(HIST("Ring/hRingObservableDeltaPhi"), 0); - histos.fill(HIST("Ring/hRingObservableIntegrated"), 0); - - // 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){ - histos.fill(HIST("RingKinematicCuts/hRingObservableDeltaPhi"), 0); - histos.fill(HIST("RingKinematicCuts/hRingObservableIntegrated"), 0); + fillRingObservableHists("RingKinematicCuts", deltaPhiJet, deltaThetaJet, v0pt, leadingJetPt, ringObservable); + fillRingObservableSquaredHists("RingKinematicCuts", deltaPhiJet, deltaThetaJet, v0pt, leadingJetPt, ringObservableSquared); } - // Extra selection criteria on jet candidates: const bool kinematicJetCheck = std::abs(leadingJetEta) < 0.5; if (kinematicJetCheck){ // This is redundant for jets with R=0.4, but for jets with R<0.4 the leading jet may be farther in eta. - + fillRingObservableHists("JetKinematicCuts", deltaPhiJet, deltaThetaJet, v0pt, leadingJetPt, ringObservable); + fillRingObservableSquaredHists("JetKinematicCuts", deltaPhiJet, deltaThetaJet, v0pt, leadingJetPt, ringObservableSquared); } - - // Extra selection criteria on Lambda and jet candidates: + // Extra selection criteria on both Lambda and jet candidates: if (kinematicLambdaCheck && kinematicJetCheck){ - + fillRingObservableHists("JetAndLambdaKinematicCuts", deltaPhiJet, deltaThetaJet, v0pt, leadingJetPt, ringObservable); + fillRingObservableSquaredHists("JetAndLambdaKinematicCuts", deltaPhiJet, deltaThetaJet, v0pt, leadingJetPt, ringObservableSquared); } - - } // end v0s loop } // end collisions } -}; \ No newline at end of file + + PROCESS_SWITCH(lambdajetpolarizationionsderived, processPolarizationData, "Process derived data in Run 3 Data", true); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc)}; +} \ No newline at end of file From cd43870f7034cca1245c6833531680bcc58a931f Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Fri, 13 Feb 2026 21:19:30 -0300 Subject: [PATCH 15/40] Fixing V0s table name to within maximum size to save in Derived Data. Added some missing fills to histograms --- PWGLF/DataModel/lambdaJetPolarizationIons.h | 6 +- .../Strangeness/lambdaJetPolarizationIons.cxx | 92 +++++++++---------- 2 files changed, 47 insertions(+), 51 deletions(-) diff --git a/PWGLF/DataModel/lambdaJetPolarizationIons.h b/PWGLF/DataModel/lambdaJetPolarizationIons.h index 21867387b98..9e5bd2d7e43 100644 --- a/PWGLF/DataModel/lambdaJetPolarizationIons.h +++ b/PWGLF/DataModel/lambdaJetPolarizationIons.h @@ -28,7 +28,7 @@ namespace o2::aod namespace lambdajetpol { -DECLARE_SOA_COLUMN(CollIdx, collIdx, uint64_t); +DECLARE_SOA_COLUMN(CollIdx, collIdx, uint64_t); // Using a regular SOA column instead of an index column for convenience DECLARE_SOA_COLUMN(Centrality, centrality, float); DECLARE_SOA_COLUMN(JetPt, jetPt, float); @@ -63,7 +63,7 @@ DECLARE_SOA_TABLE(RingJets, "AOD", "RINGJETS", // Renamed to follow convention o lambdajetpol::JetPhi, lambdajetpol::JetNConstituents); -DECLARE_SOA_TABLE(RingLambdaLikeV0s, "AOD", "RINGLAMBDALIKEV0S", +DECLARE_SOA_TABLE(RingLaV0s, "AOD", "RINGLAV0S", lambdajetpol::CollIdx, lambdajetpol::V0Pt, lambdajetpol::V0Eta, @@ -84,4 +84,4 @@ DECLARE_SOA_TABLE(RingCollisions, "AOD", "RINGCOLLISIONS", lambdajetpol::Centrality); } // namespace o2::aod -#endif // PWGLF_DATAMODEL_lambdajetpol_H_ \ No newline at end of file +#endif // PWGLF_DATAMODEL_LAMBDAJETPOL_H_ \ No newline at end of file diff --git a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIons.cxx b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIons.cxx index 33afdc2ce8e..ac84b580018 100644 --- a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIons.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIons.cxx @@ -147,9 +147,9 @@ struct lambdajetpolarizationions { // struct : ProducesGroup { // } products; - Produces tableV0s; - Produces tableJets; - Produces tableCollisions; + Produces tableV0s; + Produces tableJets; + Produces tableCollisions; // Define histogram registries: HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; @@ -184,16 +184,11 @@ struct lambdajetpolarizationions { // Configurable doCollisionAssociationQA{"doCollisionAssociationQA", true, "check collision association"}; // ///////////////////////////////////////////// - // //////////////////////////////////////////// - // Manual slice by: (TODO) - // SliceCache cache; - // Preslice V0perCollision = o2::aod::v0data::collisionId; - // Preslice CascperCollision = o2::aod::cascdata::collisionId; - // Preslice KFCascperCollision = o2::aod::cascdata::collisionId; - // Preslice TraCascperCollision = o2::aod::cascdata::collisionId; - // Preslice mcParticlePerMcCollision = o2::aod::mcparticle::mcCollisionId; - // Preslice udCollisionsPerCollision = o2::aod::udcollision::collisionId; - // /////////////////////////////////////////// + // TODO: COMPLEMENTARY ANALYSES TO STUDY SPURIOUS POLARIZATION SOURCES! + // TODO: add an event plane selection procedure to get an angle between the global polarization axis and the jet axis to uncouple polarizations? + // TODO: (related to previous comment) if we already have event plane, also estimate v_2-caused polarization. Hydro papers indicate observable is unsensitive to this spurious polarization, but this is a perfect consistency check. + // TODO: add a longitudinal polarization block of code to estimate other sources of polarization (and possibly study their differential dependence on the angle wrlt the jets and their rings)? + // TODO: add a block of code that calculates polarization from Lambda fragmentation to estimate the contamination of this third source of polarization // Configurable groups: @@ -235,7 +230,8 @@ struct lambdajetpolarizationions { // Selection criteria: acceptance Configurable rapidityCut{"rapidityCut", 1.0f, "rapidity"}; - Configurable daughterEtaCut{"daughterEtaCut", 0.9, "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 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 Configurable v0cospa{"v0cospa", 0.995, "min V0 CosPA"}; // Default is 0.97 @@ -462,7 +458,6 @@ struct lambdajetpolarizationions { } } - // Track analysis parameters -- A specific group that is different from the v0Selections. In jet analyses we need to control our PseudoJet candidates! // (TODO: include minimal selection criteria for electrons, muons and photons) // Notice you do NOT need any PID for the PseudoJet candidates! Only need is to know the 4-momentum appropriately. Thus removed nsigma checks on PID @@ -490,35 +485,8 @@ struct lambdajetpolarizationions { Configurable dcaxyMaxTrackPar2{"dcaxyMaxTrackPar2", 1.1f, "Exponent of pt dependence of DCA resolution"}; } pseudoJetCandidateTrackSelections; - // struct : ConfigurableGroup { - // std::string prefix = "jetQAConfigurations"; // JSON group name - // Configurable - // } jetQAConfigurations; // (TODO) - - // Instantiate utility class for jet background subtraction JetBkgSubUtils backgroundSub; - // // Lambda Ring Polarization axes configurable group: - // struct : ConfigurableGroup { - // std::string prefix = "ringPolConfigurations"; // JSON group name - - // } ringPolConfigurations; // (TODO) - - - // Define per-collision preslices for V0s, MC particles, and daughter tracks: - Preslice V0perCollision = o2::aod::v0data::collisionId; - // Preslice perMCCollision = o2::aod::mcparticle::mcCollisionId; - Preslice perCollisionTrk = o2::aod::track::collisionId; - - // Service pdg; - - // std::vector genLambda; - // std::vector genAntiLambda; - // std::vector genXiMinus; - // std::vector genXiPlus; - // std::vector genOmegaMinus; - // std::vector genOmegaPlus; - void init(InitContext const&){ // (TODO: add all useful histograms here! Add flags for QA plots and the such too) // setting CCDB service ccdb->setURL(ccdbConfigurations.ccdbUrl); @@ -657,7 +625,7 @@ struct lambdajetpolarizationions { }); }; constexpr bool Lambda = true; // Some constexpr to make it more readable (works at compile level) - constexpr bool AntiLambda = false; + constexpr bool AntiLambda = false; // "false" is just a flag for this addHypothesis function! It just means fill "AntiLambda" labels addHypothesis(Lambda, analyseLambda); addHypothesis(AntiLambda, analyseAntiLambda); @@ -1152,6 +1120,7 @@ struct lambdajetpolarizationions { // pseudorapidity cuts: 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!) @@ -1595,6 +1564,21 @@ struct lambdajetpolarizationions { // Saving the Lambdas into a derived data column: auto const v0pt = v0.pt(); + // LOG(INFO) << "Filling tableV0s"; + // LOG(INFO) << collIdx; + // LOG(INFO) << v0pt; + // LOG(INFO) << v0.eta(); + // LOG(INFO) << v0.phi(); + // LOG(INFO) << isLambda; + // LOG(INFO) << isAntiLambda; + // LOG(INFO) << v0.mLambda(); + // LOG(INFO) << v0.mAntiLambda(); + // LOG(INFO) << v0.positivept(); + // LOG(INFO) << v0.positiveeta(); + // LOG(INFO) << v0.positivephi(); + // LOG(INFO) << v0.negativept(); + // LOG(INFO) << v0.negativeeta(); + // LOG(INFO) << v0.negativephi(); tableV0s(collIdx, v0pt, v0.eta(), // Using eta instead of rapidity @@ -1649,6 +1633,15 @@ struct lambdajetpolarizationions { // Remaking these variables outside of the passesLambdaLambdaBarHypothesis. Loses performance, but that should be OK for QA const auto posTrackExtra = v0.template posTrack_as(); const auto negTrackExtra = v0.template negTrack_as(); + histos.fill(HIST("hPosDCAToPV"), v0.dcapostopv()); + histos.fill(HIST("hNegDCAToPV"), v0.dcanegtopv()); + histos.fill(HIST("hDCADaughters"), v0.dcaV0daughters()); + histos.fill(HIST("hPointingAngle"), std::acos(v0.v0cosPA())); + histos.fill(HIST("hV0Radius"), v0.v0radius()); + histos.fill(HIST("h2dPositiveITSvsTPCpts"), posTrackExtra.tpcNClsCrossedRows(), posTrackExtra.itsNCls()); + histos.fill(HIST("h2dNegativeITSvsTPCpts"), negTrackExtra.tpcNClsCrossedRows(), negTrackExtra.itsNCls()); + histos.fill(HIST("h2dPositivePtVsPhi"), v0.positivept(), computePhiMod(v0.positivephi(), 1)); + histos.fill(HIST("h2dNegativePtVsPhi"), v0.negativept(), computePhiMod(v0.negativephi(), -1)); if (isLambda && analyseLambda) { histos.fill(HIST("h3dMassLambda"), centrality, v0pt, v0.mLambda()); histos.fill(HIST("hMassLambda"), v0.mLambda()); @@ -1696,8 +1689,10 @@ struct lambdajetpolarizationions { } } if (isAntiLambda && analyseAntiLambda) { + // histos.add("h2dNbrOfAntiLambdaVsCentrality", "h2dNbrOfAntiLambdaVsCentrality", kTH2D, {axisConfigurations.axisCentrality, {10, -0.5f, 9.5f}}); + // histos.fill(HIST("h2dNbrOfAntiLambdaVsCentrality"), centrality, v0pt, v0.mAntiLambda()); // (TODO: add the proper call to this fill) histos.fill(HIST("h3dMassAntiLambda"), centrality, v0pt, v0.mAntiLambda()); - histos.fill(HIST("hMassAntiLambda"), v0.mAntiLambda()); + histos.fill(HIST("hMassAntiLambda"), v0.mAntiLambda()); histos.fill(HIST("AntiLambda/hPosDCAToPV"), v0.dcapostopv()); histos.fill(HIST("AntiLambda/hNegDCAToPV"), v0.dcanegtopv()); histos.fill(HIST("AntiLambda/hDCADaughters"), v0.dcaV0daughters()); @@ -1742,14 +1737,15 @@ struct lambdajetpolarizationions { } } } // end CompleteTopoQA - } + } // end V0s loop + // Only fills collision when there is a valid V0 in it: (TODO: could probably do the same for the jets table) if (hasValidV0){ - tableCollisions(collIdx, - centrality); // (TODO: add InteractionRate info and other useful cuts for later on in the analysis!) + // LOG(INFO) << "Filling tableCollisions"; + tableCollisions(collIdx, centrality); // (TODO: add InteractionRate info and other useful cuts for later on in the analysis!) } } - PROCESS_SWITCH(lambdajetpolarizationions, processJetsData, "Process jets and produced derived data in Run 3 Data", true); + PROCESS_SWITCH(lambdajetpolarizationions, processJetsData, "Process jets and produce derived data in Run 3 Data", true); PROCESS_SWITCH(lambdajetpolarizationions, processV0sData, "Process V0s and produce derived data in Run 3 Data", true); // PROCESS_SWITCH(lambdajetpolarizationions, processJetsMC, "Process jets and produced derived data in Run 3 MC", true); // PROCESS_SWITCH(lambdajetpolarizationions, processV0sMC, "Process V0s and produce derived data in Run 3 MC", true); From 2b8d86a2be844a87480362f0356e732cb1f98452 Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Fri, 13 Feb 2026 21:53:15 -0300 Subject: [PATCH 16/40] First full version of derived data consumer. Implemented preprocessor macro to avoid rewriting 20*4 histogram fills by hand (now fixed at compile time). Still not works with current JetPolarizationIons definition (collIdx has trouble with Preslice, as it is a non-monotonic. Possibly O2 is mixing the TimeFrames and combining multiple GlobalIndices in a same subfolder of the derived data) --- PWGLF/DataModel/lambdaJetPolarizationIons.h | 2 +- PWGLF/Tasks/Strangeness/CMakeLists.txt | 5 + .../Strangeness/lambdaJetPolarizationIons.cxx | 11 +- .../lambdaJetPolarizationIonsDerived.cxx | 168 +++++++++--------- 4 files changed, 96 insertions(+), 90 deletions(-) diff --git a/PWGLF/DataModel/lambdaJetPolarizationIons.h b/PWGLF/DataModel/lambdaJetPolarizationIons.h index 9e5bd2d7e43..edb135afa77 100644 --- a/PWGLF/DataModel/lambdaJetPolarizationIons.h +++ b/PWGLF/DataModel/lambdaJetPolarizationIons.h @@ -63,7 +63,7 @@ DECLARE_SOA_TABLE(RingJets, "AOD", "RINGJETS", // Renamed to follow convention o lambdajetpol::JetPhi, lambdajetpol::JetNConstituents); -DECLARE_SOA_TABLE(RingLaV0s, "AOD", "RINGLAV0S", +DECLARE_SOA_TABLE(RingLaV0s, "AOD", "RINGLAV0S", // Had to write this in a shorter form because the derived data did not accept long names lambdajetpol::CollIdx, lambdajetpol::V0Pt, lambdajetpol::V0Eta, diff --git a/PWGLF/Tasks/Strangeness/CMakeLists.txt b/PWGLF/Tasks/Strangeness/CMakeLists.txt index ca2c5ac2187..ce647140388 100644 --- a/PWGLF/Tasks/Strangeness/CMakeLists.txt +++ b/PWGLF/Tasks/Strangeness/CMakeLists.txt @@ -171,6 +171,11 @@ SOURCES lambdaJetPolarizationIons.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGJECore FastJet::FastJet FastJet::Contrib O2Physics::EventFilteringUtils O2Physics::AnalysisCCDB COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(lambdajetpolarizationionsderived +SOURCES lambdaJetPolarizationIonsDerived.cxx +PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore +COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(lambdaspincorrderived SOURCES lambdaspincorrderived.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore diff --git a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIons.cxx b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIons.cxx index ac84b580018..30c76e9c14d 100644 --- a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIons.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIons.cxx @@ -1536,7 +1536,7 @@ struct lambdajetpolarizationions { const uint64_t collIdx = collision.globalIndex(); if (v0Selections.rejectTPCsectorBoundary) initCCDB(bc); // Substituted call from collision to bc for raw data - bool hasValidV0 = false; // Bool to know if event information can be saved. + // bool hasValidV0 = false; // Bool to know if event information can be saved. for (auto const& v0 : fullV0s){ V0SelCounter.resetForNewV0(); V0SelCounter.fill(); // Fill for all v0 candidates @@ -1552,7 +1552,7 @@ struct lambdajetpolarizationions { if (analyseAntiLambda) isAntiLambda = passesLambdaLambdaBarHypothesis(v0, collision, false); if (!isLambda && !isAntiLambda) continue; // Candidate is not considered to be a Lambda (TODO: expand this to a full if block with QA about rejections) - hasValidV0 = true; + // hasValidV0 = true; if (doArmenterosQA) histos.fill(HIST("GeneralQA/h2dArmenterosSelected"), v0.alpha(), v0.qtarm()); // cross-check if (isLambda && isAntiLambda) histos.fill(HIST("hAmbiguousLambdaCandidates"), 0); @@ -1739,10 +1739,11 @@ struct lambdajetpolarizationions { } // end CompleteTopoQA } // end V0s loop // Only fills collision when there is a valid V0 in it: (TODO: could probably do the same for the jets table) - if (hasValidV0){ + // if (hasValidV0){ // LOG(INFO) << "Filling tableCollisions"; - tableCollisions(collIdx, centrality); // (TODO: add InteractionRate info and other useful cuts for later on in the analysis!) - } + // Current logic now fills tables independently of collision having V0s, for the Jets table to match correctly + tableCollisions(collIdx, centrality); // (TODO: add InteractionRate info and other useful cuts for later on in the analysis!) + // } } PROCESS_SWITCH(lambdajetpolarizationions, processJetsData, "Process jets and produce derived data in Run 3 Data", true); diff --git a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx index d572a6f1c39..6a410ca56dc 100644 --- a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx @@ -57,7 +57,6 @@ using namespace o2::framework; using namespace o2::framework::expressions; using ROOT::Math::XYZVector; using ROOT::Math::PtEtaPhiMVector; -using namespace ROOT::Math::VectorUtil; // using namespace o2::aod::lambdajetpol; // Used it explicitly along the code for clarity // Declaring constants: @@ -65,6 +64,67 @@ constexpr double protonMass = o2::constants::physics::MassProton; // Assumes par constexpr double lambdaWeakDecayConstant = 0.749; // DPG 2025 update constexpr double antiLambdaWeakDecayConstant = -0.758; // DPG 2025 update + +// Helper macro to avoid writing the histogram fills 4 times for about 20 histograms: +#define RING_OBSERVABLE_FILL_LIST(X, FOLDER) \ + /* 1D observable histograms */ \ + X(FOLDER "/hRingObservableDeltaPhi", deltaPhiJet, ringObservable) \ + X(FOLDER "/hRingObservableDeltaTheta", deltaThetaJet, ringObservable) \ + X(FOLDER "/hRingObservableIntegrated", 0., ringObservable) \ + /* Counters */ \ + X(FOLDER "/hDeltaPhi", deltaPhiJet) \ + X(FOLDER "/hDeltaTheta", deltaThetaJet) \ + X(FOLDER "/hIntegrated", 0.) \ + /* Lambda pT variation -- Youpeng's proposal */ \ + X(FOLDER "/hRingObservableLambdaPt", v0pt, ringObservable) \ + X(FOLDER "/hRingObservableLambdaPt", v0pt) \ + /* 2D Lambda correlations */ \ + X(FOLDER "/h2dRingObservableDeltaPhiVsLambdaPt", deltaPhiJet, v0pt, ringObservable) \ + X(FOLDER "/h2dRingObservableDeltaThetaVsLambdaPt", deltaThetaJet, v0pt, ringObservable) \ + /* Counters */ \ + X(FOLDER "/h2dDeltaPhiVsLambdaPt", deltaPhiJet, v0pt) \ + X(FOLDER "/h2dDeltaThetaVsLambdaPt", deltaThetaJet, v0pt) \ + /* 2D Jet correlations */ \ + X(FOLDER "/h2dRingObservableDeltaPhiVsLeadJetPt", deltaPhiJet, leadingJetPt, ringObservable) \ + X(FOLDER "/h2dRingObservableDeltaThetaVsLeadJetPt", deltaThetaJet, leadingJetPt, ringObservable) \ + /* Counters */ \ + X(FOLDER "/h2dDeltaPhiVsLeadJetPt", deltaPhiJet, leadingJetPt) \ + X(FOLDER "/h2dDeltaThetaVsLeadJetPt", deltaThetaJet, leadingJetPt) \ + /* Additional plots for instant gratification - 1D Profiles */ \ + X(FOLDER "/pRingObservableDeltaPhi", deltaPhiJet, ringObservable) \ + X(FOLDER "/pRingObservableDeltaTheta", deltaThetaJet, ringObservable) \ + X(FOLDER "/pRingObservableIntegrated", 0., ringObservable) \ + X(FOLDER "/pRingObservableLambdaPt", v0pt, ringObservable) \ + /* 2D Profiles */ \ + X(FOLDER "/p2dRingObservableDeltaPhiVsLambdaPt", deltaPhiJet, v0pt, ringObservable) \ + X(FOLDER "/p2dRingObservableDeltaThetaVsLambdaPt", deltaThetaJet, v0pt, ringObservable) \ + X(FOLDER "/p2dRingObservableDeltaPhiVsLeadJetPt", deltaPhiJet, leadingJetPt, ringObservable) \ + X(FOLDER "/p2dRingObservableDeltaThetaVsLeadJetPt", deltaThetaJet, leadingJetPt, ringObservable) + // Lambda mass correlations (1D + 1D and 2D + 1D): (TODO: signal extraction attempts) + +// ====================================================== +// Ring Observable SQUARED histogram fill list +// ====================================================== +#define RING_OBSERVABLE_SQUARED_FILL_LIST(X, FOLDER) \ + /* 1D observable histograms */ \ + X(FOLDER "/hRingObservableSquaredDeltaPhi", deltaPhiJet, ringObservableSquared) \ + X(FOLDER "/hRingObservableSquaredDeltaTheta", deltaThetaJet, ringObservableSquared) \ + X(FOLDER "/hRingObservableSquaredIntegrated", 0., ringObservableSquared) \ + /* Lambda pT variation */ \ + X(FOLDER "/hRingObservableSquaredLambdaPt", v0pt, ringObservableSquared) \ + /* 2D Lambda correlations */ \ + X(FOLDER "/h2dRingObservableSquaredDeltaPhiVsLambdaPt", deltaPhiJet, v0pt, ringObservableSquared) \ + X(FOLDER "/h2dRingObservableSquaredDeltaThetaVsLambdaPt", deltaThetaJet, v0pt, ringObservableSquared) \ + /* 2D Jet correlations */ \ + X(FOLDER "/h2dRingObservableSquaredDeltaPhiVsLeadJetPt", deltaPhiJet, leadingJetPt, ringObservableSquared) \ + X(FOLDER "/h2dRingObservableSquaredDeltaThetaVsLeadJetPt",deltaThetaJet, leadingJetPt, ringObservableSquared) + +// 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 +// would not know to end each statement with a semicolon): +#define APPLY_HISTO_FILL(NAME, ...) histos.fill(HIST(NAME), __VA_ARGS__); + + struct lambdajetpolarizationionsderived { // Define histogram registries: @@ -174,92 +234,29 @@ struct lambdajetpolarizationionsderived { // =============================== 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;cos#theta_{jet};#it{p}_{T}^{lead jet};<#it{R}>", kTProfile2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisJetPt}); + // (TODO: add mass histograms for signal extraction) }; // Execute local lambda to register histogram families: addRingObservableFamily("Ring"); addRingObservableFamily("RingKinematicCuts"); addRingObservableFamily("JetKinematicCuts"); addRingObservableFamily("JetAndLambdaKinematicCuts"); - } - //// Fill Ring Observable histograms: - // Notice that this implementation does not use the HIST() optimization for searching histograms - // (yes, we have a small overhead for looking up the histogram in the names table, but from - // AliceO2/Framework/Core/include/Framework/HistogramSpec.h, the framework seems to support - // runtime lookup of a histogram name instead of that hash lookup optimized way) - void fillRingObservableHists(const char* histFolderName, double deltaPhiJet, double deltaThetaJet, double v0pt, double leadingJetPt, double ringObservable){ - // 1D observable histograms - histos.fill(HIST((std::string(histFolderName) + "/hRingObservableDeltaPhi").c_str()), deltaPhiJet, ringObservable); - histos.fill(HIST((std::string(histFolderName) + "/hRingObservableDeltaTheta").c_str()), deltaThetaJet, ringObservable); - histos.fill(HIST((std::string(histFolderName) + "/hRingObservableIntegrated").c_str()), 0., ringObservable); - // Counters - histos.fill(HIST((std::string(histFolderName) + "/hDeltaPhi").c_str()), deltaPhiJet); - histos.fill(HIST((std::string(histFolderName) + "/hDeltaTheta").c_str()), deltaThetaJet); - histos.fill(HIST((std::string(histFolderName) + "/hIntegrated").c_str()), 0.); - - // Lambda pT variation -- Youpeng's proposal - histos.fill(HIST((std::string(histFolderName) + "/hRingObservableLambdaPt").c_str()), v0pt, ringObservable); - histos.fill(HIST((std::string(histFolderName) + "/hRingObservableLambdaPt").c_str()), v0pt); - - // 2D Lambda correlations - histos.fill(HIST((std::string(histFolderName) + "/h2dRingObservableDeltaPhiVsLambdaPt").c_str()), deltaPhiJet, v0pt, ringObservable); - histos.fill(HIST((std::string(histFolderName) + "/h2dRingObservableDeltaThetaVsLambdaPt").c_str()), deltaThetaJet, v0pt, ringObservable); - // Counters - histos.fill(HIST((std::string(histFolderName) + "/h2dDeltaPhiVsLambdaPt").c_str()), deltaPhiJet, v0pt); - histos.fill(HIST((std::string(histFolderName) + "/h2dDeltaThetaVsLambdaPt").c_str()), deltaThetaJet, v0pt); - - // 2D Jet correlations - histos.fill(HIST((std::string(histFolderName) + "/h2dRingObservableDeltaPhiVsLeadJetPt").c_str()), deltaPhiJet, leadingJetPt, ringObservable); - histos.fill(HIST((std::string(histFolderName) + "/h2dRingObservableDeltaThetaVsLeadJetPt").c_str()), deltaThetaJet, leadingJetPt, ringObservable); - // Counters - histos.fill(HIST((std::string(histFolderName) + "/h2dDeltaPhiVsLeadJetPt").c_str()), deltaPhiJet, leadingJetPt); - histos.fill(HIST((std::string(histFolderName) + "/h2dDeltaThetaVsLeadJetPt").c_str()), deltaThetaJet, leadingJetPt); - - // Additional plots for instant gratification: - // 1D Profiles - histos.fill(HIST((std::string(histFolderName) + "/pRingObservableDeltaPhi").c_str()), deltaPhiJet, ringObservable); - histos.fill(HIST((std::string(histFolderName) + "/pRingObservableDeltaTheta").c_str()), deltaThetaJet, ringObservable); - histos.fill(HIST((std::string(histFolderName) + "/pRingObservableIntegrated").c_str()), 0., ringObservable); - histos.fill(HIST((std::string(histFolderName) + "/pRingObservableLambdaPt").c_str()), v0pt, ringObservable); - // 2D Profiles - histos.fill(HIST((std::string(histFolderName) + "/p2dRingObservableDeltaPhiVsLambdaPt").c_str()), deltaPhiJet, v0pt, ringObservable); - histos.fill(HIST((std::string(histFolderName) + "/p2dRingObservableDeltaThetaVsLambdaPt").c_str()), deltaThetaJet, v0pt, ringObservable); - histos.fill(HIST((std::string(histFolderName) + "/p2dRingObservableDeltaPhiVsLeadJetPt").c_str()), deltaPhiJet, leadingJetPt, ringObservable); - histos.fill(HIST((std::string(histFolderName) + "/p2dRingObservableDeltaThetaVsLeadJetPt").c_str()), deltaThetaJet, leadingJetPt, ringObservable); - - // Lambda mass correlations (1D + 1D and 2D + 1D): (TODO: signal extraction attempts) - - } - - // Fills histograms needed for error propagation: - void fillRingObservableSquaredHists(const char* folder, double deltaPhiJet, double deltaThetaJet, double v0pt, double leadingJetPt, double ringObservableSquared){ - // 1D observable histograms - histos.fill((std::string(folder) + "/hRingObservableSquaredDeltaPhi").c_str(), deltaPhiJet, ringObservableSquared); - histos.fill((std::string(folder) + "/hRingObservableSquaredDeltaTheta").c_str(), deltaThetaJet, ringObservableSquared); - histos.fill((std::string(folder) + "/hRingObservableSquaredIntegrated").c_str(), 0., ringObservableSquared); - - // Lambda pT variation - histos.fill((std::string(folder) + "/hRingObservableSquaredLambdaPt").c_str(), v0pt, ringObservableSquared); - - // 2D Lambda correlations - histos.fill((std::string(folder) + "/h2dRingObservableSquaredDeltaPhiVsLambdaPt").c_str(), deltaPhiJet, v0pt, ringObservableSquared); - histos.fill((std::string(folder) + "/h2dRingObservableSquaredDeltaThetaVsLambdaPt").c_str(), deltaThetaJet, v0pt, ringObservableSquared); - - // 2D Jet correlations - histos.fill((std::string(folder) + "/h2dRingObservableSquaredDeltaPhiVsLeadJetPt").c_str(), deltaPhiJet, leadingJetPt, ringObservableSquared); - histos.fill((std::string(folder) + "/h2dRingObservableSquaredDeltaThetaVsLeadJetPt").c_str(), deltaThetaJet, leadingJetPt, ringObservableSquared); - } + ////////////// Fill Ring Observable histograms: + ///(This block was tranformed into a bunch of #define statements at the top of the code) + ////////////// // Preslices for correct collisions association: - Preslice perColJets = o2::aod::lambdajetpol::collIdx; - Preslice perColV0s = o2::aod::lambdajetpol::collIdx; - void processPolarizationData(aod::RingCollisions const& collisions, aod::RingJets const& jets, aod::RingLambdaLikeV0s const& v0s){ + Preslice perColJets = o2::aod::lambdajetpol::collIdx; // Slicing by the key that comes with the index column + Preslice perColV0s = o2::aod::lambdajetpol::collIdx; + void processPolarizationData(o2::aod::RingCollisions const& collisions, o2::aod::RingJets const& jets, o2::aod::RingLaV0s const& v0s){ for (auto const& collision : collisions) { const auto collId = collision.collIdx(); // const double centrality = collision.centrality(); // (TODO: implement centrality!) // Slice jets and V0s 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 jetsInColl = jets.sliceBy(perColJets, collId); auto v0sInColl = v0s.sliceBy(perColV0s, collId); @@ -268,7 +265,7 @@ struct lambdajetpolarizationionsderived { // 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!) - if (jetsInColl.empty() || v0sInColl.empty()) continue; + if (!jetsInColl.size() || !v0sInColl.size()) continue; // Get leading jet: double leadingJetPt = -1; @@ -329,7 +326,7 @@ struct lambdajetpolarizationionsderived { // Boosting proton into lambda frame: XYZVector beta = -lambdaLike4Vec.BoostToCM(); // Boost trivector that goes from laboratory frame to the rest frame - auto protonLike4VecStar = boost(protonLike4Vec, beta); + auto protonLike4VecStar = ROOT::Math::VectorUtil::boost(protonLike4Vec, beta); // Getting unit vectors and 3-components: XYZVector lambdaLike3Vec = lambdaLike4Vec.Vect(); @@ -348,30 +345,30 @@ struct lambdajetpolarizationionsderived { // Angular variables: double deltaPhiJet = wrapToPiFast(v0phi - leadingJetPhi); // Wrapped to [-PI, pi), for convenience - double deltaThetaJet = Angle(leadingJetUnitVec, lambdaLike3Vec); // 3D angular separation + double deltaThetaJet = ROOT::Math::VectorUtil::Angle(leadingJetUnitVec, lambdaLike3Vec); // 3D angular separation // Fill ring histograms: (1D, lambda 2D correlations and jet 2D correlations): - fillRingObservableHists("Ring", deltaPhiJet, deltaThetaJet, v0pt, leadingJetPt, ringObservable); - fillRingObservableSquaredHists("Ring", deltaPhiJet, deltaThetaJet, v0pt, leadingJetPt, ringObservableSquared); + RING_OBSERVABLE_FILL_LIST(APPLY_HISTO_FILL, "Ring") // Notice the usage of macros! If you change the variable names, this WILL break the code! + RING_OBSERVABLE_SQUARED_FILL_LIST(APPLY_HISTO_FILL, "Ring") // No, there should NOT be any ";" here! Read the macro definition for an explanation // 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){ - fillRingObservableHists("RingKinematicCuts", deltaPhiJet, deltaThetaJet, v0pt, leadingJetPt, ringObservable); - fillRingObservableSquaredHists("RingKinematicCuts", deltaPhiJet, deltaThetaJet, v0pt, leadingJetPt, ringObservableSquared); + RING_OBSERVABLE_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts") + RING_OBSERVABLE_SQUARED_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts") } // Extra selection criteria on jet candidates: const bool kinematicJetCheck = std::abs(leadingJetEta) < 0.5; if (kinematicJetCheck){ // This is redundant for jets with R=0.4, but for jets with R<0.4 the leading jet may be farther in eta. - fillRingObservableHists("JetKinematicCuts", deltaPhiJet, deltaThetaJet, v0pt, leadingJetPt, ringObservable); - fillRingObservableSquaredHists("JetKinematicCuts", deltaPhiJet, deltaThetaJet, v0pt, leadingJetPt, ringObservableSquared); + RING_OBSERVABLE_FILL_LIST(APPLY_HISTO_FILL, "JetKinematicCuts") + RING_OBSERVABLE_SQUARED_FILL_LIST(APPLY_HISTO_FILL, "JetKinematicCuts") } // Extra selection criteria on both Lambda and jet candidates: if (kinematicLambdaCheck && kinematicJetCheck){ - fillRingObservableHists("JetAndLambdaKinematicCuts", deltaPhiJet, deltaThetaJet, v0pt, leadingJetPt, ringObservable); - fillRingObservableSquaredHists("JetAndLambdaKinematicCuts", deltaPhiJet, deltaThetaJet, v0pt, leadingJetPt, ringObservableSquared); + RING_OBSERVABLE_FILL_LIST(APPLY_HISTO_FILL, "JetAndLambdaKinematicCuts") + RING_OBSERVABLE_SQUARED_FILL_LIST(APPLY_HISTO_FILL, "JetAndLambdaKinematicCuts") } } // end v0s loop } // end collisions @@ -384,4 +381,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{ adaptAnalysisTask(cfgc)}; -} \ No newline at end of file +} + +// Avoid macro leakage! +#undef APPLY_HISTO_FILL \ No newline at end of file From c63823793af80493cc5384b553a405b09c1a7bdf Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Fri, 13 Feb 2026 22:28:32 -0300 Subject: [PATCH 17/40] First fully functional version (still needs QA to check if there is no indexing error and physics is correct) --- PWGLF/DataModel/lambdaJetPolarizationIons.h | 9 +++++---- .../Strangeness/lambdaJetPolarizationIonsDerived.cxx | 6 +++--- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/PWGLF/DataModel/lambdaJetPolarizationIons.h b/PWGLF/DataModel/lambdaJetPolarizationIons.h index edb135afa77..c0531c0da02 100644 --- a/PWGLF/DataModel/lambdaJetPolarizationIons.h +++ b/PWGLF/DataModel/lambdaJetPolarizationIons.h @@ -28,7 +28,8 @@ namespace o2::aod namespace lambdajetpol { -DECLARE_SOA_COLUMN(CollIdx, collIdx, uint64_t); // Using a regular SOA column instead of an index column for convenience +// DECLARE_SOA_COLUMN(CollIdx, collIdx, uint64_t); // Using a regular SOA column instead of an index column for convenience +DECLARE_SOA_INDEX_COLUMN(Collision, collision); DECLARE_SOA_COLUMN(Centrality, centrality, float); DECLARE_SOA_COLUMN(JetPt, jetPt, float); @@ -57,14 +58,14 @@ DECLARE_SOA_COLUMN(NegPhi, negPhi, float); } // namespace lambdajetpol DECLARE_SOA_TABLE(RingJets, "AOD", "RINGJETS", // Renamed to follow convention on "s" at the end of table name. - lambdajetpol::CollIdx, + lambdajetpol::CollisionId, // Changed to an internal O2 index, slightly different from usual o2::soa::Index<> though lambdajetpol::JetPt, lambdajetpol::JetEta, lambdajetpol::JetPhi, lambdajetpol::JetNConstituents); DECLARE_SOA_TABLE(RingLaV0s, "AOD", "RINGLAV0S", // Had to write this in a shorter form because the derived data did not accept long names - lambdajetpol::CollIdx, + lambdajetpol::CollisionId, lambdajetpol::V0Pt, lambdajetpol::V0Eta, lambdajetpol::V0Phi, @@ -80,7 +81,7 @@ DECLARE_SOA_TABLE(RingLaV0s, "AOD", "RINGLAV0S", // Had to write this in a short lambdajetpol::NegPhi); DECLARE_SOA_TABLE(RingCollisions, "AOD", "RINGCOLLISIONS", - lambdajetpol::CollIdx, + lambdajetpol::CollisionId, lambdajetpol::Centrality); } // namespace o2::aod diff --git a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx index 6a410ca56dc..88317014527 100644 --- a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx @@ -248,11 +248,11 @@ struct lambdajetpolarizationionsderived { ////////////// // Preslices for correct collisions association: - Preslice perColJets = o2::aod::lambdajetpol::collIdx; // Slicing by the key that comes with the index column - Preslice perColV0s = o2::aod::lambdajetpol::collIdx; + Preslice perColJets = o2::aod::lambdajetpol::collisionId; + Preslice perColV0s = o2::aod::lambdajetpol::collisionId; void processPolarizationData(o2::aod::RingCollisions const& collisions, o2::aod::RingJets const& jets, o2::aod::RingLaV0s const& v0s){ for (auto const& collision : collisions) { - const auto collId = collision.collIdx(); + const auto collId = collision.collisionId(); // const double centrality = collision.centrality(); // (TODO: implement centrality!) // Slice jets and V0s belonging to this collision From 3329a517903047165452757612cdcac89773a825 Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Mon, 16 Feb 2026 17:27:08 -0300 Subject: [PATCH 18/40] Adding signal extraction histograms (mass dependencies) --- .../lambdaJetPolarizationIonsDerived.cxx | 92 +++++++++++++++++-- 1 file changed, 84 insertions(+), 8 deletions(-) diff --git a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx index 88317014527..2546aca2d0f 100644 --- a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx @@ -99,8 +99,30 @@ constexpr double antiLambdaWeakDecayConstant = -0.758; // DPG 2025 update X(FOLDER "/p2dRingObservableDeltaPhiVsLambdaPt", deltaPhiJet, v0pt, ringObservable) \ X(FOLDER "/p2dRingObservableDeltaThetaVsLambdaPt", deltaThetaJet, v0pt, ringObservable) \ X(FOLDER "/p2dRingObservableDeltaPhiVsLeadJetPt", deltaPhiJet, leadingJetPt, ringObservable) \ - X(FOLDER "/p2dRingObservableDeltaThetaVsLeadJetPt", deltaThetaJet, leadingJetPt, ringObservable) - // Lambda mass correlations (1D + 1D and 2D + 1D): (TODO: signal extraction attempts) + X(FOLDER "/p2dRingObservableDeltaThetaVsLeadJetPt", deltaThetaJet, leadingJetPt, ringObservable) \ + /* 1D Mass */ \ + X(FOLDER "/hMass", v0LambdaLikeMass) \ + X(FOLDER "/hRingObservableMass", v0LambdaLikeMass, ringObservable) \ + X(FOLDER "/hMassSigExtract", v0LambdaLikeMass) \ + /* 2D: Observable vs Mass */ \ + X(FOLDER "/h2dRingObservableDeltaPhiVsMass", deltaPhiJet, v0LambdaLikeMass, ringObservable) \ + X(FOLDER "/h2dRingObservableDeltaThetaVsMass", deltaThetaJet, v0LambdaLikeMass, ringObservable) \ + /* Counters */ \ + X(FOLDER "/h2dDeltaPhiVsMass", deltaPhiJet, v0LambdaLikeMass) \ + X(FOLDER "/h2dDeltaThetaVsMass", deltaThetaJet, v0LambdaLikeMass) \ + /* 3D: Observable vs Mass vs Lambda pT */ \ + X(FOLDER "/h3dRingObservableDeltaPhiVsMassVsLambdaPt", deltaPhiJet, v0LambdaLikeMass, v0pt, ringObservable) \ + X(FOLDER "/h3dRingObservableDeltaThetaVsMassVsLambdaPt", deltaThetaJet, v0LambdaLikeMass, v0pt, ringObservable) \ + /* Counters */ \ + X(FOLDER "/h3dDeltaPhiVsMassVsLambdaPt", deltaPhiJet, v0LambdaLikeMass, v0pt) \ + X(FOLDER "/h3dDeltaThetaVsMassVsLambdaPt", deltaThetaJet, v0LambdaLikeMass, v0pt) \ + /* 3D: Observable vs Mass vs Lead Jet pT */ \ + X(FOLDER "/h3dRingObservableDeltaPhiVsMassVsLeadJetPt", deltaPhiJet, v0LambdaLikeMass, leadingJetPt, ringObservable) \ + X(FOLDER "/h3dRingObservableDeltaThetaVsMassVsLeadJetPt", deltaThetaJet, v0LambdaLikeMass, leadingJetPt, ringObservable) \ + /* Counters */ \ + X(FOLDER "/h3dDeltaPhiVsMassVsLeadJetPt", deltaPhiJet, v0LambdaLikeMass, leadingJetPt) \ + X(FOLDER "/h3dDeltaThetaVsMassVsLeadJetPt", deltaThetaJet, v0LambdaLikeMass, leadingJetPt) + // ====================================================== // Ring Observable SQUARED histogram fill list @@ -117,7 +139,17 @@ constexpr double antiLambdaWeakDecayConstant = -0.758; // DPG 2025 update X(FOLDER "/h2dRingObservableSquaredDeltaThetaVsLambdaPt", deltaThetaJet, v0pt, ringObservableSquared) \ /* 2D Jet correlations */ \ X(FOLDER "/h2dRingObservableSquaredDeltaPhiVsLeadJetPt", deltaPhiJet, leadingJetPt, ringObservableSquared) \ - X(FOLDER "/h2dRingObservableSquaredDeltaThetaVsLeadJetPt",deltaThetaJet, leadingJetPt, ringObservableSquared) + X(FOLDER "/h2dRingObservableSquaredDeltaThetaVsLeadJetPt",deltaThetaJet, leadingJetPt, ringObservableSquared) \ + /* 2D - Mass correlations */ \ + X(FOLDER "/h2dRingObservableSquaredDeltaPhiVsMass", deltaPhiJet, v0LambdaLikeMass, ringObservableSquared) \ + X(FOLDER "/h2dRingObservableSquaredDeltaThetaVsMass", deltaThetaJet, v0LambdaLikeMass, ringObservableSquared) \ + /* 3D - LambdaPt */ \ + X(FOLDER "/h3dRingObservableSquaredDeltaPhiVsMassVsLambdaPt", deltaPhiJet, v0LambdaLikeMass, v0pt, ringObservableSquared) \ + X(FOLDER "/h3dRingObservableSquaredDeltaThetaVsMassVsLambdaPt", deltaThetaJet, v0LambdaLikeMass, v0pt, ringObservableSquared) \ + /* 3D - LeadJetPt*/ \ + X(FOLDER "/h3dRingObservableSquaredDeltaPhiVsMassVsLeadJetPt", deltaPhiJet, v0LambdaLikeMass, leadingJetPt, ringObservableSquared) \ + X(FOLDER "/h3dRingObservableSquaredDeltaThetaVsMassVsLeadJetPt", deltaThetaJet, v0LambdaLikeMass, leadingJetPt, ringObservableSquared) + // 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 @@ -141,7 +173,7 @@ 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 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 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 axisLambdaMass{"axisLambdaMass", {450, 1.08f, 1.15f}, "Lambda mass in GeV/c"}; // Default is {200, 1.101f, 1.131f} // Jet axes: @@ -149,6 +181,12 @@ struct lambdajetpolarizationionsderived { ConfigurableAxis axisJetPt{"axisJetPt",{100, 0.f, 200.f},"Jet p_{t} (GeV)"}; ConfigurableAxis axisCosTheta{"axisDeltaTheta", {100, 0, constants::math::PI}, "#Delta #theta_{jet}"}; ConfigurableAxis axisDeltaPhi{"axisDeltaPhi", {100, -constants::math::PI, constants::math::PI}, "#Delta #phi_{jet}"}; + + // Coarser axes for signal extraction: + ConfigurableAxis axisPtSigExtract{"axisPtSigExtract", {VARIABLE_WIDTH, 0.0f, 0.15f, 0.3f, 0.45f, 0.6f, 0.75f, 0.9f, 1.05f, 1.2f, 1.35f, 1.5f, 1.65f, 1.8f, 1.95f, 2.1f, 2.25f, 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 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. + 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, 4, 8, 12, 16, 20, 25, 30, 35, 40, 60, 100, 200},"Jet p_{t} (GeV)"}; } axisConfigurations; @@ -221,20 +259,58 @@ 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;cos#theta_{jet};<#it{R}>", kTProfile, {axisConfigurations.axisCosTheta}); + histos.add((folder + "/pRingObservableDeltaTheta").c_str(), "pRingObservableDeltaTheta;#Delta#theta_{jet};<#it{R}>", kTProfile, {axisConfigurations.axisCosTheta}); 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}); // =============================== // 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;cos#theta_{jet};#it{p}_{T}^{#Lambda};<#it{R}>", kTProfile2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisPt}); + histos.add((folder + "/p2dRingObservableDeltaThetaVsLambdaPt").c_str(), "p2dRingObservableDeltaThetaVsLambdaPt;#Delta#theta_{jet};#it{p}_{T}^{#Lambda};<#it{R}>", kTProfile2D, {axisConfigurations.axisCosTheta, 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;cos#theta_{jet};#it{p}_{T}^{lead jet};<#it{R}>", kTProfile2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisJetPt}); - // (TODO: add mass histograms for signal extraction) + histos.add((folder + "/p2dRingObservableDeltaThetaVsLeadJetPt").c_str(), "p2dRingObservableDeltaThetaVsLeadJetPt;#Delta#theta_{jet};#it{p}_{T}^{lead jet};<#it{R}>", kTProfile2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisJetPt}); + + // =============================== + // Multi-dimensional histograms for signal extraction + // (Mass-dependent polarization extraction) + // =============================== + // Simple invariant mass plot for QA: + histos.add((folder + "/hMass").c_str(), "hMass", kTH1D, {axisConfigurations.axisLambdaMass}); + histos.add((folder + "/hMassSigExtract").c_str(), "hMassSigExtract", kTH1D, {axisConfigurations.axisLambdaMassSigExtract}); + // 1D Observable vs Mass: + // Important to know if the signal varies with mass, or if the sideband signal subtraction will probably work well enough! + histos.add((folder + "/hRingObservableMass").c_str(), "hRingObservableMass", kTH1D, {axisConfigurations.axisLambdaMass}); + // --- 2D: Observable vs Invariant Mass --- + // Ring observable weighted with R + histos.add((folder + "/h2dRingObservableDeltaPhiVsMass").c_str(), "h2dRingObservableDeltaPhiVsMass", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract}); + histos.add((folder + "/h2dRingObservableDeltaThetaVsMass").c_str(), "h2dRingObservableDeltaThetaVsMass", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisLambdaMassSigExtract}); + // Squared observable (for variance propagation) + histos.add((folder + "/h2dRingObservableSquaredDeltaPhiVsMass").c_str(), "h2dRingObservableSquaredDeltaPhiVsMass", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract}); + histos.add((folder + "/h2dRingObservableSquaredDeltaThetaVsMass").c_str(), "h2dRingObservableSquaredDeltaThetaVsMass", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisLambdaMassSigExtract}); + // --- Counters (denominators) --- + histos.add((folder + "/h2dDeltaPhiVsMass").c_str(), "h2dDeltaPhiVsMass", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract}); + histos.add((folder + "/h2dDeltaThetaVsMass").c_str(), "h2dDeltaThetaVsMass", kTH2D,{axisConfigurations.axisCosTheta, axisConfigurations.axisLambdaMassSigExtract}); + // --- 3D: Observable vs Mass vs Lambda pT --- + histos.add((folder + "/h3dRingObservableDeltaPhiVsMassVsLambdaPt").c_str(), "h3dRingObservableDeltaPhiVsMassVsLambdaPt", kTH3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisPtSigExtract}); + histos.add((folder + "/h3dRingObservableDeltaThetaVsMassVsLambdaPt").c_str(), "h3dRingObservableDeltaThetaVsMassVsLambdaPt", kTH3D, {axisConfigurations.axisCosTheta,axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisPtSigExtract}); + // Squared version + histos.add((folder + "/h3dRingObservableSquaredDeltaPhiVsMassVsLambdaPt").c_str(), "h3dRingObservableSquaredDeltaPhiVsMassVsLambdaPt", kTH3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisPtSigExtract}); + histos.add((folder + "/h3dRingObservableSquaredDeltaThetaVsMassVsLambdaPt").c_str(), "h3dRingObservableSquaredDeltaThetaVsMassVsLambdaPt", kTH3D, {axisConfigurations.axisCosTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisPtSigExtract}); + // Counters + histos.add((folder + "/h3dDeltaPhiVsMassVsLambdaPt").c_str(), "h3dDeltaPhiVsMassVsLambdaPt", kTH3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisPtSigExtract}); + histos.add((folder + "/h3dDeltaThetaVsMassVsLambdaPt").c_str(), "h3dDeltaThetaVsMassVsLambdaPt", kTH3D, {axisConfigurations.axisCosTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisPtSigExtract}); + // --- 3D: Observable vs Mass vs Lead Jet pT --- + histos.add((folder + "/h3dRingObservableDeltaPhiVsMassVsLeadJetPt").c_str(), "h3dRingObservableDeltaPhiVsMassVsLeadJetPt", kTH3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisJetPt}); + histos.add((folder + "/h3dRingObservableDeltaThetaVsMassVsLeadJetPt").c_str(), "h3dRingObservableDeltaThetaVsMassVsLeadJetPt", kTH3D, {axisConfigurations.axisCosTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisJetPt}); + // --- Squared version --- + histos.add((folder + "/h3dRingObservableSquaredDeltaPhiVsMassVsLeadJetPt").c_str(), "h3dRingObservableSquaredDeltaPhiVsMassVsLeadJetPt", kTH3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisJetPt}); + histos.add((folder + "/h3dRingObservableSquaredDeltaThetaVsMassVsLeadJetPt").c_str(), "h3dRingObservableSquaredDeltaThetaVsMassVsLeadJetPt", kTH3D, {axisConfigurations.axisCosTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisJetPt}); + // --- Counters --- + histos.add((folder + "/h3dDeltaPhiVsMassVsLeadJetPt").c_str(), "h3dDeltaPhiVsMassVsLeadJetPt", kTH3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisJetPt}); + histos.add((folder + "/h3dDeltaThetaVsMassVsLeadJetPt").c_str(), "h3dDeltaThetaVsMassVsLeadJetPt", kTH3D, {axisConfigurations.axisCosTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisJetPt}); }; // Execute local lambda to register histogram families: addRingObservableFamily("Ring"); From 91a9de5864ba39ce899896acca374be814e36c48 Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Mon, 16 Feb 2026 19:53:08 -0300 Subject: [PATCH 19/40] Updating usage of coarser axis --- .../Strangeness/lambdaJetPolarizationIonsDerived.cxx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx index 2546aca2d0f..cd008d0e4e0 100644 --- a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx @@ -303,14 +303,14 @@ struct lambdajetpolarizationionsderived { histos.add((folder + "/h3dDeltaPhiVsMassVsLambdaPt").c_str(), "h3dDeltaPhiVsMassVsLambdaPt", kTH3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisPtSigExtract}); histos.add((folder + "/h3dDeltaThetaVsMassVsLambdaPt").c_str(), "h3dDeltaThetaVsMassVsLambdaPt", kTH3D, {axisConfigurations.axisCosTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisPtSigExtract}); // --- 3D: Observable vs Mass vs Lead Jet pT --- - histos.add((folder + "/h3dRingObservableDeltaPhiVsMassVsLeadJetPt").c_str(), "h3dRingObservableDeltaPhiVsMassVsLeadJetPt", kTH3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisJetPt}); - histos.add((folder + "/h3dRingObservableDeltaThetaVsMassVsLeadJetPt").c_str(), "h3dRingObservableDeltaThetaVsMassVsLeadJetPt", kTH3D, {axisConfigurations.axisCosTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisJetPt}); + histos.add((folder + "/h3dRingObservableDeltaPhiVsMassVsLeadJetPt").c_str(), "h3dRingObservableDeltaPhiVsMassVsLeadJetPt", kTH3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisJetPtSigExtract}); + histos.add((folder + "/h3dRingObservableDeltaThetaVsMassVsLeadJetPt").c_str(), "h3dRingObservableDeltaThetaVsMassVsLeadJetPt", kTH3D, {axisConfigurations.axisCosTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisJetPtSigExtract}); // --- Squared version --- - histos.add((folder + "/h3dRingObservableSquaredDeltaPhiVsMassVsLeadJetPt").c_str(), "h3dRingObservableSquaredDeltaPhiVsMassVsLeadJetPt", kTH3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisJetPt}); - histos.add((folder + "/h3dRingObservableSquaredDeltaThetaVsMassVsLeadJetPt").c_str(), "h3dRingObservableSquaredDeltaThetaVsMassVsLeadJetPt", kTH3D, {axisConfigurations.axisCosTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisJetPt}); + histos.add((folder + "/h3dRingObservableSquaredDeltaPhiVsMassVsLeadJetPt").c_str(), "h3dRingObservableSquaredDeltaPhiVsMassVsLeadJetPt", kTH3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisJetPtSigExtract}); + histos.add((folder + "/h3dRingObservableSquaredDeltaThetaVsMassVsLeadJetPt").c_str(), "h3dRingObservableSquaredDeltaThetaVsMassVsLeadJetPt", kTH3D, {axisConfigurations.axisCosTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisJetPtSigExtract}); // --- Counters --- - histos.add((folder + "/h3dDeltaPhiVsMassVsLeadJetPt").c_str(), "h3dDeltaPhiVsMassVsLeadJetPt", kTH3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisJetPt}); - histos.add((folder + "/h3dDeltaThetaVsMassVsLeadJetPt").c_str(), "h3dDeltaThetaVsMassVsLeadJetPt", kTH3D, {axisConfigurations.axisCosTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisJetPt}); + histos.add((folder + "/h3dDeltaPhiVsMassVsLeadJetPt").c_str(), "h3dDeltaPhiVsMassVsLeadJetPt", kTH3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisJetPtSigExtract}); + histos.add((folder + "/h3dDeltaThetaVsMassVsLeadJetPt").c_str(), "h3dDeltaThetaVsMassVsLeadJetPt", kTH3D, {axisConfigurations.axisCosTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisJetPtSigExtract}); }; // Execute local lambda to register histogram families: addRingObservableFamily("Ring"); From 5dfa2f4bed9b8f21f0825f630f85b54b0d63ca4d Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Tue, 17 Feb 2026 12:16:24 -0300 Subject: [PATCH 20/40] Fixing signal extraction pt axes (now contains the 0.5-1.5 interval exactly). Added Lambda rest frame polarization observables to compare with Hydro (still without event plane determination though!) --- .../lambdaJetPolarizationIonsDerived.cxx | 69 +++++++++++++++++-- 1 file changed, 65 insertions(+), 4 deletions(-) diff --git a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx index cd008d0e4e0..ac2d0569955 100644 --- a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx @@ -63,7 +63,8 @@ using ROOT::Math::PtEtaPhiMVector; 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; // Helper macro to avoid writing the histogram fills 4 times for about 20 histograms: #define RING_OBSERVABLE_FILL_LIST(X, FOLDER) \ @@ -150,6 +151,25 @@ constexpr double antiLambdaWeakDecayConstant = -0.758; // DPG 2025 update X(FOLDER "/h3dRingObservableSquaredDeltaPhiVsMassVsLeadJetPt", deltaPhiJet, v0LambdaLikeMass, leadingJetPt, ringObservableSquared) \ X(FOLDER "/h3dRingObservableSquaredDeltaThetaVsMassVsLeadJetPt", deltaThetaJet, v0LambdaLikeMass, leadingJetPt, ringObservableSquared) +#define POLARIZATION_PROFILE_FILL_LIST(X, FOLDER) \ + /* =============================== */ \ + /* 1D TProfiles vs v0phi */ \ + /* =============================== */ \ + X(FOLDER "/pPxStarPhi", v0phi, PolStarX) \ + X(FOLDER "/pPyStarPhi", v0phi, PolStarY) \ + X(FOLDER "/pPzStarPhi", v0phi, PolStarZ) \ + /* =============================== */ \ + /* 1D TProfiles vs DeltaPhi_jet */ \ + /* =============================== */ \ + X(FOLDER "/pPxStarDeltaPhi", deltaPhiJet, PolStarX) \ + X(FOLDER "/pPyStarDeltaPhi", deltaPhiJet, PolStarY) \ + X(FOLDER "/pPzStarDeltaPhi", deltaPhiJet, PolStarZ) \ + /* =============================== */ \ + /* 2D TProfiles vs DeltaPhi_jet and Lambda pT */ \ + /* =============================== */ \ + X(FOLDER "/p2dPxStarDeltaPhiVsLambdaPt", deltaPhiJet, v0pt, PolStarX) \ + X(FOLDER "/p2dPyStarDeltaPhiVsLambdaPt", deltaPhiJet, v0pt, PolStarY) \ + X(FOLDER "/p2dPzStarDeltaPhiVsLambdaPt", deltaPhiJet, v0pt, 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 @@ -183,10 +203,12 @@ struct lambdajetpolarizationionsderived { ConfigurableAxis axisDeltaPhi{"axisDeltaPhi", {100, -constants::math::PI, constants::math::PI}, "#Delta #phi_{jet}"}; // Coarser axes for signal extraction: - ConfigurableAxis axisPtSigExtract{"axisPtSigExtract", {VARIABLE_WIDTH, 0.0f, 0.15f, 0.3f, 0.45f, 0.6f, 0.75f, 0.9f, 1.05f, 1.2f, 1.35f, 1.5f, 1.65f, 1.8f, 1.95f, 2.1f, 2.25f, 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 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. 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, 4, 8, 12, 16, 20, 25, 30, 35, 40, 60, 100, 200},"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)"}; + + // (TODO: add a lambdaPt axis that is pre-selected only on the 0.5 to 1.5 Pt region) } axisConfigurations; @@ -311,6 +333,27 @@ struct lambdajetpolarizationionsderived { // --- Counters --- histos.add((folder + "/h3dDeltaPhiVsMassVsLeadJetPt").c_str(), "h3dDeltaPhiVsMassVsLeadJetPt", kTH3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisJetPtSigExtract}); histos.add((folder + "/h3dDeltaThetaVsMassVsLeadJetPt").c_str(), "h3dDeltaThetaVsMassVsLeadJetPt", kTH3D, {axisConfigurations.axisCosTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisJetPtSigExtract}); + + // =============================== + // 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 + // =============================== + // 1D TProfiles + // =============================== + histos.add((folder + "/pPxStarPhi").c_str(), "pPxStarPhi;#varphi_#Lambda;_x", kTProfile, {axisConfigurations.axisDeltaPhi}); + histos.add((folder + "/pPyStarPhi").c_str(), "pPyStarPhi;#varphi_#Lambda;_y", kTProfile, {axisConfigurations.axisDeltaPhi}); + histos.add((folder + "/pPzStarPhi").c_str(), "pPzStarPhi;#varphi_#Lambda;_z", kTProfile, {axisConfigurations.axisDeltaPhi}); + histos.add((folder + "/pPxStarDeltaPhi").c_str(), "pPxStarDeltaPhi;#Delta#varphi_{jet};_x", kTProfile, {axisConfigurations.axisDeltaPhi}); + histos.add((folder + "/pPyStarDeltaPhi").c_str(), "pPyStarDeltaPhi;#Delta#varphi_{jet};_y", kTProfile, {axisConfigurations.axisDeltaPhi}); + histos.add((folder + "/pPzStarDeltaPhi").c_str(), "pPzStarDeltaPhi;#Delta#varphi_{jet};_z", kTProfile, {axisConfigurations.axisDeltaPhi}); + // =============================== + // 2D TProfiles (Lambda correlations) + // =============================== + histos.add((folder + "/p2dPxStarDeltaPhiVsLambdaPt").c_str(), "p2dPxStarDeltaPhiVsLambdaPt;#Delta#varphi_{jet};#it{p}_{T}^{#Lambda};_x", kTProfile2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisPtSigExtract}); + histos.add((folder + "/p2dPyStarDeltaPhiVsLambdaPt").c_str(), "p2dPyStarDeltaPhiVsLambdaPt;#Delta#varphi_{jet};#it{p}_{T}^{#Lambda};_y", kTProfile2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisPtSigExtract}); + histos.add((folder + "/p2dPzStarDeltaPhiVsLambdaPt").c_str(), "p2dPzStarDeltaPhiVsLambdaPt;#Delta#varphi_{jet};#it{p}_{T}^{#Lambda};_z", kTProfile2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisPtSigExtract}); }; // Execute local lambda to register histogram families: addRingObservableFamily("Ring"); @@ -414,7 +457,7 @@ struct lambdajetpolarizationionsderived { double ringObservable = protonLikeStarUnit3Vec.Dot(cross) / crossProductNorm; // Adding the prefactor related to the CP-violating decay (decay constants have different signs) - ringObservable *= (isLambda) ? 3./lambdaWeakDecayConstant : 3./antiLambdaWeakDecayConstant; + ringObservable *= (isLambda) ? polPrefactorLambda : polPrefactorAntiLambda; // Calculating error bars: double ringObservableSquared = ringObservable*ringObservable; @@ -423,15 +466,31 @@ struct lambdajetpolarizationionsderived { double deltaPhiJet = wrapToPiFast(v0phi - leadingJetPhi); // Wrapped to [-PI, pi), for convenience double deltaThetaJet = ROOT::Math::VectorUtil::Angle(leadingJetUnitVec, lambdaLike3Vec); // 3D angular separation + // 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) * + double PolStarX, PolStarY, PolStarZ; + 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 if (isAntiLambda){ + PolStarX = polPrefactorAntiLambda * protonLikeStarUnit3Vec.X(); + PolStarY = polPrefactorAntiLambda * protonLikeStarUnit3Vec.Y(); + PolStarZ = polPrefactorAntiLambda * protonLikeStarUnit3Vec.Z(); + } + // Fill ring histograms: (1D, lambda 2D correlations and jet 2D correlations): RING_OBSERVABLE_FILL_LIST(APPLY_HISTO_FILL, "Ring") // Notice the usage of macros! If you change the variable names, this WILL break the code! RING_OBSERVABLE_SQUARED_FILL_LIST(APPLY_HISTO_FILL, "Ring") // No, there should NOT be any ";" here! Read the macro definition for an explanation + POLARIZATION_PROFILE_FILL_LIST(APPLY_HISTO_FILL, "Ring") // 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_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts") RING_OBSERVABLE_SQUARED_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts") + POLARIZATION_PROFILE_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts") } // Extra selection criteria on jet candidates: @@ -439,12 +498,14 @@ struct lambdajetpolarizationionsderived { if (kinematicJetCheck){ // This is redundant for jets with R=0.4, but for jets with R<0.4 the leading jet may be farther in eta. RING_OBSERVABLE_FILL_LIST(APPLY_HISTO_FILL, "JetKinematicCuts") RING_OBSERVABLE_SQUARED_FILL_LIST(APPLY_HISTO_FILL, "JetKinematicCuts") + POLARIZATION_PROFILE_FILL_LIST(APPLY_HISTO_FILL, "JetKinematicCuts") } // Extra selection criteria on both Lambda and jet candidates: if (kinematicLambdaCheck && kinematicJetCheck){ RING_OBSERVABLE_FILL_LIST(APPLY_HISTO_FILL, "JetAndLambdaKinematicCuts") RING_OBSERVABLE_SQUARED_FILL_LIST(APPLY_HISTO_FILL, "JetAndLambdaKinematicCuts") + POLARIZATION_PROFILE_FILL_LIST(APPLY_HISTO_FILL, "JetAndLambdaKinematicCuts") } } // end v0s loop } // end collisions From 103ee3773aa5db868ce35e1659a174096fcdae26 Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Wed, 18 Feb 2026 13:08:10 -0300 Subject: [PATCH 21/40] Fixing histogram names and wrapping some angular variables to reuse axes --- .../lambdaJetPolarizationIonsDerived.cxx | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx index ac2d0569955..26158cb743d 100644 --- a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx @@ -155,9 +155,9 @@ constexpr double polPrefactorAntiLambda = 3.0/antiLambdaWeakDecayConstant; /* =============================== */ \ /* 1D TProfiles vs v0phi */ \ /* =============================== */ \ - X(FOLDER "/pPxStarPhi", v0phi, PolStarX) \ - X(FOLDER "/pPyStarPhi", v0phi, PolStarY) \ - X(FOLDER "/pPzStarPhi", v0phi, PolStarZ) \ + X(FOLDER "/pPxStarPhi", v0phiToFillHists, PolStarX) \ + X(FOLDER "/pPyStarPhi", v0phiToFillHists, PolStarY) \ + X(FOLDER "/pPzStarPhi", v0phiToFillHists, PolStarZ) \ /* =============================== */ \ /* 1D TProfiles vs DeltaPhi_jet */ \ /* =============================== */ \ @@ -342,18 +342,18 @@ struct lambdajetpolarizationionsderived { // =============================== // 1D TProfiles // =============================== - histos.add((folder + "/pPxStarPhi").c_str(), "pPxStarPhi;#varphi_#Lambda;_x", kTProfile, {axisConfigurations.axisDeltaPhi}); - histos.add((folder + "/pPyStarPhi").c_str(), "pPyStarPhi;#varphi_#Lambda;_y", kTProfile, {axisConfigurations.axisDeltaPhi}); - histos.add((folder + "/pPzStarPhi").c_str(), "pPzStarPhi;#varphi_#Lambda;_z", kTProfile, {axisConfigurations.axisDeltaPhi}); - histos.add((folder + "/pPxStarDeltaPhi").c_str(), "pPxStarDeltaPhi;#Delta#varphi_{jet};_x", kTProfile, {axisConfigurations.axisDeltaPhi}); - histos.add((folder + "/pPyStarDeltaPhi").c_str(), "pPyStarDeltaPhi;#Delta#varphi_{jet};_y", kTProfile, {axisConfigurations.axisDeltaPhi}); - histos.add((folder + "/pPzStarDeltaPhi").c_str(), "pPzStarDeltaPhi;#Delta#varphi_{jet};_z", kTProfile, {axisConfigurations.axisDeltaPhi}); + histos.add((folder + "/pPxStarPhi").c_str(), "pPxStarPhi;#varphi_{#Lambda};_{x}", kTProfile, {axisConfigurations.axisDeltaPhi}); + histos.add((folder + "/pPyStarPhi").c_str(), "pPyStarPhi;#varphi_{#Lambda};_{y}", kTProfile, {axisConfigurations.axisDeltaPhi}); + histos.add((folder + "/pPzStarPhi").c_str(), "pPzStarPhi;#varphi_{#Lambda};_{z}", kTProfile, {axisConfigurations.axisDeltaPhi}); + histos.add((folder + "/pPxStarDeltaPhi").c_str(), "pPxStarDeltaPhi;#Delta#varphi_{jet};_{x}", kTProfile, {axisConfigurations.axisDeltaPhi}); + histos.add((folder + "/pPyStarDeltaPhi").c_str(), "pPyStarDeltaPhi;#Delta#varphi_{jet};_{y}", kTProfile, {axisConfigurations.axisDeltaPhi}); + histos.add((folder + "/pPzStarDeltaPhi").c_str(), "pPzStarDeltaPhi;#Delta#varphi_{jet};_{z}", kTProfile, {axisConfigurations.axisDeltaPhi}); // =============================== // 2D TProfiles (Lambda correlations) // =============================== - histos.add((folder + "/p2dPxStarDeltaPhiVsLambdaPt").c_str(), "p2dPxStarDeltaPhiVsLambdaPt;#Delta#varphi_{jet};#it{p}_{T}^{#Lambda};_x", kTProfile2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisPtSigExtract}); - histos.add((folder + "/p2dPyStarDeltaPhiVsLambdaPt").c_str(), "p2dPyStarDeltaPhiVsLambdaPt;#Delta#varphi_{jet};#it{p}_{T}^{#Lambda};_y", kTProfile2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisPtSigExtract}); - histos.add((folder + "/p2dPzStarDeltaPhiVsLambdaPt").c_str(), "p2dPzStarDeltaPhiVsLambdaPt;#Delta#varphi_{jet};#it{p}_{T}^{#Lambda};_z", kTProfile2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisPtSigExtract}); + histos.add((folder + "/p2dPxStarDeltaPhiVsLambdaPt").c_str(), "p2dPxStarDeltaPhiVsLambdaPt;#Delta#varphi_{jet};#it{p}_{T}^{#Lambda};_{x}", kTProfile2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisPtSigExtract}); + histos.add((folder + "/p2dPyStarDeltaPhiVsLambdaPt").c_str(), "p2dPyStarDeltaPhiVsLambdaPt;#Delta#varphi_{jet};#it{p}_{T}^{#Lambda};_{y}", kTProfile2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisPtSigExtract}); + histos.add((folder + "/p2dPzStarDeltaPhiVsLambdaPt").c_str(), "p2dPzStarDeltaPhiVsLambdaPt;#Delta#varphi_{jet};#it{p}_{T}^{#Lambda};_{z}", kTProfile2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisPtSigExtract}); }; // Execute local lambda to register histogram families: addRingObservableFamily("Ring"); @@ -480,6 +480,8 @@ struct lambdajetpolarizationionsderived { PolStarZ = polPrefactorAntiLambda * protonLikeStarUnit3Vec.Z(); } + double v0phiToFillHists = wrapToPiFast(v0phi); // A short wrap to reuse some predefined axes + // Fill ring histograms: (1D, lambda 2D correlations and jet 2D correlations): RING_OBSERVABLE_FILL_LIST(APPLY_HISTO_FILL, "Ring") // Notice the usage of macros! If you change the variable names, this WILL break the code! RING_OBSERVABLE_SQUARED_FILL_LIST(APPLY_HISTO_FILL, "Ring") // No, there should NOT be any ";" here! Read the macro definition for an explanation From ef120bda0caefc4ccd836e8c43f02f444404b7be Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Wed, 18 Feb 2026 16:28:37 -0300 Subject: [PATCH 22/40] Removing old parameter from file configurable group --- PWGLF/Tasks/Strangeness/lambdaJetPolarizationIons.cxx | 1 - 1 file changed, 1 deletion(-) diff --git a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIons.cxx b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIons.cxx index 30c76e9c14d..f35edbf29fe 100644 --- a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIons.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIons.cxx @@ -271,7 +271,6 @@ struct lambdajetpolarizationions { Configurable tpcPidNsigmaCut{"tpcPidNsigmaCut", 3, "tpcPidNsigmaCut"}; // Default is 5. Reduced to agree with strangenessInJetsIons Configurable tofPidNsigmaCutLaPr{"tofPidNsigmaCutLaPr", 1e+6, "tofPidNsigmaCutLaPr"}; Configurable tofPidNsigmaCutLaPi{"tofPidNsigmaCutLaPi", 1e+6, "tofPidNsigmaCutLaPi"}; - Configurable tofPidNsigmaCutK0Pi{"tofPidNsigmaCutK0Pi", 1e+6, "tofPidNsigmaCutK0Pi"}; // PID (TOF) Configurable maxDeltaTimeProton{"maxDeltaTimeProton", 1e+9, "check maximum allowed time"}; From c5aab8cba5026776ba3a67eb608a9857197a7599 Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Wed, 18 Feb 2026 18:24:52 -0300 Subject: [PATCH 23/40] Moving JetPolarization builder --- .../Strangeness/lambdaJetPolarizationIons.cxx | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename PWGLF/{Tasks => TableProducer}/Strangeness/lambdaJetPolarizationIons.cxx (100%) diff --git a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIons.cxx b/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx similarity index 100% rename from PWGLF/Tasks/Strangeness/lambdaJetPolarizationIons.cxx rename to PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx From 6b5a309b8bc2027e99b4628ad1f524792a475831 Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Thu, 19 Feb 2026 19:21:00 -0300 Subject: [PATCH 24/40] Fixing centrality usage --- .../Strangeness/lambdaJetPolarizationIons.cxx | 86 ++++++++++--------- 1 file changed, 44 insertions(+), 42 deletions(-) diff --git a/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx b/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx index f35edbf29fe..a78786ec34e 100644 --- a/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx +++ b/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx @@ -122,7 +122,7 @@ using DauTracks = soa::Join; // using V0CandidatesSimple = soa::Join; // No TOF -using PseudoJetTracks = soa::Join; // Simpler tracks access. (TODO: Should I include TracksIU and TracksCovIU? I don't use any getters from them!) +using PseudoJetTracks = soa::Join; // Simpler tracks access. (Not using TracksIU and TracksCovIU. Did not use their info for now) // , aod::pidTOFFullPi, aod::pidTOFFullKa, aod::pidTOFFullPr>; // Not using TOF right now due to some possible mismatches // using SimCollisions = soa::Join; // using DauTracksMC = soa::Join; @@ -167,7 +167,7 @@ struct lambdajetpolarizationions { ///////////////////////////////////////////// Configurable doEventQA{"doEventQA", false, "do event QA histograms"}; - Configurable qaCentrality{"qaCentrality", false, "qa centrality flag: check base raw values"}; + // 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 doArmenterosQA{"doArmenterosQA", false, "do Armenteros QA histograms"}; @@ -524,8 +524,8 @@ struct lambdajetpolarizationions { histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(20, "Above max IR"); histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(21, "RCT flags"); - histos.add("hEventCentrality", "hEventCentrality", kTH1D, {{101, 0.0f, 101.0f}}); - histos.add("hCentralityVsNch", "hCentralityVsNch", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisNch}); + 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, {{21, -0.5f, +20.5f}, {101, 0.0f, 101.0f}}); histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(1, "All collisions"); @@ -555,16 +555,16 @@ struct lambdajetpolarizationions { histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(20, "Above max IR"); histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(21, "RCT flags"); - histos.add("hCentralityVsNGlobal", "hCentralityVsNGlobal", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisNch}); - histos.add("hEventCentVsMultFT0M", "hEventCentVsMultFT0M", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisMultFT0M}); - histos.add("hEventCentVsMultFT0C", "hEventCentVsMultFT0C", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisMultFT0C}); - histos.add("hEventCentVsMultNGlobal", "hEventCentVsMultNGlobal", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisNch}); - histos.add("hEventCentVsMultFV0A", "hEventCentVsMultFV0A", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisMultFV0A}); - histos.add("hEventMultFT0MvsMultNGlobal", "hEventMultFT0MvsMultNGlobal", kTH2D, {axisConfigurations.axisMultFT0M, axisConfigurations.axisNch}); - histos.add("hEventMultFT0CvsMultNGlobal", "hEventMultFT0CvsMultNGlobal", kTH2D, {axisConfigurations.axisMultFT0C, axisConfigurations.axisNch}); - histos.add("hEventMultFV0AvsMultNGlobal", "hEventMultFV0AvsMultNGlobal", kTH2D, {axisConfigurations.axisMultFV0A, axisConfigurations.axisNch}); - histos.add("hEventMultPVvsMultNGlobal", "hEventMultPVvsMultNGlobal", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisNch}); - histos.add("hEventMultFT0CvsMultFV0A", "hEventMultFT0CvsMultFV0A", kTH2D, {axisConfigurations.axisMultFT0C, axisConfigurations.axisMultFV0A}); + histos.add("Centrality/hCentralityVsNGlobal", "hCentralityVsNGlobal", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisNch}); + 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/hEventCentVsMultNGlobal", "hEventCentVsMultNGlobal", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisNch}); + histos.add("Centrality/hEventCentVsMultFV0A", "hEventCentVsMultFV0A", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisMultFV0A}); + histos.add("Centrality/hEventMultFT0MvsMultNGlobal", "hEventMultFT0MvsMultNGlobal", kTH2D, {axisConfigurations.axisMultFT0M, axisConfigurations.axisNch}); + histos.add("Centrality/hEventMultFT0CvsMultNGlobal", "hEventMultFT0CvsMultNGlobal", kTH2D, {axisConfigurations.axisMultFT0C, axisConfigurations.axisNch}); + histos.add("Centrality/hEventMultFV0AvsMultNGlobal", "hEventMultFV0AvsMultNGlobal", kTH2D, {axisConfigurations.axisMultFV0A, axisConfigurations.axisNch}); + histos.add("Centrality/hEventMultPVvsMultNGlobal", "hEventMultPVvsMultNGlobal", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisNch}); + histos.add("Centrality/hEventMultFT0CvsMultFV0A", "hEventMultFT0CvsMultFV0A", kTH2D, {axisConfigurations.axisMultFT0C, axisConfigurations.axisMultFV0A}); } histos.add("hEventPVz", "hEventPVz", kTH1D, {{100, -20.0f, +20.0f}}); @@ -579,12 +579,12 @@ struct lambdajetpolarizationions { histos.add("hInteractionRateVsOccupancy", "hInteractionRateVsOccupancy", kTH2D, {axisConfigurations.axisIRBinning, axisConfigurations.axisOccupancy}); // for QA and test purposes - auto hRawCentrality = histos.add("hRawCentrality", "hRawCentrality", kTH1D, {axisConfigurations.axisRawCentrality}); + // auto hRawCentrality = histos.add("Centrality/hRawCentrality", "hRawCentrality", kTH1D, {axisConfigurations.axisRawCentrality}); - for (int ii = 1; ii < 101; ii++) { - float value = 100.5f - static_cast(ii); - hRawCentrality->SetBinContent(ii, value); - } + // for (int ii = 1; ii < 101; ii++) { + // float value = 100.5f - static_cast(ii); + // hRawCentrality->SetBinContent(ii, value); + // } ////////////////////////////////////////////////////////////// /// Lambda / AntiLambda V0 selection QA @@ -616,10 +616,10 @@ struct lambdajetpolarizationions { {p + "DCA_{#pi} to PV", true}, {p + "TPC PID p", v0Selections.tpcPidNsigmaCut > 0}, {p + "TPC PID #pi", v0Selections.tpcPidNsigmaCut > 0}, - {p + "TOF #Delta t p", v0Selections.maxDeltaTimeProton < 1e8}, - {p + "TOF #Delta t #pi", v0Selections.maxDeltaTimePion < 1e8}, - {p + "TOF PID p", v0Selections.tofPidNsigmaCutLaPr < 1e8}, - {p + "TOF PID #pi", v0Selections.tofPidNsigmaCutLaPi < 1e8}, + {p + "TOF #Delta t p", v0Selections.maxDeltaTimeProton < 1e+9}, + {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} }); }; @@ -914,10 +914,12 @@ struct lambdajetpolarizationions { // Minimal helper to fill the hSelectionV0s histogram without having to deal with bins by myself // (CAUTION! If you change selection order, change this too!) struct V0SelectionFlowCounter{ // Using struct to keep internal bin counter over different functions - int binValue = 0; // Starts at x=0, which is bin 1 in the definition of hSelectionV0s + 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 = 0;} + void resetForNewV0(){binValue = -1;} void fill(){histos->fill(HIST("GeneralQA/hSelectionV0s"), ++binValue);} // Hardcoded hSelectionV0s histogram, as it will not change. Increments before filling, by default }; V0SelectionFlowCounter V0SelCounter{0, &histos}; @@ -933,23 +935,23 @@ struct lambdajetpolarizationions { template void fillCentralityProperties(TCollision const& collision, float centrality) { - if (qaCentrality) { - auto hRawCentrality = histos.get(HIST("hRawCentrality")); - centrality = hRawCentrality->GetBinContent(hRawCentrality->FindBin(doPPAnalysis ? collision.multFT0A() + collision.multFT0C() : collision.multFT0C())); - } - histos.fill(HIST("hEventCentrality"), centrality); - histos.fill(HIST("hCentralityVsNch"), centrality, collision.multNTracksPVeta1()); + // if (qaCentrality) { + // auto hRawCentrality = histos.get(HIST("Centrality/hRawCentrality")); + // centrality = hRawCentrality->GetBinContent(hRawCentrality->FindBin(doPPAnalysis ? collision.multFT0A() + collision.multFT0C() : collision.multFT0C())); + // } + histos.fill(HIST("Centrality/hEventCentrality"), centrality); + histos.fill(HIST("Centrality/hCentralityVsNch"), centrality, collision.multNTracksPVeta1()); if (doEventQA) { - histos.fill(HIST("hCentralityVsNGlobal"), centrality, collision.multNTracksGlobal()); - histos.fill(HIST("hEventCentVsMultFT0M"), collision.centFT0M(), collision.multFT0A() + collision.multFT0C()); - histos.fill(HIST("hEventCentVsMultFT0C"), collision.centFT0C(), collision.multFT0C()); - histos.fill(HIST("hEventCentVsMultNGlobal"), collision.centNGlobal(), collision.multNTracksGlobal()); - histos.fill(HIST("hEventCentVsMultFV0A"), collision.centFV0A(), collision.multFV0A()); - histos.fill(HIST("hEventMultFT0MvsMultNGlobal"), collision.multFT0A() + collision.multFT0C(), collision.multNTracksGlobal()); - histos.fill(HIST("hEventMultFT0CvsMultNGlobal"), collision.multFT0C(), collision.multNTracksGlobal()); - histos.fill(HIST("hEventMultFV0AvsMultNGlobal"), collision.multFV0A(), collision.multNTracksGlobal()); - histos.fill(HIST("hEventMultPVvsMultNGlobal"), collision.multNTracksPVeta1(), collision.multNTracksGlobal()); - histos.fill(HIST("hEventMultFT0CvsMultFV0A"), collision.multFT0C(), collision.multFV0A()); + histos.fill(HIST("Centrality/hCentralityVsNGlobal"), centrality, collision.multNTracksGlobal()); + histos.fill(HIST("Centrality/hEventCentVsMultFT0M"), collision.centFT0M(), collision.multFT0A() + collision.multFT0C()); + histos.fill(HIST("Centrality/hEventCentVsMultFT0C"), collision.centFT0C(), collision.multFT0C()); + histos.fill(HIST("Centrality/hEventCentVsMultNGlobal"), collision.centNGlobal(), collision.multNTracksGlobal()); + histos.fill(HIST("Centrality/hEventCentVsMultFV0A"), collision.centFV0A(), collision.multFV0A()); + histos.fill(HIST("Centrality/hEventMultFT0MvsMultNGlobal"), collision.multFT0A() + collision.multFT0C(), collision.multNTracksGlobal()); + histos.fill(HIST("Centrality/hEventMultFT0CvsMultNGlobal"), collision.multFT0C(), collision.multNTracksGlobal()); + histos.fill(HIST("Centrality/hEventMultFV0AvsMultNGlobal"), collision.multFV0A(), collision.multNTracksGlobal()); + histos.fill(HIST("Centrality/hEventMultPVvsMultNGlobal"), collision.multNTracksPVeta1(), collision.multNTracksGlobal()); + histos.fill(HIST("Centrality/hEventMultFT0CvsMultFV0A"), collision.multFT0C(), collision.multFV0A()); } return; } @@ -1524,7 +1526,7 @@ struct lambdajetpolarizationions { // Had to include DauTracks in subscription, even though I don't loop in it, for the indices to resolve, avoiding " Exception while running: Index pointing to Tracks is not bound!" void processV0sData(SelCollisions::iterator const& collision, V0CandidatesWithTOF const& fullV0s, aod::BCsWithTimestamps const& bcs, DauTracks const& V0DauTracks){ - const float centrality = getCentrality(collision); + float centrality = getCentrality(collision); histos.fill(HIST("hEventSelection"), 0. /* all collisions */); histos.fill(HIST("hEventSelectionVsCentrality"), 0. /* all collisions */, centrality); From 7c9475afabf6fa0c5067f1b17c569fe68c541a64 Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Thu, 19 Feb 2026 19:21:17 -0300 Subject: [PATCH 25/40] Fixing CMakeLists for new Derived data consumer --- PWGLF/TableProducer/Strangeness/CMakeLists.txt | 5 +++++ PWGLF/Tasks/Strangeness/CMakeLists.txt | 5 ----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/PWGLF/TableProducer/Strangeness/CMakeLists.txt b/PWGLF/TableProducer/Strangeness/CMakeLists.txt index 43c6331ae07..7c711caa5fd 100644 --- a/PWGLF/TableProducer/Strangeness/CMakeLists.txt +++ b/PWGLF/TableProducer/Strangeness/CMakeLists.txt @@ -147,6 +147,11 @@ o2physics_add_dpl_workflow(lambdajetpolarizationbuilder PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(lambdajetpolarizationions + SOURCES lambdaJetPolarizationIons.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGJECore FastJet::FastJet FastJet::Contrib O2Physics::EventFilteringUtils O2Physics::AnalysisCCDB +COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(stracents SOURCES stracents.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore diff --git a/PWGLF/Tasks/Strangeness/CMakeLists.txt b/PWGLF/Tasks/Strangeness/CMakeLists.txt index ce647140388..89d6615a8e8 100644 --- a/PWGLF/Tasks/Strangeness/CMakeLists.txt +++ b/PWGLF/Tasks/Strangeness/CMakeLists.txt @@ -166,11 +166,6 @@ o2physics_add_dpl_workflow(lambdajetpolarization PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGJECore FastJet::FastJet FastJet::Contrib O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) -o2physics_add_dpl_workflow(lambdajetpolarizationions -SOURCES lambdaJetPolarizationIons.cxx -PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGJECore FastJet::FastJet FastJet::Contrib O2Physics::EventFilteringUtils O2Physics::AnalysisCCDB -COMPONENT_NAME Analysis) - o2physics_add_dpl_workflow(lambdajetpolarizationionsderived SOURCES lambdaJetPolarizationIonsDerived.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore From 7f87d66637373339bebf010b9e3cb081abef55d6 Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Thu, 19 Feb 2026 19:51:51 -0300 Subject: [PATCH 26/40] Adding centrality selections, more TProfiles for appropriate error bar calculation, coarser binning in the LambdaMass axis for signal extraction --- .../lambdaJetPolarizationIonsDerived.cxx | 144 ++++++++++++++---- 1 file changed, 111 insertions(+), 33 deletions(-) diff --git a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx index 26158cb743d..3794b504f3a 100644 --- a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx @@ -78,7 +78,7 @@ constexpr double polPrefactorAntiLambda = 3.0/antiLambdaWeakDecayConstant; X(FOLDER "/hIntegrated", 0.) \ /* Lambda pT variation -- Youpeng's proposal */ \ X(FOLDER "/hRingObservableLambdaPt", v0pt, ringObservable) \ - X(FOLDER "/hRingObservableLambdaPt", v0pt) \ + X(FOLDER "/hLambdaPt", v0pt) \ /* 2D Lambda correlations */ \ X(FOLDER "/h2dRingObservableDeltaPhiVsLambdaPt", deltaPhiJet, v0pt, ringObservable) \ X(FOLDER "/h2dRingObservableDeltaThetaVsLambdaPt", deltaThetaJet, v0pt, ringObservable) \ @@ -122,7 +122,32 @@ constexpr double polPrefactorAntiLambda = 3.0/antiLambdaWeakDecayConstant; X(FOLDER "/h3dRingObservableDeltaThetaVsMassVsLeadJetPt", deltaThetaJet, v0LambdaLikeMass, leadingJetPt, ringObservable) \ /* Counters */ \ X(FOLDER "/h3dDeltaPhiVsMassVsLeadJetPt", deltaPhiJet, v0LambdaLikeMass, leadingJetPt) \ - X(FOLDER "/h3dDeltaThetaVsMassVsLeadJetPt", deltaThetaJet, v0LambdaLikeMass, leadingJetPt) + X(FOLDER "/h3dDeltaThetaVsMassVsLeadJetPt", deltaThetaJet, v0LambdaLikeMass, leadingJetPt) \ + /* 2D: Observable vs Mass vs Centrality (projected as 2D Mass vs Cent for integrated observable) */ \ + X(FOLDER "/h2dRingObservableMassVsCent", v0LambdaLikeMass, centrality, ringObservable) \ + /* 3D: Observable vs Mass vs Centrality */ \ + X(FOLDER "/h3dRingObservableDeltaPhiVsMassVsCent", deltaPhiJet, v0LambdaLikeMass, centrality, ringObservable) \ + X(FOLDER "/h3dRingObservableDeltaThetaVsMassVsCent", deltaThetaJet, v0LambdaLikeMass, centrality, ringObservable) \ + /* Counters */ \ + X(FOLDER "/h3dDeltaPhiVsMassVsCent", deltaPhiJet, v0LambdaLikeMass, centrality) \ + X(FOLDER "/h3dDeltaThetaVsMassVsCent", deltaThetaJet, v0LambdaLikeMass, centrality) \ + /* TProfile of Ring vs Mass */ \ + X(FOLDER "/pRingObservableMass", v0LambdaLikeMass, ringObservable) \ + /* 2D Profiles: Angle vs Mass */ \ + X(FOLDER "/p2dRingObservableDeltaPhiVsMass", deltaPhiJet, v0LambdaLikeMass, ringObservable) \ + X(FOLDER "/p2dRingObservableDeltaThetaVsMass", deltaThetaJet, v0LambdaLikeMass, ringObservable) \ + /* 3D Profiles: Angle vs Mass vs Lambda pT */ \ + X(FOLDER "/p3dRingObservableDeltaPhiVsMassVsLambdaPt", deltaPhiJet, v0LambdaLikeMass, v0pt, ringObservable) \ + X(FOLDER "/p3dRingObservableDeltaThetaVsMassVsLambdaPt", deltaThetaJet, v0LambdaLikeMass, v0pt, ringObservable) \ + /* 3D Profiles: Angle vs Mass vs Lead Jet pT */ \ + X(FOLDER "/p3dRingObservableDeltaPhiVsMassVsLeadJetPt", deltaPhiJet, v0LambdaLikeMass, leadingJetPt, ringObservable) \ + X(FOLDER "/p3dRingObservableDeltaThetaVsMassVsLeadJetPt", deltaThetaJet, v0LambdaLikeMass, leadingJetPt, ringObservable) \ + /* 2D Profile: Mass vs Centrality */ \ + X(FOLDER "/p2dRingObservableMassVsCent", v0LambdaLikeMass, centrality, ringObservable) \ + /* 3D Profiles: Angle vs Mass vs Centrality */ \ + X(FOLDER "/p3dRingObservableDeltaPhiVsMassVsCent", deltaPhiJet, v0LambdaLikeMass, centrality, ringObservable) \ + X(FOLDER "/p3dRingObservableDeltaThetaVsMassVsCent", deltaThetaJet, v0LambdaLikeMass, centrality, ringObservable) + // (TODO: add counters for regular TH2Ds about centrality) // ====================================================== @@ -149,7 +174,10 @@ constexpr double polPrefactorAntiLambda = 3.0/antiLambdaWeakDecayConstant; X(FOLDER "/h3dRingObservableSquaredDeltaThetaVsMassVsLambdaPt", deltaThetaJet, v0LambdaLikeMass, v0pt, ringObservableSquared) \ /* 3D - LeadJetPt*/ \ X(FOLDER "/h3dRingObservableSquaredDeltaPhiVsMassVsLeadJetPt", deltaPhiJet, v0LambdaLikeMass, leadingJetPt, ringObservableSquared) \ - X(FOLDER "/h3dRingObservableSquaredDeltaThetaVsMassVsLeadJetPt", deltaThetaJet, v0LambdaLikeMass, leadingJetPt, ringObservableSquared) + X(FOLDER "/h3dRingObservableSquaredDeltaThetaVsMassVsLeadJetPt", deltaThetaJet, v0LambdaLikeMass, leadingJetPt, ringObservableSquared) \ + /* 3D: Squared observable vs Mass vs Centrality */ \ + X(FOLDER "/h3dRingObservableSquaredDeltaPhiVsMassVsCent", deltaPhiJet, v0LambdaLikeMass, centrality, ringObservableSquared) \ + X(FOLDER "/h3dRingObservableSquaredDeltaThetaVsMassVsCent", deltaThetaJet, v0LambdaLikeMass, centrality, ringObservableSquared) #define POLARIZATION_PROFILE_FILL_LIST(X, FOLDER) \ /* =============================== */ \ @@ -199,16 +227,21 @@ struct lambdajetpolarizationionsderived { // Jet axes: ConfigurableAxis axisLeadingParticlePt{"axisLeadingParticlePt",{100, 0.f, 200.f},"Leading particle p_{T} (GeV/c)"}; // Simpler version! ConfigurableAxis axisJetPt{"axisJetPt",{100, 0.f, 200.f},"Jet p_{t} (GeV)"}; - ConfigurableAxis axisCosTheta{"axisDeltaTheta", {100, 0, constants::math::PI}, "#Delta #theta_{jet}"}; + ConfigurableAxis axisDeltaTheta{"axisDeltaTheta", {100, 0, constants::math::PI}, "#Delta #theta_{jet}"}; ConfigurableAxis axisDeltaPhi{"axisDeltaPhi", {100, -constants::math::PI, constants::math::PI}, "#Delta #phi_{jet}"}; // 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. + // 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. + // Rewrote the axisLambdaMassSigextract to have 5x coarser bins outside the peak region, and 2x coarser bins in the peak region + // (this allows for better fits and smaller fluctuations) + ConfigurableAxis axisLambdaMassSigExtract{"axisLambdaMassSigExtract", {VARIABLE_WIDTH, 1.080000, 1.082000, 1.084000, 1.086000, 1.088000, 1.090000, 1.092000, 1.094000, 1.096000, 1.098000, 1.100000, 1.102000, 1.104000, 1.106000, 1.108000, 1.109683, 1.110483, 1.111283, 1.112083, 1.112883, 1.113683, 1.114483, 1.115283, 1.116083, 1.116883, 1.117683, 1.118483, 1.119283, 1.120083, 1.120883, 1.121683, 1.123683, 1.125683, 1.127683, 1.129683, 1.131683, 1.133683, 1.135683, 1.137683, 1.139683, 1.141683, 1.143683, 1.145683, 1.147683, 1.149683, 1.150000}, "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)"}; // (TODO: add a lambdaPt axis that is pre-selected only on the 0.5 to 1.5 Pt region) + + 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"}; } axisConfigurations; @@ -230,44 +263,45 @@ struct lambdajetpolarizationionsderived { // =============================== // 1D observable histograms // =============================== - histos.add((folder + "/hRingObservableDeltaPhi").c_str(), "hRingObservableDeltaPhi", kTH1D, {axisConfigurations.axisDeltaPhi}); - histos.add((folder + "/hRingObservableDeltaTheta").c_str(), "hRingObservableDeltaTheta", kTH1D, {axisConfigurations.axisCosTheta}); + histos.add((folder + "/hRingObservableDeltaPhi").c_str(), "hRingObservableDeltaPhi", kTH1D, {axisConfigurations.axisDeltaPhi}); // Not quite the ring observable itself: this is just the numerator of = \sum R_i / N_\Lambda (error bars WILL be wrong without TProfile) + histos.add((folder + "/hRingObservableDeltaTheta").c_str(), "hRingObservableDeltaTheta", kTH1D, {axisConfigurations.axisDeltaTheta}); histos.add((folder + "/hRingObservableIntegrated").c_str(), "hRingObservableIntegrated", kTH1D, {{1, -0.5, 0.5}}); // Counters (denominators) histos.add((folder + "/hDeltaPhi").c_str(), "hDeltaPhi", kTH1D, {axisConfigurations.axisDeltaPhi}); - histos.add((folder + "/hDeltaTheta").c_str(), "hDeltaTheta", kTH1D, {axisConfigurations.axisCosTheta}); + histos.add((folder + "/hDeltaTheta").c_str(), "hDeltaTheta", kTH1D, {axisConfigurations.axisDeltaTheta}); histos.add((folder + "/hIntegrated").c_str(), "hIntegrated", kTH1D, {{1, -0.5, 0.5}}); // =============================== // Lambda pT dependence // =============================== histos.add((folder + "/hRingObservableLambdaPt").c_str(), "hRingObservableLambdaPt", kTH1D, {axisConfigurations.axisPt}); + histos.add((folder + "/hLambdaPt").c_str(), "hLambdaPt", kTH1D, {axisConfigurations.axisPt}); // =============================== // 2D Lambda correlations // =============================== histos.add((folder + "/h2dRingObservableDeltaPhiVsLambdaPt").c_str(), "h2dRingObservableDeltaPhiVsLambdaPt", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisPt}); - histos.add((folder + "/h2dRingObservableDeltaThetaVsLambdaPt").c_str(), "h2dRingObservableDeltaThetaVsLambdaPt", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisPt}); + histos.add((folder + "/h2dRingObservableDeltaThetaVsLambdaPt").c_str(), "h2dRingObservableDeltaThetaVsLambdaPt", kTH2D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisPt}); // Counters histos.add((folder + "/h2dDeltaPhiVsLambdaPt").c_str(), "h2dDeltaPhiVsLambdaPt", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisPt}); - histos.add((folder + "/h2dDeltaThetaVsLambdaPt").c_str(), "h2dDeltaThetaVsLambdaPt", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisPt}); + histos.add((folder + "/h2dDeltaThetaVsLambdaPt").c_str(), "h2dDeltaThetaVsLambdaPt", kTH2D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisPt}); // =============================== // 2D Jet correlations // =============================== histos.add((folder + "/h2dRingObservableDeltaPhiVsLeadJetPt").c_str(), "h2dRingObservableDeltaPhiVsLeadJetPt", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisJetPt}); - histos.add((folder + "/h2dRingObservableDeltaThetaVsLeadJetPt").c_str(), "h2dRingObservableDeltaThetaVsLeadJetPt", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisJetPt}); + histos.add((folder + "/h2dRingObservableDeltaThetaVsLeadJetPt").c_str(), "h2dRingObservableDeltaThetaVsLeadJetPt", kTH2D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisJetPt}); // Counters histos.add((folder + "/h2dDeltaPhiVsLeadJetPt").c_str(), "h2dDeltaPhiVsLeadJetPt", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisJetPt}); - histos.add((folder + "/h2dDeltaThetaVsLeadJetPt").c_str(), "h2dDeltaThetaVsLeadJetPt", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisJetPt}); + histos.add((folder + "/h2dDeltaThetaVsLeadJetPt").c_str(), "h2dDeltaThetaVsLeadJetPt", kTH2D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisJetPt}); // =============================== // Squared observable (error propagation) // =============================== histos.add((folder + "/hRingObservableSquaredDeltaPhi").c_str(), "hRingObservableSquaredDeltaPhi", kTH1D, {axisConfigurations.axisDeltaPhi}); - histos.add((folder + "/hRingObservableSquaredDeltaTheta").c_str(), "hRingObservableSquaredDeltaTheta", kTH1D, {axisConfigurations.axisCosTheta}); + histos.add((folder + "/hRingObservableSquaredDeltaTheta").c_str(), "hRingObservableSquaredDeltaTheta", kTH1D, {axisConfigurations.axisDeltaTheta}); histos.add((folder + "/hRingObservableSquaredIntegrated").c_str(), "hRingObservableSquaredIntegrated", kTH1D, {{1, -0.5, 0.5}}); histos.add((folder + "/hRingObservableSquaredLambdaPt").c_str(), "hRingObservableSquaredLambdaPt", kTH1D, {axisConfigurations.axisPt}); histos.add((folder + "/h2dRingObservableSquaredDeltaPhiVsLambdaPt").c_str(), "h2dRingObservableSquaredDeltaPhiVsLambdaPt", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisPt}); - histos.add((folder + "/h2dRingObservableSquaredDeltaThetaVsLambdaPt").c_str(), "h2dRingObservableSquaredDeltaThetaVsLambdaPt", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisPt}); + histos.add((folder + "/h2dRingObservableSquaredDeltaThetaVsLambdaPt").c_str(), "h2dRingObservableSquaredDeltaThetaVsLambdaPt", kTH2D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisPt}); histos.add((folder + "/h2dRingObservableSquaredDeltaPhiVsLeadJetPt").c_str(), "h2dRingObservableSquaredDeltaPhiVsLeadJetPt", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisJetPt}); - histos.add((folder + "/h2dRingObservableSquaredDeltaThetaVsLeadJetPt").c_str(), "h2dRingObservableSquaredDeltaThetaVsLeadJetPt", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisJetPt}); + histos.add((folder + "/h2dRingObservableSquaredDeltaThetaVsLeadJetPt").c_str(), "h2dRingObservableSquaredDeltaThetaVsLeadJetPt", kTH2D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisJetPt}); // Additional plots for instant gratification: // -- TProfiles will handle the error estimate of the Ring Observable via the variance, even though @@ -281,58 +315,102 @@ 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.axisCosTheta}); + 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}); // =============================== // 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.axisCosTheta, axisConfigurations.axisPt}); + histos.add((folder + "/p2dRingObservableDeltaThetaVsLambdaPt").c_str(), "p2dRingObservableDeltaThetaVsLambdaPt;#Delta#theta_{jet};#it{p}_{T}^{#Lambda};<#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.axisCosTheta, axisConfigurations.axisJetPt}); + histos.add((folder + "/p2dRingObservableDeltaThetaVsLeadJetPt").c_str(), "p2dRingObservableDeltaThetaVsLeadJetPt;#Delta#theta_{jet};#it{p}_{T}^{lead jet};<#it{R}>", kTProfile2D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisJetPt}); // =============================== // Multi-dimensional histograms for signal extraction // (Mass-dependent polarization extraction) // =============================== + // (TODO: possibly remove all TH2Ds that are not counter histograms and deal only with TProfiles!) // Simple invariant mass plot for QA: histos.add((folder + "/hMass").c_str(), "hMass", kTH1D, {axisConfigurations.axisLambdaMass}); histos.add((folder + "/hMassSigExtract").c_str(), "hMassSigExtract", kTH1D, {axisConfigurations.axisLambdaMassSigExtract}); - // 1D Observable vs Mass: + // 1D Mass dependence of observable: // Important to know if the signal varies with mass, or if the sideband signal subtraction will probably work well enough! - histos.add((folder + "/hRingObservableMass").c_str(), "hRingObservableMass", kTH1D, {axisConfigurations.axisLambdaMass}); - // --- 2D: Observable vs Invariant Mass --- + histos.add((folder + "/hRingObservableMass").c_str(), "hRingObservableMass", kTH1D, {axisConfigurations.axisLambdaMassSigExtract}); + // --- 2D: Angular observable vs Invariant Mass --- // Ring observable weighted with R histos.add((folder + "/h2dRingObservableDeltaPhiVsMass").c_str(), "h2dRingObservableDeltaPhiVsMass", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract}); - histos.add((folder + "/h2dRingObservableDeltaThetaVsMass").c_str(), "h2dRingObservableDeltaThetaVsMass", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisLambdaMassSigExtract}); + histos.add((folder + "/h2dRingObservableDeltaThetaVsMass").c_str(), "h2dRingObservableDeltaThetaVsMass", kTH2D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract}); // Squared observable (for variance propagation) histos.add((folder + "/h2dRingObservableSquaredDeltaPhiVsMass").c_str(), "h2dRingObservableSquaredDeltaPhiVsMass", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract}); - histos.add((folder + "/h2dRingObservableSquaredDeltaThetaVsMass").c_str(), "h2dRingObservableSquaredDeltaThetaVsMass", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisLambdaMassSigExtract}); + histos.add((folder + "/h2dRingObservableSquaredDeltaThetaVsMass").c_str(), "h2dRingObservableSquaredDeltaThetaVsMass", kTH2D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract}); // --- Counters (denominators) --- histos.add((folder + "/h2dDeltaPhiVsMass").c_str(), "h2dDeltaPhiVsMass", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract}); - histos.add((folder + "/h2dDeltaThetaVsMass").c_str(), "h2dDeltaThetaVsMass", kTH2D,{axisConfigurations.axisCosTheta, axisConfigurations.axisLambdaMassSigExtract}); - // --- 3D: Observable vs Mass vs Lambda pT --- + histos.add((folder + "/h2dDeltaThetaVsMass").c_str(), "h2dDeltaThetaVsMass", kTH2D,{axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract}); + // --- 3D: Angular observable vs Mass vs Lambda pT --- histos.add((folder + "/h3dRingObservableDeltaPhiVsMassVsLambdaPt").c_str(), "h3dRingObservableDeltaPhiVsMassVsLambdaPt", kTH3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisPtSigExtract}); - histos.add((folder + "/h3dRingObservableDeltaThetaVsMassVsLambdaPt").c_str(), "h3dRingObservableDeltaThetaVsMassVsLambdaPt", kTH3D, {axisConfigurations.axisCosTheta,axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisPtSigExtract}); + histos.add((folder + "/h3dRingObservableDeltaThetaVsMassVsLambdaPt").c_str(), "h3dRingObservableDeltaThetaVsMassVsLambdaPt", kTH3D, {axisConfigurations.axisDeltaTheta,axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisPtSigExtract}); // Squared version histos.add((folder + "/h3dRingObservableSquaredDeltaPhiVsMassVsLambdaPt").c_str(), "h3dRingObservableSquaredDeltaPhiVsMassVsLambdaPt", kTH3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisPtSigExtract}); - histos.add((folder + "/h3dRingObservableSquaredDeltaThetaVsMassVsLambdaPt").c_str(), "h3dRingObservableSquaredDeltaThetaVsMassVsLambdaPt", kTH3D, {axisConfigurations.axisCosTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisPtSigExtract}); + histos.add((folder + "/h3dRingObservableSquaredDeltaThetaVsMassVsLambdaPt").c_str(), "h3dRingObservableSquaredDeltaThetaVsMassVsLambdaPt", kTH3D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisPtSigExtract}); // Counters histos.add((folder + "/h3dDeltaPhiVsMassVsLambdaPt").c_str(), "h3dDeltaPhiVsMassVsLambdaPt", kTH3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisPtSigExtract}); - histos.add((folder + "/h3dDeltaThetaVsMassVsLambdaPt").c_str(), "h3dDeltaThetaVsMassVsLambdaPt", kTH3D, {axisConfigurations.axisCosTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisPtSigExtract}); - // --- 3D: Observable vs Mass vs Lead Jet pT --- + histos.add((folder + "/h3dDeltaThetaVsMassVsLambdaPt").c_str(), "h3dDeltaThetaVsMassVsLambdaPt", kTH3D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisPtSigExtract}); + // --- 3D: Angular observable vs Mass vs Lead Jet pT --- histos.add((folder + "/h3dRingObservableDeltaPhiVsMassVsLeadJetPt").c_str(), "h3dRingObservableDeltaPhiVsMassVsLeadJetPt", kTH3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisJetPtSigExtract}); - histos.add((folder + "/h3dRingObservableDeltaThetaVsMassVsLeadJetPt").c_str(), "h3dRingObservableDeltaThetaVsMassVsLeadJetPt", kTH3D, {axisConfigurations.axisCosTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisJetPtSigExtract}); + histos.add((folder + "/h3dRingObservableDeltaThetaVsMassVsLeadJetPt").c_str(), "h3dRingObservableDeltaThetaVsMassVsLeadJetPt", kTH3D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisJetPtSigExtract}); // --- Squared version --- histos.add((folder + "/h3dRingObservableSquaredDeltaPhiVsMassVsLeadJetPt").c_str(), "h3dRingObservableSquaredDeltaPhiVsMassVsLeadJetPt", kTH3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisJetPtSigExtract}); - histos.add((folder + "/h3dRingObservableSquaredDeltaThetaVsMassVsLeadJetPt").c_str(), "h3dRingObservableSquaredDeltaThetaVsMassVsLeadJetPt", kTH3D, {axisConfigurations.axisCosTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisJetPtSigExtract}); + histos.add((folder + "/h3dRingObservableSquaredDeltaThetaVsMassVsLeadJetPt").c_str(), "h3dRingObservableSquaredDeltaThetaVsMassVsLeadJetPt", kTH3D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisJetPtSigExtract}); // --- Counters --- histos.add((folder + "/h3dDeltaPhiVsMassVsLeadJetPt").c_str(), "h3dDeltaPhiVsMassVsLeadJetPt", kTH3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisJetPtSigExtract}); - histos.add((folder + "/h3dDeltaThetaVsMassVsLeadJetPt").c_str(), "h3dDeltaThetaVsMassVsLeadJetPt", kTH3D, {axisConfigurations.axisCosTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisJetPtSigExtract}); + histos.add((folder + "/h3dDeltaThetaVsMassVsLeadJetPt").c_str(), "h3dDeltaThetaVsMassVsLeadJetPt", kTH3D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisJetPtSigExtract}); + + ///////////////////////////////////// + /// TProfiles with the proper errors for quick glancing + ///////////////////////////////////// + // 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}); + // TProfile2D: vs Mass (DeltaPhi) + 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}); + + // --- 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 --- + histos.add((folder + "/p3dRingObservableDeltaThetaVsMassVsLambdaPt").c_str(), "p3dRingObservableDeltaThetaVsMassVsLambdaPt;#Delta#theta;m_{p#pi};p_{T}^{#Lambda};<#it{R}>", 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}); + // --- 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}); + + // =============================== + // Mass histograms with centrality + // =============================== + // 2D Mass dependence of observable vs Centrality: + // Important to know if the signal varies with mass, or if the sideband signal subtraction will probably work well enough! + histos.add((folder + "/h2dRingObservableMassVsCent").c_str(), "h2dRingObservableMassVsCent", kTH2D, {axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisCentrality}); + // --- 3D: Angular observable vs Invariant Mass --- + histos.add((folder + "/h3dRingObservableDeltaPhiVsMassVsCent").c_str(), "h3dRingObservableDeltaPhiVsMassVsCent", kTH3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisCentrality}); + histos.add((folder + "/h3dRingObservableDeltaThetaVsMassVsCent").c_str(), "h3dRingObservableDeltaThetaVsMassVsCent", kTH3D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisCentrality}); + // Squared observable (for variance propagation) + histos.add((folder + "/h3dRingObservableSquaredDeltaPhiVsMassVsCent").c_str(), "h3dRingObservableSquaredDeltaPhiVsMassVsCent", kTH3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisCentrality}); + histos.add((folder + "/h3dRingObservableSquaredDeltaThetaVsMassVsCent").c_str(), "h3dRingObservableSquaredDeltaThetaVsMassVsCent", kTH3D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisCentrality}); + // --- Counters (denominators) --- + histos.add((folder + "/h3dDeltaPhiVsMassVsCent").c_str(), "h3dDeltaPhiVsMassVsCent", kTH3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisCentrality}); + histos.add((folder + "/h3dDeltaThetaVsMassVsCent").c_str(), "h3dDeltaThetaVsMassVsCent", kTH3D,{axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisCentrality}); + + // Useful TProfiles: + // --- 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 --- + histos.add((folder + "/p3dRingObservableDeltaPhiVsMassVsCent").c_str(), "p3dRingObservableDeltaPhiVsMassVsCent;#Delta#varphi;m_{p#pi};Centrality;<#it{R}>", 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}); // =============================== // Polarization observable QAs @@ -372,7 +450,7 @@ struct lambdajetpolarizationionsderived { void processPolarizationData(o2::aod::RingCollisions const& collisions, o2::aod::RingJets const& jets, o2::aod::RingLaV0s const& v0s){ for (auto const& collision : collisions) { const auto collId = collision.collisionId(); - // const double centrality = collision.centrality(); // (TODO: implement centrality!) + const double centrality = collision.centrality(); // Slice jets and V0s belonging to this collision // (global collision indices repeat a lot, but they are unique to a same TimeFrame (TF) subfolder in the derived data) From c6c0d51ccbb0834c972f8cf102d9979c0c31b172 Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Thu, 19 Feb 2026 21:21:36 -0300 Subject: [PATCH 27/40] Removing unused squared observable plots (no longer using them for error propagation! Used TProfiles for convenience and correctness) --- .../lambdaJetPolarizationIonsDerived.cxx | 111 +++++++++--------- 1 file changed, 56 insertions(+), 55 deletions(-) diff --git a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx index 3794b504f3a..7e7294ac3a2 100644 --- a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx @@ -150,34 +150,34 @@ constexpr double polPrefactorAntiLambda = 3.0/antiLambdaWeakDecayConstant; // (TODO: add counters for regular TH2Ds about centrality) -// ====================================================== -// Ring Observable SQUARED histogram fill list -// ====================================================== -#define RING_OBSERVABLE_SQUARED_FILL_LIST(X, FOLDER) \ - /* 1D observable histograms */ \ - X(FOLDER "/hRingObservableSquaredDeltaPhi", deltaPhiJet, ringObservableSquared) \ - X(FOLDER "/hRingObservableSquaredDeltaTheta", deltaThetaJet, ringObservableSquared) \ - X(FOLDER "/hRingObservableSquaredIntegrated", 0., ringObservableSquared) \ - /* Lambda pT variation */ \ - X(FOLDER "/hRingObservableSquaredLambdaPt", v0pt, ringObservableSquared) \ - /* 2D Lambda correlations */ \ - X(FOLDER "/h2dRingObservableSquaredDeltaPhiVsLambdaPt", deltaPhiJet, v0pt, ringObservableSquared) \ - X(FOLDER "/h2dRingObservableSquaredDeltaThetaVsLambdaPt", deltaThetaJet, v0pt, ringObservableSquared) \ - /* 2D Jet correlations */ \ - X(FOLDER "/h2dRingObservableSquaredDeltaPhiVsLeadJetPt", deltaPhiJet, leadingJetPt, ringObservableSquared) \ - X(FOLDER "/h2dRingObservableSquaredDeltaThetaVsLeadJetPt",deltaThetaJet, leadingJetPt, ringObservableSquared) \ - /* 2D - Mass correlations */ \ - X(FOLDER "/h2dRingObservableSquaredDeltaPhiVsMass", deltaPhiJet, v0LambdaLikeMass, ringObservableSquared) \ - X(FOLDER "/h2dRingObservableSquaredDeltaThetaVsMass", deltaThetaJet, v0LambdaLikeMass, ringObservableSquared) \ - /* 3D - LambdaPt */ \ - X(FOLDER "/h3dRingObservableSquaredDeltaPhiVsMassVsLambdaPt", deltaPhiJet, v0LambdaLikeMass, v0pt, ringObservableSquared) \ - X(FOLDER "/h3dRingObservableSquaredDeltaThetaVsMassVsLambdaPt", deltaThetaJet, v0LambdaLikeMass, v0pt, ringObservableSquared) \ - /* 3D - LeadJetPt*/ \ - X(FOLDER "/h3dRingObservableSquaredDeltaPhiVsMassVsLeadJetPt", deltaPhiJet, v0LambdaLikeMass, leadingJetPt, ringObservableSquared) \ - X(FOLDER "/h3dRingObservableSquaredDeltaThetaVsMassVsLeadJetPt", deltaThetaJet, v0LambdaLikeMass, leadingJetPt, ringObservableSquared) \ - /* 3D: Squared observable vs Mass vs Centrality */ \ - X(FOLDER "/h3dRingObservableSquaredDeltaPhiVsMassVsCent", deltaPhiJet, v0LambdaLikeMass, centrality, ringObservableSquared) \ - X(FOLDER "/h3dRingObservableSquaredDeltaThetaVsMassVsCent", deltaThetaJet, v0LambdaLikeMass, centrality, ringObservableSquared) +// // ====================================================== +// // Ring Observable SQUARED histogram fill list +// // ====================================================== +// #define RING_OBSERVABLE_SQUARED_FILL_LIST(X, FOLDER) \ +// /* 1D observable histograms */ \ +// X(FOLDER "/hRingObservableSquaredDeltaPhi", deltaPhiJet, ringObservableSquared) \ +// X(FOLDER "/hRingObservableSquaredDeltaTheta", deltaThetaJet, ringObservableSquared) \ +// X(FOLDER "/hRingObservableSquaredIntegrated", 0., ringObservableSquared) \ +// /* Lambda pT variation */ \ +// X(FOLDER "/hRingObservableSquaredLambdaPt", v0pt, ringObservableSquared) \ +// /* 2D Lambda correlations */ \ +// X(FOLDER "/h2dRingObservableSquaredDeltaPhiVsLambdaPt", deltaPhiJet, v0pt, ringObservableSquared) \ +// X(FOLDER "/h2dRingObservableSquaredDeltaThetaVsLambdaPt", deltaThetaJet, v0pt, ringObservableSquared) \ +// /* 2D Jet correlations */ \ +// X(FOLDER "/h2dRingObservableSquaredDeltaPhiVsLeadJetPt", deltaPhiJet, leadingJetPt, ringObservableSquared) \ +// X(FOLDER "/h2dRingObservableSquaredDeltaThetaVsLeadJetPt",deltaThetaJet, leadingJetPt, ringObservableSquared) \ +// /* 2D - Mass correlations */ \ +// X(FOLDER "/h2dRingObservableSquaredDeltaPhiVsMass", deltaPhiJet, v0LambdaLikeMass, ringObservableSquared) \ +// X(FOLDER "/h2dRingObservableSquaredDeltaThetaVsMass", deltaThetaJet, v0LambdaLikeMass, ringObservableSquared) \ +// /* 3D - LambdaPt */ \ +// X(FOLDER "/h3dRingObservableSquaredDeltaPhiVsMassVsLambdaPt", deltaPhiJet, v0LambdaLikeMass, v0pt, ringObservableSquared) \ +// X(FOLDER "/h3dRingObservableSquaredDeltaThetaVsMassVsLambdaPt", deltaThetaJet, v0LambdaLikeMass, v0pt, ringObservableSquared) \ +// /* 3D - LeadJetPt*/ \ +// X(FOLDER "/h3dRingObservableSquaredDeltaPhiVsMassVsLeadJetPt", deltaPhiJet, v0LambdaLikeMass, leadingJetPt, ringObservableSquared) \ +// X(FOLDER "/h3dRingObservableSquaredDeltaThetaVsMassVsLeadJetPt", deltaThetaJet, v0LambdaLikeMass, leadingJetPt, ringObservableSquared) \ +// /* 3D: Squared observable vs Mass vs Centrality */ \ +// X(FOLDER "/h3dRingObservableSquaredDeltaPhiVsMassVsCent", deltaPhiJet, v0LambdaLikeMass, centrality, ringObservableSquared) \ +// X(FOLDER "/h3dRingObservableSquaredDeltaThetaVsMassVsCent", deltaThetaJet, v0LambdaLikeMass, centrality, ringObservableSquared) #define POLARIZATION_PROFILE_FILL_LIST(X, FOLDER) \ /* =============================== */ \ @@ -291,17 +291,18 @@ struct lambdajetpolarizationionsderived { // Counters histos.add((folder + "/h2dDeltaPhiVsLeadJetPt").c_str(), "h2dDeltaPhiVsLeadJetPt", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisJetPt}); histos.add((folder + "/h2dDeltaThetaVsLeadJetPt").c_str(), "h2dDeltaThetaVsLeadJetPt", kTH2D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisJetPt}); - // =============================== - // Squared observable (error propagation) - // =============================== - histos.add((folder + "/hRingObservableSquaredDeltaPhi").c_str(), "hRingObservableSquaredDeltaPhi", kTH1D, {axisConfigurations.axisDeltaPhi}); - histos.add((folder + "/hRingObservableSquaredDeltaTheta").c_str(), "hRingObservableSquaredDeltaTheta", kTH1D, {axisConfigurations.axisDeltaTheta}); - histos.add((folder + "/hRingObservableSquaredIntegrated").c_str(), "hRingObservableSquaredIntegrated", kTH1D, {{1, -0.5, 0.5}}); - histos.add((folder + "/hRingObservableSquaredLambdaPt").c_str(), "hRingObservableSquaredLambdaPt", kTH1D, {axisConfigurations.axisPt}); - histos.add((folder + "/h2dRingObservableSquaredDeltaPhiVsLambdaPt").c_str(), "h2dRingObservableSquaredDeltaPhiVsLambdaPt", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisPt}); - histos.add((folder + "/h2dRingObservableSquaredDeltaThetaVsLambdaPt").c_str(), "h2dRingObservableSquaredDeltaThetaVsLambdaPt", kTH2D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisPt}); - histos.add((folder + "/h2dRingObservableSquaredDeltaPhiVsLeadJetPt").c_str(), "h2dRingObservableSquaredDeltaPhiVsLeadJetPt", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisJetPt}); - histos.add((folder + "/h2dRingObservableSquaredDeltaThetaVsLeadJetPt").c_str(), "h2dRingObservableSquaredDeltaThetaVsLeadJetPt", kTH2D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisJetPt}); + // (TODO: check if all squared observables were actually transformed into a (much better) TProfile version) + // // =============================== + // // Squared observable (error propagation) + // // =============================== + // histos.add((folder + "/hRingObservableSquaredDeltaPhi").c_str(), "hRingObservableSquaredDeltaPhi", kTH1D, {axisConfigurations.axisDeltaPhi}); + // histos.add((folder + "/hRingObservableSquaredDeltaTheta").c_str(), "hRingObservableSquaredDeltaTheta", kTH1D, {axisConfigurations.axisDeltaTheta}); + // histos.add((folder + "/hRingObservableSquaredIntegrated").c_str(), "hRingObservableSquaredIntegrated", kTH1D, {{1, -0.5, 0.5}}); + // histos.add((folder + "/hRingObservableSquaredLambdaPt").c_str(), "hRingObservableSquaredLambdaPt", kTH1D, {axisConfigurations.axisPt}); + // histos.add((folder + "/h2dRingObservableSquaredDeltaPhiVsLambdaPt").c_str(), "h2dRingObservableSquaredDeltaPhiVsLambdaPt", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisPt}); + // histos.add((folder + "/h2dRingObservableSquaredDeltaThetaVsLambdaPt").c_str(), "h2dRingObservableSquaredDeltaThetaVsLambdaPt", kTH2D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisPt}); + // histos.add((folder + "/h2dRingObservableSquaredDeltaPhiVsLeadJetPt").c_str(), "h2dRingObservableSquaredDeltaPhiVsLeadJetPt", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisJetPt}); + // histos.add((folder + "/h2dRingObservableSquaredDeltaThetaVsLeadJetPt").c_str(), "h2dRingObservableSquaredDeltaThetaVsLeadJetPt", kTH2D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisJetPt}); // Additional plots for instant gratification: // -- TProfiles will handle the error estimate of the Ring Observable via the variance, even though @@ -344,27 +345,27 @@ struct lambdajetpolarizationionsderived { // Ring observable weighted with R histos.add((folder + "/h2dRingObservableDeltaPhiVsMass").c_str(), "h2dRingObservableDeltaPhiVsMass", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract}); histos.add((folder + "/h2dRingObservableDeltaThetaVsMass").c_str(), "h2dRingObservableDeltaThetaVsMass", kTH2D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract}); - // Squared observable (for variance propagation) - histos.add((folder + "/h2dRingObservableSquaredDeltaPhiVsMass").c_str(), "h2dRingObservableSquaredDeltaPhiVsMass", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract}); - histos.add((folder + "/h2dRingObservableSquaredDeltaThetaVsMass").c_str(), "h2dRingObservableSquaredDeltaThetaVsMass", kTH2D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract}); + // // Squared observable (for variance propagation) + // histos.add((folder + "/h2dRingObservableSquaredDeltaPhiVsMass").c_str(), "h2dRingObservableSquaredDeltaPhiVsMass", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract}); + // histos.add((folder + "/h2dRingObservableSquaredDeltaThetaVsMass").c_str(), "h2dRingObservableSquaredDeltaThetaVsMass", kTH2D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract}); // --- Counters (denominators) --- histos.add((folder + "/h2dDeltaPhiVsMass").c_str(), "h2dDeltaPhiVsMass", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract}); histos.add((folder + "/h2dDeltaThetaVsMass").c_str(), "h2dDeltaThetaVsMass", kTH2D,{axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract}); // --- 3D: Angular observable vs Mass vs Lambda pT --- histos.add((folder + "/h3dRingObservableDeltaPhiVsMassVsLambdaPt").c_str(), "h3dRingObservableDeltaPhiVsMassVsLambdaPt", kTH3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisPtSigExtract}); histos.add((folder + "/h3dRingObservableDeltaThetaVsMassVsLambdaPt").c_str(), "h3dRingObservableDeltaThetaVsMassVsLambdaPt", kTH3D, {axisConfigurations.axisDeltaTheta,axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisPtSigExtract}); - // Squared version - histos.add((folder + "/h3dRingObservableSquaredDeltaPhiVsMassVsLambdaPt").c_str(), "h3dRingObservableSquaredDeltaPhiVsMassVsLambdaPt", kTH3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisPtSigExtract}); - histos.add((folder + "/h3dRingObservableSquaredDeltaThetaVsMassVsLambdaPt").c_str(), "h3dRingObservableSquaredDeltaThetaVsMassVsLambdaPt", kTH3D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisPtSigExtract}); + // // Squared version + // histos.add((folder + "/h3dRingObservableSquaredDeltaPhiVsMassVsLambdaPt").c_str(), "h3dRingObservableSquaredDeltaPhiVsMassVsLambdaPt", kTH3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisPtSigExtract}); + // histos.add((folder + "/h3dRingObservableSquaredDeltaThetaVsMassVsLambdaPt").c_str(), "h3dRingObservableSquaredDeltaThetaVsMassVsLambdaPt", kTH3D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisPtSigExtract}); // Counters histos.add((folder + "/h3dDeltaPhiVsMassVsLambdaPt").c_str(), "h3dDeltaPhiVsMassVsLambdaPt", kTH3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisPtSigExtract}); histos.add((folder + "/h3dDeltaThetaVsMassVsLambdaPt").c_str(), "h3dDeltaThetaVsMassVsLambdaPt", kTH3D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisPtSigExtract}); // --- 3D: Angular observable vs Mass vs Lead Jet pT --- histos.add((folder + "/h3dRingObservableDeltaPhiVsMassVsLeadJetPt").c_str(), "h3dRingObservableDeltaPhiVsMassVsLeadJetPt", kTH3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisJetPtSigExtract}); histos.add((folder + "/h3dRingObservableDeltaThetaVsMassVsLeadJetPt").c_str(), "h3dRingObservableDeltaThetaVsMassVsLeadJetPt", kTH3D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisJetPtSigExtract}); - // --- Squared version --- - histos.add((folder + "/h3dRingObservableSquaredDeltaPhiVsMassVsLeadJetPt").c_str(), "h3dRingObservableSquaredDeltaPhiVsMassVsLeadJetPt", kTH3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisJetPtSigExtract}); - histos.add((folder + "/h3dRingObservableSquaredDeltaThetaVsMassVsLeadJetPt").c_str(), "h3dRingObservableSquaredDeltaThetaVsMassVsLeadJetPt", kTH3D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisJetPtSigExtract}); + // // --- Squared version --- + // histos.add((folder + "/h3dRingObservableSquaredDeltaPhiVsMassVsLeadJetPt").c_str(), "h3dRingObservableSquaredDeltaPhiVsMassVsLeadJetPt", kTH3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisJetPtSigExtract}); + // histos.add((folder + "/h3dRingObservableSquaredDeltaThetaVsMassVsLeadJetPt").c_str(), "h3dRingObservableSquaredDeltaThetaVsMassVsLeadJetPt", kTH3D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisJetPtSigExtract}); // --- Counters --- histos.add((folder + "/h3dDeltaPhiVsMassVsLeadJetPt").c_str(), "h3dDeltaPhiVsMassVsLeadJetPt", kTH3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisJetPtSigExtract}); histos.add((folder + "/h3dDeltaThetaVsMassVsLeadJetPt").c_str(), "h3dDeltaThetaVsMassVsLeadJetPt", kTH3D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisJetPtSigExtract}); @@ -397,9 +398,9 @@ struct lambdajetpolarizationionsderived { // --- 3D: Angular observable vs Invariant Mass --- histos.add((folder + "/h3dRingObservableDeltaPhiVsMassVsCent").c_str(), "h3dRingObservableDeltaPhiVsMassVsCent", kTH3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisCentrality}); histos.add((folder + "/h3dRingObservableDeltaThetaVsMassVsCent").c_str(), "h3dRingObservableDeltaThetaVsMassVsCent", kTH3D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisCentrality}); - // Squared observable (for variance propagation) - histos.add((folder + "/h3dRingObservableSquaredDeltaPhiVsMassVsCent").c_str(), "h3dRingObservableSquaredDeltaPhiVsMassVsCent", kTH3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisCentrality}); - histos.add((folder + "/h3dRingObservableSquaredDeltaThetaVsMassVsCent").c_str(), "h3dRingObservableSquaredDeltaThetaVsMassVsCent", kTH3D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisCentrality}); + // // Squared observable (for variance propagation) + // histos.add((folder + "/h3dRingObservableSquaredDeltaPhiVsMassVsCent").c_str(), "h3dRingObservableSquaredDeltaPhiVsMassVsCent", kTH3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisCentrality}); + // histos.add((folder + "/h3dRingObservableSquaredDeltaThetaVsMassVsCent").c_str(), "h3dRingObservableSquaredDeltaThetaVsMassVsCent", kTH3D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisCentrality}); // --- Counters (denominators) --- histos.add((folder + "/h3dDeltaPhiVsMassVsCent").c_str(), "h3dDeltaPhiVsMassVsCent", kTH3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisCentrality}); histos.add((folder + "/h3dDeltaThetaVsMassVsCent").c_str(), "h3dDeltaThetaVsMassVsCent", kTH3D,{axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisCentrality}); @@ -562,14 +563,14 @@ struct lambdajetpolarizationionsderived { // Fill ring histograms: (1D, lambda 2D correlations and jet 2D correlations): RING_OBSERVABLE_FILL_LIST(APPLY_HISTO_FILL, "Ring") // Notice the usage of macros! If you change the variable names, this WILL break the code! - RING_OBSERVABLE_SQUARED_FILL_LIST(APPLY_HISTO_FILL, "Ring") // No, there should NOT be any ";" here! Read the macro definition for an explanation + // RING_OBSERVABLE_SQUARED_FILL_LIST(APPLY_HISTO_FILL, "Ring") // No, there should NOT be any ";" here! Read the macro definition for an explanation POLARIZATION_PROFILE_FILL_LIST(APPLY_HISTO_FILL, "Ring") // 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_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts") - RING_OBSERVABLE_SQUARED_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts") + // RING_OBSERVABLE_SQUARED_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts") POLARIZATION_PROFILE_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts") } @@ -577,14 +578,14 @@ struct lambdajetpolarizationionsderived { const bool kinematicJetCheck = std::abs(leadingJetEta) < 0.5; if (kinematicJetCheck){ // This is redundant for jets with R=0.4, but for jets with R<0.4 the leading jet may be farther in eta. RING_OBSERVABLE_FILL_LIST(APPLY_HISTO_FILL, "JetKinematicCuts") - RING_OBSERVABLE_SQUARED_FILL_LIST(APPLY_HISTO_FILL, "JetKinematicCuts") + // RING_OBSERVABLE_SQUARED_FILL_LIST(APPLY_HISTO_FILL, "JetKinematicCuts") POLARIZATION_PROFILE_FILL_LIST(APPLY_HISTO_FILL, "JetKinematicCuts") } // Extra selection criteria on both Lambda and jet candidates: if (kinematicLambdaCheck && kinematicJetCheck){ RING_OBSERVABLE_FILL_LIST(APPLY_HISTO_FILL, "JetAndLambdaKinematicCuts") - RING_OBSERVABLE_SQUARED_FILL_LIST(APPLY_HISTO_FILL, "JetAndLambdaKinematicCuts") + // RING_OBSERVABLE_SQUARED_FILL_LIST(APPLY_HISTO_FILL, "JetAndLambdaKinematicCuts") POLARIZATION_PROFILE_FILL_LIST(APPLY_HISTO_FILL, "JetAndLambdaKinematicCuts") } } // end v0s loop From 34e72508b230e708f00a405f4beeff8bb5e06641 Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Wed, 25 Feb 2026 21:13:43 -0300 Subject: [PATCH 28/40] Adding convenience enums to builder + new hEventSelection bins for QA. Adding QA calculations of ring observable with perpendicular jet direction and inverse polarization sign to test hypothesis of true signal --- PWGLF/DataModel/lambdaJetPolarizationIons.h | 1 + .../Strangeness/lambdaJetPolarizationIons.cxx | 82 ++++++-- .../lambdaJetPolarizationIonsDerived.cxx | 183 ++++++++++++++---- 3 files changed, 212 insertions(+), 54 deletions(-) diff --git a/PWGLF/DataModel/lambdaJetPolarizationIons.h b/PWGLF/DataModel/lambdaJetPolarizationIons.h index c0531c0da02..f3ad984067c 100644 --- a/PWGLF/DataModel/lambdaJetPolarizationIons.h +++ b/PWGLF/DataModel/lambdaJetPolarizationIons.h @@ -54,6 +54,7 @@ DECLARE_SOA_COLUMN(NegEta, negEta, float); DECLARE_SOA_COLUMN(NegPhi, negPhi, float); // (TODO: add dynamic columns with jet px, py, pz) +// (TODO: add leading particle as one of the columns!) } // namespace lambdajetpol diff --git a/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx b/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx index a78786ec34e..f775e79fc91 100644 --- a/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx +++ b/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx @@ -142,6 +142,32 @@ enum CentEstimator { kCentFV0A }; +enum JetAlgorithm { + kKt = 0, + kCambridgeAachen, + kAntiKt +}; + +enum JetRecombScheme { + kEScheme = 0, + kPtScheme = 1, + kPt2Scheme = 2, + kWTAScheme = 7 +}; + +enum JetType { + kChargedJet = 0, + kFullJet, + kPhotonJet, + kZJet +}; + +enum BkgSubtraction { + kNoSubtraction = 0, + kAreaBased, + kConstituentBased +}; + ////////////////////////////////////////////// struct lambdajetpolarizationions { @@ -397,11 +423,13 @@ struct lambdajetpolarizationions { 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. - Configurable jetAlgorithm{"jetAlgorithm", 2, "jet clustering algorithm. 0 = kT, 1 = C/A, 2 = Anti-kT"}; - Configurable jetRecombScheme{"jetRecombScheme", 0, "Jet recombination scheme: E_scheme (0), pT-scheme (1), pt2-scheme (2), WTA_pt_scheme (7)"}; // See PWGJE/JetFinders/jetFinder.h for more info. - Configurable bkgSubtraction{"bkgSubtraction", false, "Jet background subtraction: No subtraction (false), Area (true), Constituent (TODO)"}; // Selection bool for background subtraction strategy + 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 jetType{"jetType", 0, "Jet type: Charged Jet (0), Full Jet (1), Photon-tagged (2), Z-tagged (3)"}; // (TODO: create a reasonable track selection for full jets and photon/Z-tagged jet tracks, including detector angular acceptance parameters for EMCal) + // Using an enum for readability: + Configurable jetType{"jetType", kChargedJet, "Jet type: 0: Charged Jet, 1: Full Jet, 2: Photon-tagged, 3: Z-tagged"}; + // (TODO: create a reasonable track selection for full jets and photon/Z-tagged jet tracks, including detector angular acceptance parameters for EMCal) // // Configurables from JE PWG: // // (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!) @@ -496,7 +524,7 @@ struct lambdajetpolarizationions { rctFlagsChecker.init(rctConfigurations.cfgRCTLabel.value, rctConfigurations.cfgCheckZDC, rctConfigurations.cfgTreatLimitedAcceptanceAsBad); // Event Counters - histos.add("hEventSelection", "hEventSelection", kTH1D, {{21, -0.5f, +20.5f}}); + histos.add("hEventSelection", "hEventSelection", kTH1D, {{23, -0.5f, +20.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"); @@ -523,11 +551,15 @@ struct lambdajetpolarizationions { 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"); + // (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, {{21, -0.5f, +20.5f}, {101, 0.0f, 101.0f}}); + histos.add("hEventSelectionVsCentrality", "hEventSelectionVsCentrality", kTH2D, {{23, -0.5f, +20.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"); @@ -554,7 +586,10 @@ struct lambdajetpolarizationions { 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"); + // Centrality: histos.add("Centrality/hCentralityVsNGlobal", "hCentralityVsNGlobal", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisNch}); 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}); @@ -1275,6 +1310,11 @@ struct lambdajetpolarizationions { void processJetsData(SelCollisionsSimple::iterator const& collision, PseudoJetTracks const& tracks, aod::BCsWithTimestamps const& bcs){ // Uses BCsWithTimestamps to get timestamps for rejectTPCsectorBoundary float centrality = -1.0f; // Just a placeholder + // 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 validJetAlreadyFound = false; // Do not fill Event QA more than once + auto bc = bcs.iteratorAt(collision.bcId()); // Got the iteratorAt() idea from O2Physics/PWGUD/Core/UDHelpers.h if (!isEventAccepted(collision, bc, centrality, false)) return; // Uses return instead of continue, as there is no explicit loop here const uint64_t collIdx = collision.globalIndex(); @@ -1306,7 +1346,7 @@ struct lambdajetpolarizationions { // Cluster particles using the anti-kt algorithm fastjet::JetDefinition jetDef(mapFJAlgorithm(jetConfigurations.jetAlgorithm), jetConfigurations.radiusJet, mapFJRecombScheme(jetConfigurations.jetRecombScheme)); // std::vector jets_pt, jets_eta, jets_phi; // Not worth it to store 4-vectors: the tracks assume pion mass hypothesis, so energy and rapidity are not right. - if (jetConfigurations.bkgSubtraction){ + if (jetConfigurations.bkgSubtraction == kAreaBased){ 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 @@ -1351,6 +1391,9 @@ struct lambdajetpolarizationions { histos.fill(HIST("hJetsPerEvent"), selectedJets); if (selectedJets == 0) return; histos.fill(HIST("hEventsWithJet"), 0.5); + // Another version of this counter, which is already integrated in the Event Selection flow: + if (doEventQA && !validJetAlreadyFound) fillEventSelectionQA(lastBinEvSel-1, centrality); // hasRingJet passes + validJetAlreadyFound = true; if (doJetKinematicsQA){ histos.fill(HIST("JetKinematicsQA/hLeadingJetPt"), leadingJetSub.pt()); @@ -1433,6 +1476,9 @@ struct lambdajetpolarizationions { if (jetsInEvent == 0) return; histos.fill(HIST("hEventsWithJet"), 0.5); + // Another version of this counter, which is already integrated in the Event Selection flow: + if (doEventQA && !validJetAlreadyFound) fillEventSelectionQA(lastBinEvSel-1, centrality); // hasRingJet passes + validJetAlreadyFound = true; const auto& leadingJet = jets[0]; for (const auto& jet : jets){ @@ -1524,9 +1570,16 @@ struct lambdajetpolarizationions { } } - // Had to include DauTracks in subscription, even though I don't loop in it, for the indices to resolve, avoiding " Exception while running: Index pointing to Tracks is not bound!" + // Had to include DauTracks in subscription, even though I don't loop in it, for the indices + // to resolve, avoiding " Exception while running: Index pointing to Tracks is not bound!" void processV0sData(SelCollisions::iterator const& collision, V0CandidatesWithTOF const& fullV0s, aod::BCsWithTimestamps const& bcs, DauTracks const& V0DauTracks){ float centrality = getCentrality(collision); + + // 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); @@ -1537,6 +1590,9 @@ struct lambdajetpolarizationions { const uint64_t collIdx = collision.globalIndex(); if (v0Selections.rejectTPCsectorBoundary) initCCDB(bc); // Substituted call from collision to bc for raw data + // Fill event table: + tableCollisions(collIdx, centrality); // (TODO: add InteractionRate info and other useful cuts for later on in the analysis!) + // bool hasValidV0 = false; // Bool to know if event information can be saved. for (auto const& v0 : fullV0s){ V0SelCounter.resetForNewV0(); @@ -1558,6 +1614,8 @@ struct lambdajetpolarizationions { if (doArmenterosQA) histos.fill(HIST("GeneralQA/h2dArmenterosSelected"), v0.alpha(), v0.qtarm()); // cross-check if (isLambda && isAntiLambda) histos.fill(HIST("hAmbiguousLambdaCandidates"), 0); + if (doEventQA) fillEventSelectionQA(lastBinEvSel, centrality); // hasRingV0 passes + // // Extra competing mass rejection of Lambdas // (TODO: test competing mass cuts) // v0.mLambda() @@ -1595,7 +1653,9 @@ struct lambdajetpolarizationions { v0.negativeeta(), v0.negativephi() ); - + if (doEventQA && !validV0AlreadyFound) fillEventSelectionQA(lastBinEvSel, centrality); // hasRingV0 passes + validV0AlreadyFound = true; + if (doV0KinematicQA){ // Cache kinematics once const float v0y = v0.yLambda(); @@ -1742,8 +1802,8 @@ struct lambdajetpolarizationions { // Only fills collision when there is a valid V0 in it: (TODO: could probably do the same for the jets table) // if (hasValidV0){ // LOG(INFO) << "Filling tableCollisions"; - // Current logic now fills tables independently of collision having V0s, for the Jets table to match correctly - tableCollisions(collIdx, centrality); // (TODO: add InteractionRate info and other useful cuts for later on in the analysis!) + // Current logic now fills tables independently of collision having V0s, for the Jets table to match correctly, at the START of the code + // tableCollisions(collIdx, centrality); // } } diff --git a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx index 7e7294ac3a2..570659e8bca 100644 --- a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx @@ -51,6 +51,7 @@ #include #include #include +#include // For perpendicular jet direction QAs using namespace o2; using namespace o2::framework; @@ -150,34 +151,36 @@ constexpr double polPrefactorAntiLambda = 3.0/antiLambdaWeakDecayConstant; // (TODO: add counters for regular TH2Ds about centrality) -// // ====================================================== -// // Ring Observable SQUARED histogram fill list -// // ====================================================== -// #define RING_OBSERVABLE_SQUARED_FILL_LIST(X, FOLDER) \ -// /* 1D observable histograms */ \ -// X(FOLDER "/hRingObservableSquaredDeltaPhi", deltaPhiJet, ringObservableSquared) \ -// X(FOLDER "/hRingObservableSquaredDeltaTheta", deltaThetaJet, ringObservableSquared) \ -// X(FOLDER "/hRingObservableSquaredIntegrated", 0., ringObservableSquared) \ -// /* Lambda pT variation */ \ -// X(FOLDER "/hRingObservableSquaredLambdaPt", v0pt, ringObservableSquared) \ -// /* 2D Lambda correlations */ \ -// X(FOLDER "/h2dRingObservableSquaredDeltaPhiVsLambdaPt", deltaPhiJet, v0pt, ringObservableSquared) \ -// X(FOLDER "/h2dRingObservableSquaredDeltaThetaVsLambdaPt", deltaThetaJet, v0pt, ringObservableSquared) \ -// /* 2D Jet correlations */ \ -// X(FOLDER "/h2dRingObservableSquaredDeltaPhiVsLeadJetPt", deltaPhiJet, leadingJetPt, ringObservableSquared) \ -// X(FOLDER "/h2dRingObservableSquaredDeltaThetaVsLeadJetPt",deltaThetaJet, leadingJetPt, ringObservableSquared) \ -// /* 2D - Mass correlations */ \ -// X(FOLDER "/h2dRingObservableSquaredDeltaPhiVsMass", deltaPhiJet, v0LambdaLikeMass, ringObservableSquared) \ -// X(FOLDER "/h2dRingObservableSquaredDeltaThetaVsMass", deltaThetaJet, v0LambdaLikeMass, ringObservableSquared) \ -// /* 3D - LambdaPt */ \ -// X(FOLDER "/h3dRingObservableSquaredDeltaPhiVsMassVsLambdaPt", deltaPhiJet, v0LambdaLikeMass, v0pt, ringObservableSquared) \ -// X(FOLDER "/h3dRingObservableSquaredDeltaThetaVsMassVsLambdaPt", deltaThetaJet, v0LambdaLikeMass, v0pt, ringObservableSquared) \ -// /* 3D - LeadJetPt*/ \ -// X(FOLDER "/h3dRingObservableSquaredDeltaPhiVsMassVsLeadJetPt", deltaPhiJet, v0LambdaLikeMass, leadingJetPt, ringObservableSquared) \ -// X(FOLDER "/h3dRingObservableSquaredDeltaThetaVsMassVsLeadJetPt", deltaThetaJet, v0LambdaLikeMass, leadingJetPt, ringObservableSquared) \ -// /* 3D: Squared observable vs Mass vs Centrality */ \ -// X(FOLDER "/h3dRingObservableSquaredDeltaPhiVsMassVsCent", deltaPhiJet, v0LambdaLikeMass, centrality, ringObservableSquared) \ -// X(FOLDER "/h3dRingObservableSquaredDeltaThetaVsMassVsCent", deltaThetaJet, v0LambdaLikeMass, centrality, ringObservableSquared) +// ====================================================== +// Ring Observable SQUARED histogram fill list +// ====================================================== +#if 0 // Disabling the whole definition in a cleaner way -- Multiline comments do not work appropriately in this type of macro definition! +#define RING_OBSERVABLE_SQUARED_FILL_LIST(X, FOLDER) \ + /* 1D observable histograms */ \ + X(FOLDER "/hRingObservableSquaredDeltaPhi", deltaPhiJet, ringObservableSquared) \ + X(FOLDER "/hRingObservableSquaredDeltaTheta", deltaThetaJet, ringObservableSquared) \ + X(FOLDER "/hRingObservableSquaredIntegrated", 0., ringObservableSquared) \ + /* Lambda pT variation */ \ + X(FOLDER "/hRingObservableSquaredLambdaPt", v0pt, ringObservableSquared) \ + /* 2D Lambda correlations */ \ + X(FOLDER "/h2dRingObservableSquaredDeltaPhiVsLambdaPt", deltaPhiJet, v0pt, ringObservableSquared) \ + X(FOLDER "/h2dRingObservableSquaredDeltaThetaVsLambdaPt", deltaThetaJet, v0pt, ringObservableSquared) \ + /* 2D Jet correlations */ \ + X(FOLDER "/h2dRingObservableSquaredDeltaPhiVsLeadJetPt", deltaPhiJet, leadingJetPt, ringObservableSquared) \ + X(FOLDER "/h2dRingObservableSquaredDeltaThetaVsLeadJetPt",deltaThetaJet, leadingJetPt, ringObservableSquared) \ + /* 2D - Mass correlations */ \ + X(FOLDER "/h2dRingObservableSquaredDeltaPhiVsMass", deltaPhiJet, v0LambdaLikeMass, ringObservableSquared) \ + X(FOLDER "/h2dRingObservableSquaredDeltaThetaVsMass", deltaThetaJet, v0LambdaLikeMass, ringObservableSquared) \ + /* 3D - LambdaPt */ \ + X(FOLDER "/h3dRingObservableSquaredDeltaPhiVsMassVsLambdaPt", deltaPhiJet, v0LambdaLikeMass, v0pt, ringObservableSquared) \ + X(FOLDER "/h3dRingObservableSquaredDeltaThetaVsMassVsLambdaPt", deltaThetaJet, v0LambdaLikeMass, v0pt, ringObservableSquared) \ + /* 3D - LeadJetPt*/ \ + X(FOLDER "/h3dRingObservableSquaredDeltaPhiVsMassVsLeadJetPt", deltaPhiJet, v0LambdaLikeMass, leadingJetPt, ringObservableSquared) \ + X(FOLDER "/h3dRingObservableSquaredDeltaThetaVsMassVsLeadJetPt", deltaThetaJet, v0LambdaLikeMass, leadingJetPt, ringObservableSquared) \ + /* 3D: Squared observable vs Mass vs Centrality */ \ + X(FOLDER "/h3dRingObservableSquaredDeltaPhiVsMassVsCent", deltaPhiJet, v0LambdaLikeMass, centrality, ringObservableSquared) \ + X(FOLDER "/h3dRingObservableSquaredDeltaThetaVsMassVsCent", deltaThetaJet, v0LambdaLikeMass, centrality, ringObservableSquared) +#endif #define POLARIZATION_PROFILE_FILL_LIST(X, FOLDER) \ /* =============================== */ \ @@ -205,6 +208,18 @@ constexpr double polPrefactorAntiLambda = 3.0/antiLambdaWeakDecayConstant; #define APPLY_HISTO_FILL(NAME, ...) histos.fill(HIST(NAME), __VA_ARGS__); +// // Another macro for the significance histograms expansion: // (Moved into signal extraction post-processing to allow for pipelining) +// #define RING_1DSIGNIFICANCE_LIST(X, FOLDER) \ +// X(FOLDER "/pRingObservableDeltaPhi", FOLDER "/hRingSignificanceDeltaPhi") \ +// X(FOLDER "/pRingObservableDeltaTheta", FOLDER "/hRingSignificanceDeltaTheta") \ +// X(FOLDER "/pRingObservableIntegrated", FOLDER "/hRingSignificanceIntegrated") \ +// X(FOLDER "/pRingObservableLambdaPt", FOLDER "/hRingSignificanceLambdaPt") \ +// X(FOLDER "/pRingObservableMass", FOLDER "/hRingSignificanceMass") + +// #define APPLY_RING_SIGNIFICANCE(PROFILE, HISTO) \ +// fillSignificance(histos.get(HIST(PROFILE)), histos.get(HIST(HISTO))); + + struct lambdajetpolarizationionsderived { // Define histogram registries: @@ -214,6 +229,11 @@ struct lambdajetpolarizationionsderived { Configurable analyseLambda{"analyseLambda", true, "process Lambda-like candidates"}; Configurable analyseAntiLambda{"analyseAntiLambda", false, "process AntiLambda-like candidates"}; Configurable doPPAnalysis{"doPPAnalysis", false, "if in pp, set to true. Default is HI"}; + + // 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 blocks: @@ -226,20 +246,34 @@ struct lambdajetpolarizationionsderived { // Jet axes: ConfigurableAxis axisLeadingParticlePt{"axisLeadingParticlePt",{100, 0.f, 200.f},"Leading particle p_{T} (GeV/c)"}; // Simpler version! - ConfigurableAxis axisJetPt{"axisJetPt",{100, 0.f, 200.f},"Jet p_{t} (GeV)"}; - ConfigurableAxis axisDeltaTheta{"axisDeltaTheta", {100, 0, constants::math::PI}, "#Delta #theta_{jet}"}; - ConfigurableAxis axisDeltaPhi{"axisDeltaPhi", {100, -constants::math::PI, constants::math::PI}, "#Delta #phi_{jet}"}; + ConfigurableAxis axisJetPt{"axisJetPt",{50, 0.f, 200.f},"Jet p_{t} (GeV)"}; + ConfigurableAxis axisDeltaTheta{"axisDeltaTheta", {40, 0, constants::math::PI}, "#Delta #theta_{jet}"}; + ConfigurableAxis axisDeltaPhi{"axisDeltaPhi", {40, -constants::math::PI, constants::math::PI}, "#Delta #phi_{jet}"}; // 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. // Rewrote the axisLambdaMassSigextract to have 5x coarser bins outside the peak region, and 2x coarser bins in the peak region // (this allows for better fits and smaller fluctuations) - ConfigurableAxis axisLambdaMassSigExtract{"axisLambdaMassSigExtract", {VARIABLE_WIDTH, 1.080000, 1.082000, 1.084000, 1.086000, 1.088000, 1.090000, 1.092000, 1.094000, 1.096000, 1.098000, 1.100000, 1.102000, 1.104000, 1.106000, 1.108000, 1.109683, 1.110483, 1.111283, 1.112083, 1.112883, 1.113683, 1.114483, 1.115283, 1.116083, 1.116883, 1.117683, 1.118483, 1.119283, 1.120083, 1.120883, 1.121683, 1.123683, 1.125683, 1.127683, 1.129683, 1.131683, 1.133683, 1.135683, 1.137683, 1.139683, 1.141683, 1.143683, 1.145683, 1.147683, 1.149683, 1.150000}, "Lambda mass in GeV/c"}; + // ConfigurableAxis axisLambdaMassSigExtract{"axisLambdaMassSigExtract", {VARIABLE_WIDTH, 1.080000, 1.082000, 1.084000, 1.086000, 1.088000, 1.090000, 1.092000, 1.094000, 1.096000, 1.098000, 1.100000, 1.102000, 1.104000, 1.106000, 1.108000, 1.109683, 1.110483, 1.111283, 1.112083, 1.112883, 1.113683, 1.114483, 1.115283, 1.116083, 1.116883, 1.117683, 1.118483, 1.119283, 1.120083, 1.120883, 1.121683, 1.123683, 1.125683, 1.127683, 1.129683, 1.131683, 1.133683, 1.135683, 1.137683, 1.139683, 1.141683, 1.143683, 1.145683, 1.147683, 1.149683, 1.150000}, "Lambda mass in GeV/c"}; + // Even coarser axis: + 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 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)"}; - // (TODO: add a lambdaPt axis that is pre-selected only on the 0.5 to 1.5 Pt region) + // (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"}; } axisConfigurations; @@ -433,6 +467,16 @@ struct lambdajetpolarizationionsderived { histos.add((folder + "/p2dPxStarDeltaPhiVsLambdaPt").c_str(), "p2dPxStarDeltaPhiVsLambdaPt;#Delta#varphi_{jet};#it{p}_{T}^{#Lambda};_{x}", kTProfile2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisPtSigExtract}); histos.add((folder + "/p2dPyStarDeltaPhiVsLambdaPt").c_str(), "p2dPyStarDeltaPhiVsLambdaPt;#Delta#varphi_{jet};#it{p}_{T}^{#Lambda};_{y}", kTProfile2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisPtSigExtract}); histos.add((folder + "/p2dPzStarDeltaPhiVsLambdaPt").c_str(), "p2dPzStarDeltaPhiVsLambdaPt;#Delta#varphi_{jet};#it{p}_{T}^{#Lambda};_{z}", kTProfile2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisPtSigExtract}); + + // =============================== + // QA histograms - Useful numbers + // =============================== + // (TODO: implement these!) + // Added to a separate folder for further control (changed the usage of the "folder" string): + histos.add(("QA_Numbers/" + folder + "/hEventsWithV0").c_str(), "hEventsWithV0", kTH1D, {{1,0,1}}); // In the current derived data, all saved events have a V0 + histos.add(("QA_Numbers/" + folder + "/hLambdaCounter").c_str(), "hLambdaCounter", kTH1D, {{1,0,1}}); + histos.add(("QA_Numbers/" + folder + "/hAntiLambdaCounter").c_str(), "hAntiLambdaCounter", kTH1D, {{1,0,1}}); + histos.add(("QA_Numbers/" + folder + "/hValidLeadJets").c_str(), "hValidLeadJets", kTH1D, {{1,0,1}}); }; // Execute local lambda to register histogram families: addRingObservableFamily("Ring"); @@ -441,14 +485,32 @@ struct lambdajetpolarizationionsderived { addRingObservableFamily("JetAndLambdaKinematicCuts"); } - ////////////// Fill Ring Observable histograms: - ///(This block was tranformed into a bunch of #define statements at the top of the code) - ////////////// + // 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: Preslice perColJets = o2::aod::lambdajetpol::collisionId; Preslice perColV0s = o2::aod::lambdajetpol::collisionId; void processPolarizationData(o2::aod::RingCollisions const& collisions, o2::aod::RingJets const& jets, o2::aod::RingLaV0s const& v0s){ + // Custom grouping + // (TODO: test using global index's custom grouping utility as in the sigma0builder) + // std::vector> v0Grouped(collisions.size()); + // for (const auto& v0 : v0s) {v0Grouped[v0.collisionId()].push_back(v0.globalIndex());} + // std::vector> jetsGrouped(collisions.size()); + // for (const auto& jet : jets) {jetsGrouped[jet.collisionId()].push_back(jet.globalIndex());} + // Only really need the leading jet for now: + // -1 means "no jet for this collision" + // std::vector leadingJetIndex(collisions.size(), -1); + // std::vector leadingJetPt(collisions.size(), -1.f); + // for (const auto& jet : jets) { + // int collId = jet.collisionId(); + // float pt = jet.pt(); // or whatever pT accessor you use + // if (pt > leadingJetPt[collId]) { + // leadingJetPt[collId] = pt; + // leadingJetIndex[collId] = jet.globalIndex(); + // } + // } + for (auto const& collision : collisions) { const auto collId = collision.collisionId(); const double centrality = collision.centrality(); @@ -458,6 +520,11 @@ struct lambdajetpolarizationionsderived { auto jetsInColl = jets.sliceBy(perColJets, collId); auto v0sInColl = v0s.sliceBy(perColV0s, collId); + // Alternative custom grouping block: + // int jetIndex = leadingJetIndex[collision.globalIndex()]; + // if (jetIndex < 0) continue; + // auto leadingJet = jets.rawIteratorAt(jetIndex); + // 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 @@ -487,10 +554,31 @@ struct lambdajetpolarizationionsderived { XYZVector leadingJetVec(jetPx, jetPy, jetPz); XYZVector leadingJetUnitVec = leadingJetVec.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); + XYZVector rotatedPerpVec = perpVec * std::cos(randomAngle) + leadingJetUnitVec.Cross(perpVec) * std::sin(randomAngle); + leadingJetUnitVec = rotatedPerpVec; + } + // TODO: add centrality selection procedure and options (one configurable for no centrality separation at all too!) // TODO: add Lambda candidate selection. Think of a statistical method like signal extraction (if possible) for ring polarization // TODO: add calculations with second to leading jet too. // TODO: Add calculations with leading particle + + // Custom grouping alternative: + // for (size_t i = 0; i < v0Grouped[coll.globalIndex()].size(); i++) { + // auto v0 = v0s.rawIteratorAt(v0Grouped[collision.globalIndex()][i]); + // } + for (auto const& v0 : v0sInColl) { const bool isLambda = v0.isLambda(); const bool isAntiLambda = v0.isAntiLambda(); @@ -499,10 +587,10 @@ struct lambdajetpolarizationionsderived { const double v0eta = v0.v0Eta(); const double v0phi = v0.v0Phi(); - double v0LambdaLikeMass; - double protonLikePt; - double protonLikeEta; - double protonLikePhi; + double v0LambdaLikeMass = 0; // Initialized just to catch any stray behavior + double protonLikePt = 0; + double protonLikeEta = 0; + double protonLikePhi = 0; if (isLambda){ if (!analyseLambda) continue; v0LambdaLikeMass = v0.massLambda(); @@ -536,10 +624,11 @@ struct lambdajetpolarizationionsderived { double ringObservable = protonLikeStarUnit3Vec.Dot(cross) / crossProductNorm; // Adding the prefactor related to the CP-violating decay (decay constants have different signs) - ringObservable *= (isLambda) ? polPrefactorLambda : polPrefactorAntiLambda; + if (!forcePolSignQA) ringObservable *= (isLambda) ? polPrefactorLambda : polPrefactorAntiLambda; + else ringObservable *= (isLambda) ? polPrefactorLambda : -1.0*polPrefactorAntiLambda; - // Calculating error bars: - double ringObservableSquared = ringObservable*ringObservable; + // // Calculating error bars: + // double ringObservableSquared = ringObservable*ringObservable; // Angular variables: double deltaPhiJet = wrapToPiFast(v0phi - leadingJetPhi); // Wrapped to [-PI, pi), for convenience @@ -591,6 +680,14 @@ struct lambdajetpolarizationionsderived { } // end v0s loop } // end collisions } + + // // Filling final histograms for QA based on previous 1D TProfiles, after all processing has been done + // void finalize(){ + // RING_1DSIGNIFICANCE_LIST(APPLY_RING_SIGNIFICANCE, "Ring") + // RING_1DSIGNIFICANCE_LIST(APPLY_RING_SIGNIFICANCE, "RingKinematicCuts") + // RING_1DSIGNIFICANCE_LIST(APPLY_RING_SIGNIFICANCE, "JetKinematicCuts") + // RING_1DSIGNIFICANCE_LIST(APPLY_RING_SIGNIFICANCE, "JetAndLambdaKinematicCuts") + // } PROCESS_SWITCH(lambdajetpolarizationionsderived, processPolarizationData, "Process derived data in Run 3 Data", true); }; From 08a03a1040fb23628054cf727feea5f8c0fbd56d Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Fri, 27 Feb 2026 21:08:02 -0300 Subject: [PATCH 29/40] Adding leading particle QA and second-to-leading jet studies. Studying Armenteros Lambdas --- PWGLF/DataModel/lambdaJetPolarizationIons.h | 11 +- .../Strangeness/lambdaJetPolarizationIons.cxx | 67 +++- .../lambdaJetPolarizationIonsDerived.cxx | 379 ++++++++++-------- 3 files changed, 274 insertions(+), 183 deletions(-) diff --git a/PWGLF/DataModel/lambdaJetPolarizationIons.h b/PWGLF/DataModel/lambdaJetPolarizationIons.h index f3ad984067c..012cc6eb314 100644 --- a/PWGLF/DataModel/lambdaJetPolarizationIons.h +++ b/PWGLF/DataModel/lambdaJetPolarizationIons.h @@ -37,6 +37,10 @@ DECLARE_SOA_COLUMN(JetEta, jetEta, float); DECLARE_SOA_COLUMN(JetPhi, jetPhi, float); DECLARE_SOA_COLUMN(JetNConstituents, jetNConstituents, uint64_t); +DECLARE_SOA_COLUMN(LeadParticlePt, leadParticlePt, float); +DECLARE_SOA_COLUMN(LeadParticleEta, leadParticleEta, float); +DECLARE_SOA_COLUMN(LeadParticlePhi, leadParticlePhi, float); + DECLARE_SOA_COLUMN(V0Pt, v0Pt, float); DECLARE_SOA_COLUMN(V0Eta, v0Eta, float); DECLARE_SOA_COLUMN(V0Phi, v0Phi, float); @@ -54,7 +58,6 @@ DECLARE_SOA_COLUMN(NegEta, negEta, float); DECLARE_SOA_COLUMN(NegPhi, negPhi, float); // (TODO: add dynamic columns with jet px, py, pz) -// (TODO: add leading particle as one of the columns!) } // namespace lambdajetpol @@ -65,6 +68,12 @@ DECLARE_SOA_TABLE(RingJets, "AOD", "RINGJETS", // Renamed to follow convention o lambdajetpol::JetPhi, lambdajetpol::JetNConstituents); +DECLARE_SOA_TABLE(RingLeadP, "AOD", "RINGLEADP", // Leading particle table + lambdajetpol::CollisionId, + lambdajetpol::LeadParticlePt, + lambdajetpol::LeadParticleEta, + lambdajetpol::LeadParticlePhi); + DECLARE_SOA_TABLE(RingLaV0s, "AOD", "RINGLAV0S", // Had to write this in a shorter form because the derived data did not accept long names lambdajetpol::CollisionId, lambdajetpol::V0Pt, diff --git a/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx b/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx index f775e79fc91..634e68dcf21 100644 --- a/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx +++ b/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx @@ -175,6 +175,7 @@ struct lambdajetpolarizationions { // } products; Produces tableV0s; Produces tableJets; + Produces tableLeadParticles; Produces tableCollisions; // Define histogram registries: @@ -838,7 +839,11 @@ struct lambdajetpolarizationions { // Check if doing the right thing in AP space please histos.add("GeneralQA/h2dArmenterosAll", "h2dArmenterosAll", kTH2D, {axisConfigurations.axisAPAlpha, axisConfigurations.axisAPQt}); - histos.add("GeneralQA/h2dArmenterosSelected", "h2dArmenterosSelected", 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/h2dArmenterosFullSelectedAmbiguous", "h2dArmenterosFullSelectedAmbiguous", kTH2D, {axisConfigurations.axisAPAlpha, axisConfigurations.axisAPQt}); // Jets histograms: // Histogram that needs to be present even out of QA: @@ -1300,12 +1305,32 @@ struct lambdajetpolarizationions { return true; } - // (TODO: possible function to distinguish ambiguous candidates that pass both the Lambda_hypothesis true (i.e., a Lambda) or false (i.e., an AntiLambda)) - // template - // bool isCandidateLambda(TV0 const& v0){ - // + // 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 + // (This function is only called in about 1-3% of the Lambda-Like V0s which remain ambiguous after all other cuts) + // (TODO: add a histogram that tracks the amount of ambiguous candidates distinguished + // by Armenteros, so that we can reconstruct the full ambiguous candidates number from + // the number of ambiguous even after Armenteros and the total number before the cut) + // int isCandidateArmenterosLambda(const float alpha, const float qt){ + // // Remove K0s band + // if (std::abs(alpha) < v0Selections.armK0AlphaThreshold && qt < v0Selections.armK0QtThreshold) return kIsArmenterosK0; + // // std::abs(alpha) < 0.2 && qt < 0.1 + // if (std::abs(alpha) < v0Selections.armMinAlpha) return kArmenterosAmbiguous; + // // std::abs(alpha) < 0.01f + // // Lambda selection + // if (alpha > 0) return kIsArmenterosLambda; + // else return kIsArmenterosAntiLambda; // } + // TODO: another possible check that could be done (if not implemented already inside mLambda() getters) + // template + // int isCandidateMassLambda(TV0 const& v0) { + // float m1 = v0.mLambda(); // proton=positive + // float m2 = v0.mAntiLambda(); // proton=negative + // float d = std::abs(m1 - mLambdaTrue) - std::abs(m2 - mLambdaTrue); + // if (d < 0.f) return +1; // Lambda + // else return -1; // AntiLambda + // } void processJetsData(SelCollisionsSimple::iterator const& collision, PseudoJetTracks const& tracks, aod::BCsWithTimestamps const& bcs){ // Uses BCsWithTimestamps to get timestamps for rejectTPCsectorBoundary float centrality = -1.0f; // Just a placeholder @@ -1342,6 +1367,12 @@ struct lambdajetpolarizationions { // Reject empty events if (fjParticles.size() < 1) return; + auto const& leadingParticle = fjParticles[leadingParticleIdx]; + tableLeadParticles(collIdx, + leadingParticle.pt(), + leadingParticle.eta(), + leadingParticle.phi()); + // Start jet clusterization: // Cluster particles using the anti-kt algorithm fastjet::JetDefinition jetDef(mapFJAlgorithm(jetConfigurations.jetAlgorithm), jetConfigurations.radiusJet, mapFJRecombScheme(jetConfigurations.jetRecombScheme)); @@ -1435,8 +1466,6 @@ struct lambdajetpolarizationions { histos.fill(HIST("JetKinematicsQA/h2dJetsPerEventvsCosThetaToLead"), selectedJets, cosTheta); } // Leading particle comparisons: - auto const& leadingParticle = fjParticles[leadingParticleIdx]; - histos.fill(HIST("JetVsLeadingParticleQA/hLeadingParticlePt"), leadingParticle.pt()); histos.fill(HIST("JetVsLeadingParticleQA/hLeadingParticleEta"), leadingParticle.eta()); histos.fill(HIST("JetVsLeadingParticleQA/hLeadingParticlePhi"), leadingParticle.phi()); @@ -1538,8 +1567,6 @@ struct lambdajetpolarizationions { histos.fill(HIST("JetKinematicsQA/hLeadingJetPhi"), leadingJet.phi()); // Leading particle comparisons: - auto const& leadingParticle = fjParticles[leadingParticleIdx]; - histos.fill(HIST("JetVsLeadingParticleQA/hLeadingParticlePt"), leadingParticle.pt()); histos.fill(HIST("JetVsLeadingParticleQA/hLeadingParticleEta"), leadingParticle.eta()); histos.fill(HIST("JetVsLeadingParticleQA/hLeadingParticlePhi"), leadingParticle.phi()); @@ -1597,10 +1624,10 @@ struct lambdajetpolarizationions { for (auto const& v0 : fullV0s){ V0SelCounter.resetForNewV0(); 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)) continue; - // fill AP plot for all V0s - if (doArmenterosQA) histos.fill(HIST("GeneralQA/h2dArmenterosAll"), v0.alpha(), v0.qtarm()); + if (doArmenterosQA) histos.fill(HIST("GeneralQA/h2dArmenterosKinematicSelected"), v0.alpha(), v0.qtarm()); // Else, just continue the loop: bool isLambda = false; @@ -1611,8 +1638,22 @@ struct lambdajetpolarizationions { if (!isLambda && !isAntiLambda) continue; // Candidate is not considered to be a Lambda (TODO: expand this to a full if block with QA about rejections) // hasValidV0 = true; - if (doArmenterosQA) histos.fill(HIST("GeneralQA/h2dArmenterosSelected"), v0.alpha(), v0.qtarm()); // cross-check - if (isLambda && isAntiLambda) histos.fill(HIST("hAmbiguousLambdaCandidates"), 0); + if (doArmenterosQA) histos.fill(HIST("GeneralQA/h2dArmenterosFullSelected"), v0.alpha(), v0.qtarm()); // cross-check + if (isLambda && !isAntiLambda) histos.fill(HIST("GeneralQA/h2dArmenterosFullSelectedLambda"), v0.alpha(), v0.qtarm()); + if (!isLambda && isAntiLambda) histos.fill(HIST("GeneralQA/h2dArmenterosFullSelectedAntiLambda"), v0.alpha(), v0.qtarm()); + + // int lambdaIdx = -1; // No need to pass armenteros + if (isLambda && isAntiLambda) { + 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 + + // Armenteros cut is not worth it! From QA histograms, only about 0.05% of ambiguous candidates are in the regions probable to be Lamda/AntiLambdas! + // The statistics gain is not worth it. + // // Third and final check to distinguish between Lambda and AntiLambda ambiguous v0s: + // // (This check is only performed to recycle AMBIGUOUS candidates! Not a hard cut on all candidates!) + // lambdaIdx = isCandidateArmenterosLambda(v0.alpha(), v0.qtarm()); + } + // if (lambdaIdx == kIsArmenterosK0) continue; // Should just skip this step then! if (doEventQA) fillEventSelectionQA(lastBinEvSel, centrality); // hasRingV0 passes diff --git a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx index 570659e8bca..822db0b66f9 100644 --- a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx @@ -68,28 +68,28 @@ constexpr double polPrefactorLambda = 3.0/lambdaWeakDecayConstant; constexpr double polPrefactorAntiLambda = 3.0/antiLambdaWeakDecayConstant; // Helper macro to avoid writing the histogram fills 4 times for about 20 histograms: -#define RING_OBSERVABLE_FILL_LIST(X, FOLDER) \ - /* 1D observable histograms */ \ +#define RING_OBSERVABLE_FILL_LIST(X, FOLDER) \ + /* 1D observable histograms */ \ X(FOLDER "/hRingObservableDeltaPhi", deltaPhiJet, ringObservable) \ X(FOLDER "/hRingObservableDeltaTheta", deltaThetaJet, ringObservable) \ X(FOLDER "/hRingObservableIntegrated", 0., ringObservable) \ - /* Counters */ \ + /* Counters */ \ X(FOLDER "/hDeltaPhi", deltaPhiJet) \ X(FOLDER "/hDeltaTheta", deltaThetaJet) \ X(FOLDER "/hIntegrated", 0.) \ /* Lambda pT variation -- Youpeng's proposal */ \ X(FOLDER "/hRingObservableLambdaPt", v0pt, ringObservable) \ X(FOLDER "/hLambdaPt", v0pt) \ - /* 2D Lambda correlations */ \ + /* 2D Lambda correlations */ \ X(FOLDER "/h2dRingObservableDeltaPhiVsLambdaPt", deltaPhiJet, v0pt, ringObservable) \ X(FOLDER "/h2dRingObservableDeltaThetaVsLambdaPt", deltaThetaJet, v0pt, ringObservable) \ - /* Counters */ \ + /* Counters */ \ X(FOLDER "/h2dDeltaPhiVsLambdaPt", deltaPhiJet, v0pt) \ X(FOLDER "/h2dDeltaThetaVsLambdaPt", deltaThetaJet, v0pt) \ - /* 2D Jet correlations */ \ + /* 2D Jet correlations */ \ X(FOLDER "/h2dRingObservableDeltaPhiVsLeadJetPt", deltaPhiJet, leadingJetPt, ringObservable) \ X(FOLDER "/h2dRingObservableDeltaThetaVsLeadJetPt", deltaThetaJet, leadingJetPt, ringObservable) \ - /* Counters */ \ + /* Counters */ \ X(FOLDER "/h2dDeltaPhiVsLeadJetPt", deltaPhiJet, leadingJetPt) \ X(FOLDER "/h2dDeltaThetaVsLeadJetPt", deltaThetaJet, leadingJetPt) \ /* Additional plots for instant gratification - 1D Profiles */ \ @@ -97,90 +97,76 @@ constexpr double polPrefactorAntiLambda = 3.0/antiLambdaWeakDecayConstant; X(FOLDER "/pRingObservableDeltaTheta", deltaThetaJet, ringObservable) \ X(FOLDER "/pRingObservableIntegrated", 0., ringObservable) \ X(FOLDER "/pRingObservableLambdaPt", v0pt, ringObservable) \ - /* 2D Profiles */ \ + /* 2D Profiles */ \ X(FOLDER "/p2dRingObservableDeltaPhiVsLambdaPt", deltaPhiJet, v0pt, ringObservable) \ X(FOLDER "/p2dRingObservableDeltaThetaVsLambdaPt", deltaThetaJet, v0pt, ringObservable) \ X(FOLDER "/p2dRingObservableDeltaPhiVsLeadJetPt", deltaPhiJet, leadingJetPt, ringObservable) \ X(FOLDER "/p2dRingObservableDeltaThetaVsLeadJetPt", deltaThetaJet, leadingJetPt, ringObservable) \ - /* 1D Mass */ \ - X(FOLDER "/hMass", v0LambdaLikeMass) \ - X(FOLDER "/hRingObservableMass", v0LambdaLikeMass, ringObservable) \ - X(FOLDER "/hMassSigExtract", v0LambdaLikeMass) \ - /* 2D: Observable vs Mass */ \ + /* 1D Mass */ \ + X(FOLDER "/hMass", v0LambdaLikeMass) \ + X(FOLDER "/hRingObservableMass", v0LambdaLikeMass, ringObservable) \ + X(FOLDER "/hMassSigExtract", v0LambdaLikeMass) \ + /* 2D: Observable vs Mass */ \ X(FOLDER "/h2dRingObservableDeltaPhiVsMass", deltaPhiJet, v0LambdaLikeMass, ringObservable) \ X(FOLDER "/h2dRingObservableDeltaThetaVsMass", deltaThetaJet, v0LambdaLikeMass, ringObservable) \ - /* Counters */ \ - X(FOLDER "/h2dDeltaPhiVsMass", deltaPhiJet, v0LambdaLikeMass) \ - X(FOLDER "/h2dDeltaThetaVsMass", deltaThetaJet, v0LambdaLikeMass) \ - /* 3D: Observable vs Mass vs Lambda pT */ \ + /* Counters */ \ + X(FOLDER "/h2dDeltaPhiVsMass", deltaPhiJet, v0LambdaLikeMass) \ + X(FOLDER "/h2dDeltaThetaVsMass", deltaThetaJet, v0LambdaLikeMass) \ + /* 3D: Observable vs Mass vs Lambda pT */ \ X(FOLDER "/h3dRingObservableDeltaPhiVsMassVsLambdaPt", deltaPhiJet, v0LambdaLikeMass, v0pt, ringObservable) \ X(FOLDER "/h3dRingObservableDeltaThetaVsMassVsLambdaPt", deltaThetaJet, v0LambdaLikeMass, v0pt, ringObservable) \ - /* Counters */ \ - X(FOLDER "/h3dDeltaPhiVsMassVsLambdaPt", deltaPhiJet, v0LambdaLikeMass, v0pt) \ - X(FOLDER "/h3dDeltaThetaVsMassVsLambdaPt", deltaThetaJet, v0LambdaLikeMass, v0pt) \ - /* 3D: Observable vs Mass vs Lead Jet pT */ \ + /* Counters */ \ + X(FOLDER "/h3dDeltaPhiVsMassVsLambdaPt", deltaPhiJet, v0LambdaLikeMass, v0pt) \ + X(FOLDER "/h3dDeltaThetaVsMassVsLambdaPt", deltaThetaJet, v0LambdaLikeMass, v0pt) \ + /* 3D: Observable vs Mass vs Lead Jet pT */ \ X(FOLDER "/h3dRingObservableDeltaPhiVsMassVsLeadJetPt", deltaPhiJet, v0LambdaLikeMass, leadingJetPt, ringObservable) \ X(FOLDER "/h3dRingObservableDeltaThetaVsMassVsLeadJetPt", deltaThetaJet, v0LambdaLikeMass, leadingJetPt, ringObservable) \ - /* Counters */ \ + /* Counters */ \ X(FOLDER "/h3dDeltaPhiVsMassVsLeadJetPt", deltaPhiJet, v0LambdaLikeMass, leadingJetPt) \ X(FOLDER "/h3dDeltaThetaVsMassVsLeadJetPt", deltaThetaJet, v0LambdaLikeMass, leadingJetPt) \ /* 2D: Observable vs Mass vs Centrality (projected as 2D Mass vs Cent for integrated observable) */ \ - X(FOLDER "/h2dRingObservableMassVsCent", v0LambdaLikeMass, centrality, ringObservable) \ - /* 3D: Observable vs Mass vs Centrality */ \ + X(FOLDER "/h2dRingObservableMassVsCent", v0LambdaLikeMass, centrality, ringObservable) \ + /* 3D: Observable vs Mass vs Centrality */ \ X(FOLDER "/h3dRingObservableDeltaPhiVsMassVsCent", deltaPhiJet, v0LambdaLikeMass, centrality, ringObservable) \ X(FOLDER "/h3dRingObservableDeltaThetaVsMassVsCent", deltaThetaJet, v0LambdaLikeMass, centrality, ringObservable) \ - /* Counters */ \ - X(FOLDER "/h3dDeltaPhiVsMassVsCent", deltaPhiJet, v0LambdaLikeMass, centrality) \ - X(FOLDER "/h3dDeltaThetaVsMassVsCent", deltaThetaJet, v0LambdaLikeMass, centrality) \ - /* TProfile of Ring vs Mass */ \ - X(FOLDER "/pRingObservableMass", v0LambdaLikeMass, ringObservable) \ - /* 2D Profiles: Angle vs Mass */ \ - X(FOLDER "/p2dRingObservableDeltaPhiVsMass", deltaPhiJet, v0LambdaLikeMass, ringObservable) \ - X(FOLDER "/p2dRingObservableDeltaThetaVsMass", deltaThetaJet, v0LambdaLikeMass, ringObservable) \ - /* 3D Profiles: Angle vs Mass vs Lambda pT */ \ + /* Counters */ \ + X(FOLDER "/h3dDeltaPhiVsMassVsCent", deltaPhiJet, v0LambdaLikeMass, centrality) \ + X(FOLDER "/h3dDeltaThetaVsMassVsCent", deltaThetaJet, v0LambdaLikeMass, centrality) \ + /* TProfile of Ring vs Mass */ \ + X(FOLDER "/pRingObservableMass", v0LambdaLikeMass, ringObservable) \ + /* TProfile of Ring vs Mass -- Leading Particle and 2nd-to-leading jet - QA */ \ + X(FOLDER "/pRingObservableLeadPMass", v0LambdaLikeMass, ringObservableLeadP) \ + X(FOLDER "/pRingObservable2ndJetMass", v0LambdaLikeMass, ringObservable2ndJet) \ + /* 2D Profiles: Angle vs Mass */ \ + X(FOLDER "/p2dRingObservableDeltaPhiVsMass", deltaPhiJet, v0LambdaLikeMass, ringObservable) \ + X(FOLDER "/p2dRingObservableDeltaThetaVsMass", deltaThetaJet, v0LambdaLikeMass, ringObservable) \ + /* 3D Profiles: Angle vs Mass vs Lambda pT */ \ X(FOLDER "/p3dRingObservableDeltaPhiVsMassVsLambdaPt", deltaPhiJet, v0LambdaLikeMass, v0pt, ringObservable) \ X(FOLDER "/p3dRingObservableDeltaThetaVsMassVsLambdaPt", deltaThetaJet, v0LambdaLikeMass, v0pt, ringObservable) \ - /* 3D Profiles: Angle vs Mass vs Lead Jet pT */ \ + /* 3D Profiles: Angle vs Mass vs Lead Jet pT */ \ X(FOLDER "/p3dRingObservableDeltaPhiVsMassVsLeadJetPt", deltaPhiJet, v0LambdaLikeMass, leadingJetPt, ringObservable) \ X(FOLDER "/p3dRingObservableDeltaThetaVsMassVsLeadJetPt", deltaThetaJet, v0LambdaLikeMass, leadingJetPt, ringObservable) \ - /* 2D Profile: Mass vs Centrality */ \ - X(FOLDER "/p2dRingObservableMassVsCent", v0LambdaLikeMass, centrality, ringObservable) \ - /* 3D Profiles: Angle vs Mass vs Centrality */ \ + /* 2D Profile: Mass vs Centrality */ \ + X(FOLDER "/p2dRingObservableMassVsCent", v0LambdaLikeMass, centrality, ringObservable) \ + /* 3D Profiles: Angle vs Mass vs Centrality */ \ X(FOLDER "/p3dRingObservableDeltaPhiVsMassVsCent", deltaPhiJet, v0LambdaLikeMass, centrality, ringObservable) \ X(FOLDER "/p3dRingObservableDeltaThetaVsMassVsCent", deltaThetaJet, v0LambdaLikeMass, centrality, ringObservable) // (TODO: add counters for regular TH2Ds about centrality) +// For leading particle +#define RING_OBSERVABLE_LEADP_FILL_LIST(X, FOLDER) \ + X(FOLDER "/pRingObservableLeadPDeltaPhi", deltaPhiLeadP, ringObservableLeadP) \ + X(FOLDER "/pRingObservableLeadPDeltaTheta", deltaThetaLeadP, ringObservableLeadP) \ + X(FOLDER "/pRingObservableLeadPIntegrated", 0., ringObservableLeadP) \ + X(FOLDER "/pRingObservableLeadPLambdaPt", v0pt, ringObservableLeadP) + +// For subleading jet: +#define RING_OBSERVABLE_2NDJET_FILL_LIST(X, FOLDER) \ + X(FOLDER "/pRingObservable2ndJetDeltaPhi", deltaPhi2ndJet, ringObservable2ndJet) \ + X(FOLDER "/pRingObservable2ndJetDeltaTheta", deltaTheta2ndJet, ringObservable2ndJet) \ + X(FOLDER "/pRingObservable2ndJetIntegrated", 0., ringObservable2ndJet) \ + X(FOLDER "/pRingObservable2ndJetLambdaPt", v0pt, ringObservable2ndJet) -// ====================================================== -// Ring Observable SQUARED histogram fill list -// ====================================================== -#if 0 // Disabling the whole definition in a cleaner way -- Multiline comments do not work appropriately in this type of macro definition! -#define RING_OBSERVABLE_SQUARED_FILL_LIST(X, FOLDER) \ - /* 1D observable histograms */ \ - X(FOLDER "/hRingObservableSquaredDeltaPhi", deltaPhiJet, ringObservableSquared) \ - X(FOLDER "/hRingObservableSquaredDeltaTheta", deltaThetaJet, ringObservableSquared) \ - X(FOLDER "/hRingObservableSquaredIntegrated", 0., ringObservableSquared) \ - /* Lambda pT variation */ \ - X(FOLDER "/hRingObservableSquaredLambdaPt", v0pt, ringObservableSquared) \ - /* 2D Lambda correlations */ \ - X(FOLDER "/h2dRingObservableSquaredDeltaPhiVsLambdaPt", deltaPhiJet, v0pt, ringObservableSquared) \ - X(FOLDER "/h2dRingObservableSquaredDeltaThetaVsLambdaPt", deltaThetaJet, v0pt, ringObservableSquared) \ - /* 2D Jet correlations */ \ - X(FOLDER "/h2dRingObservableSquaredDeltaPhiVsLeadJetPt", deltaPhiJet, leadingJetPt, ringObservableSquared) \ - X(FOLDER "/h2dRingObservableSquaredDeltaThetaVsLeadJetPt",deltaThetaJet, leadingJetPt, ringObservableSquared) \ - /* 2D - Mass correlations */ \ - X(FOLDER "/h2dRingObservableSquaredDeltaPhiVsMass", deltaPhiJet, v0LambdaLikeMass, ringObservableSquared) \ - X(FOLDER "/h2dRingObservableSquaredDeltaThetaVsMass", deltaThetaJet, v0LambdaLikeMass, ringObservableSquared) \ - /* 3D - LambdaPt */ \ - X(FOLDER "/h3dRingObservableSquaredDeltaPhiVsMassVsLambdaPt", deltaPhiJet, v0LambdaLikeMass, v0pt, ringObservableSquared) \ - X(FOLDER "/h3dRingObservableSquaredDeltaThetaVsMassVsLambdaPt", deltaThetaJet, v0LambdaLikeMass, v0pt, ringObservableSquared) \ - /* 3D - LeadJetPt*/ \ - X(FOLDER "/h3dRingObservableSquaredDeltaPhiVsMassVsLeadJetPt", deltaPhiJet, v0LambdaLikeMass, leadingJetPt, ringObservableSquared) \ - X(FOLDER "/h3dRingObservableSquaredDeltaThetaVsMassVsLeadJetPt", deltaThetaJet, v0LambdaLikeMass, leadingJetPt, ringObservableSquared) \ - /* 3D: Squared observable vs Mass vs Centrality */ \ - X(FOLDER "/h3dRingObservableSquaredDeltaPhiVsMassVsCent", deltaPhiJet, v0LambdaLikeMass, centrality, ringObservableSquared) \ - X(FOLDER "/h3dRingObservableSquaredDeltaThetaVsMassVsCent", deltaThetaJet, v0LambdaLikeMass, centrality, ringObservableSquared) -#endif #define POLARIZATION_PROFILE_FILL_LIST(X, FOLDER) \ /* =============================== */ \ @@ -208,18 +194,6 @@ constexpr double polPrefactorAntiLambda = 3.0/antiLambdaWeakDecayConstant; #define APPLY_HISTO_FILL(NAME, ...) histos.fill(HIST(NAME), __VA_ARGS__); -// // Another macro for the significance histograms expansion: // (Moved into signal extraction post-processing to allow for pipelining) -// #define RING_1DSIGNIFICANCE_LIST(X, FOLDER) \ -// X(FOLDER "/pRingObservableDeltaPhi", FOLDER "/hRingSignificanceDeltaPhi") \ -// X(FOLDER "/pRingObservableDeltaTheta", FOLDER "/hRingSignificanceDeltaTheta") \ -// X(FOLDER "/pRingObservableIntegrated", FOLDER "/hRingSignificanceIntegrated") \ -// X(FOLDER "/pRingObservableLambdaPt", FOLDER "/hRingSignificanceLambdaPt") \ -// X(FOLDER "/pRingObservableMass", FOLDER "/hRingSignificanceMass") - -// #define APPLY_RING_SIGNIFICANCE(PROFILE, HISTO) \ -// fillSignificance(histos.get(HIST(PROFILE)), histos.get(HIST(HISTO))); - - struct lambdajetpolarizationionsderived { // Define histogram registries: @@ -253,10 +227,7 @@ struct lambdajetpolarizationionsderived { // 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. - // Rewrote the axisLambdaMassSigextract to have 5x coarser bins outside the peak region, and 2x coarser bins in the peak region - // (this allows for better fits and smaller fluctuations) - // ConfigurableAxis axisLambdaMassSigExtract{"axisLambdaMassSigExtract", {VARIABLE_WIDTH, 1.080000, 1.082000, 1.084000, 1.086000, 1.088000, 1.090000, 1.092000, 1.094000, 1.096000, 1.098000, 1.100000, 1.102000, 1.104000, 1.106000, 1.108000, 1.109683, 1.110483, 1.111283, 1.112083, 1.112883, 1.113683, 1.114483, 1.115283, 1.116083, 1.116883, 1.117683, 1.118483, 1.119283, 1.120083, 1.120883, 1.121683, 1.123683, 1.125683, 1.127683, 1.129683, 1.131683, 1.133683, 1.135683, 1.137683, 1.139683, 1.141683, 1.143683, 1.145683, 1.147683, 1.149683, 1.150000}, "Lambda mass in GeV/c"}; - // Even coarser axis: + // A coarser axis (sigma is still well estimated, with about 8 bins in the peak region) ConfigurableAxis axisLambdaMassSigExtract{ "axisLambdaMassSigExtract", {VARIABLE_WIDTH, // Left sideband (7 bins, 0.004 width) @@ -325,19 +296,7 @@ struct lambdajetpolarizationionsderived { // Counters histos.add((folder + "/h2dDeltaPhiVsLeadJetPt").c_str(), "h2dDeltaPhiVsLeadJetPt", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisJetPt}); histos.add((folder + "/h2dDeltaThetaVsLeadJetPt").c_str(), "h2dDeltaThetaVsLeadJetPt", kTH2D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisJetPt}); - // (TODO: check if all squared observables were actually transformed into a (much better) TProfile version) - // // =============================== - // // Squared observable (error propagation) - // // =============================== - // histos.add((folder + "/hRingObservableSquaredDeltaPhi").c_str(), "hRingObservableSquaredDeltaPhi", kTH1D, {axisConfigurations.axisDeltaPhi}); - // histos.add((folder + "/hRingObservableSquaredDeltaTheta").c_str(), "hRingObservableSquaredDeltaTheta", kTH1D, {axisConfigurations.axisDeltaTheta}); - // histos.add((folder + "/hRingObservableSquaredIntegrated").c_str(), "hRingObservableSquaredIntegrated", kTH1D, {{1, -0.5, 0.5}}); - // histos.add((folder + "/hRingObservableSquaredLambdaPt").c_str(), "hRingObservableSquaredLambdaPt", kTH1D, {axisConfigurations.axisPt}); - // histos.add((folder + "/h2dRingObservableSquaredDeltaPhiVsLambdaPt").c_str(), "h2dRingObservableSquaredDeltaPhiVsLambdaPt", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisPt}); - // histos.add((folder + "/h2dRingObservableSquaredDeltaThetaVsLambdaPt").c_str(), "h2dRingObservableSquaredDeltaThetaVsLambdaPt", kTH2D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisPt}); - // histos.add((folder + "/h2dRingObservableSquaredDeltaPhiVsLeadJetPt").c_str(), "h2dRingObservableSquaredDeltaPhiVsLeadJetPt", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisJetPt}); - // histos.add((folder + "/h2dRingObservableSquaredDeltaThetaVsLeadJetPt").c_str(), "h2dRingObservableSquaredDeltaThetaVsLeadJetPt", kTH2D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisJetPt}); - + // Additional plots for instant gratification: // -- 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 @@ -353,6 +312,17 @@ 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}); + + // 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}); + // 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}); // =============================== // 2D TProfiles (Lambda correlations) // =============================== @@ -379,27 +349,18 @@ struct lambdajetpolarizationionsderived { // Ring observable weighted with R histos.add((folder + "/h2dRingObservableDeltaPhiVsMass").c_str(), "h2dRingObservableDeltaPhiVsMass", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract}); histos.add((folder + "/h2dRingObservableDeltaThetaVsMass").c_str(), "h2dRingObservableDeltaThetaVsMass", kTH2D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract}); - // // Squared observable (for variance propagation) - // histos.add((folder + "/h2dRingObservableSquaredDeltaPhiVsMass").c_str(), "h2dRingObservableSquaredDeltaPhiVsMass", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract}); - // histos.add((folder + "/h2dRingObservableSquaredDeltaThetaVsMass").c_str(), "h2dRingObservableSquaredDeltaThetaVsMass", kTH2D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract}); // --- Counters (denominators) --- histos.add((folder + "/h2dDeltaPhiVsMass").c_str(), "h2dDeltaPhiVsMass", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract}); histos.add((folder + "/h2dDeltaThetaVsMass").c_str(), "h2dDeltaThetaVsMass", kTH2D,{axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract}); // --- 3D: Angular observable vs Mass vs Lambda pT --- histos.add((folder + "/h3dRingObservableDeltaPhiVsMassVsLambdaPt").c_str(), "h3dRingObservableDeltaPhiVsMassVsLambdaPt", kTH3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisPtSigExtract}); histos.add((folder + "/h3dRingObservableDeltaThetaVsMassVsLambdaPt").c_str(), "h3dRingObservableDeltaThetaVsMassVsLambdaPt", kTH3D, {axisConfigurations.axisDeltaTheta,axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisPtSigExtract}); - // // Squared version - // histos.add((folder + "/h3dRingObservableSquaredDeltaPhiVsMassVsLambdaPt").c_str(), "h3dRingObservableSquaredDeltaPhiVsMassVsLambdaPt", kTH3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisPtSigExtract}); - // histos.add((folder + "/h3dRingObservableSquaredDeltaThetaVsMassVsLambdaPt").c_str(), "h3dRingObservableSquaredDeltaThetaVsMassVsLambdaPt", kTH3D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisPtSigExtract}); // Counters histos.add((folder + "/h3dDeltaPhiVsMassVsLambdaPt").c_str(), "h3dDeltaPhiVsMassVsLambdaPt", kTH3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisPtSigExtract}); histos.add((folder + "/h3dDeltaThetaVsMassVsLambdaPt").c_str(), "h3dDeltaThetaVsMassVsLambdaPt", kTH3D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisPtSigExtract}); // --- 3D: Angular observable vs Mass vs Lead Jet pT --- histos.add((folder + "/h3dRingObservableDeltaPhiVsMassVsLeadJetPt").c_str(), "h3dRingObservableDeltaPhiVsMassVsLeadJetPt", kTH3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisJetPtSigExtract}); histos.add((folder + "/h3dRingObservableDeltaThetaVsMassVsLeadJetPt").c_str(), "h3dRingObservableDeltaThetaVsMassVsLeadJetPt", kTH3D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisJetPtSigExtract}); - // // --- Squared version --- - // histos.add((folder + "/h3dRingObservableSquaredDeltaPhiVsMassVsLeadJetPt").c_str(), "h3dRingObservableSquaredDeltaPhiVsMassVsLeadJetPt", kTH3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisJetPtSigExtract}); - // histos.add((folder + "/h3dRingObservableSquaredDeltaThetaVsMassVsLeadJetPt").c_str(), "h3dRingObservableSquaredDeltaThetaVsMassVsLeadJetPt", kTH3D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisJetPtSigExtract}); // --- Counters --- histos.add((folder + "/h3dDeltaPhiVsMassVsLeadJetPt").c_str(), "h3dDeltaPhiVsMassVsLeadJetPt", kTH3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisJetPtSigExtract}); histos.add((folder + "/h3dDeltaThetaVsMassVsLeadJetPt").c_str(), "h3dDeltaThetaVsMassVsLeadJetPt", kTH3D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisJetPtSigExtract}); @@ -409,6 +370,8 @@ struct lambdajetpolarizationionsderived { ///////////////////////////////////// // 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(), "pRingObservableLeadPMass;m_{p#pi};<#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}); // TProfile2D: vs Mass (DeltaTheta) @@ -432,9 +395,6 @@ struct lambdajetpolarizationionsderived { // --- 3D: Angular observable vs Invariant Mass --- histos.add((folder + "/h3dRingObservableDeltaPhiVsMassVsCent").c_str(), "h3dRingObservableDeltaPhiVsMassVsCent", kTH3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisCentrality}); histos.add((folder + "/h3dRingObservableDeltaThetaVsMassVsCent").c_str(), "h3dRingObservableDeltaThetaVsMassVsCent", kTH3D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisCentrality}); - // // Squared observable (for variance propagation) - // histos.add((folder + "/h3dRingObservableSquaredDeltaPhiVsMassVsCent").c_str(), "h3dRingObservableSquaredDeltaPhiVsMassVsCent", kTH3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisCentrality}); - // histos.add((folder + "/h3dRingObservableSquaredDeltaThetaVsMassVsCent").c_str(), "h3dRingObservableSquaredDeltaThetaVsMassVsCent", kTH3D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisCentrality}); // --- Counters (denominators) --- histos.add((folder + "/h3dDeltaPhiVsMassVsCent").c_str(), "h3dDeltaPhiVsMassVsCent", kTH3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisCentrality}); histos.add((folder + "/h3dDeltaThetaVsMassVsCent").c_str(), "h3dDeltaThetaVsMassVsCent", kTH3D,{axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisCentrality}); @@ -472,11 +432,12 @@ struct lambdajetpolarizationionsderived { // QA histograms - Useful numbers // =============================== // (TODO: implement these!) + // (TODO: implement momentum imbalance checks for jets!) // Added to a separate folder for further control (changed the usage of the "folder" string): - histos.add(("QA_Numbers/" + folder + "/hEventsWithV0").c_str(), "hEventsWithV0", kTH1D, {{1,0,1}}); // In the current derived data, all saved events have a V0 - histos.add(("QA_Numbers/" + folder + "/hLambdaCounter").c_str(), "hLambdaCounter", kTH1D, {{1,0,1}}); - histos.add(("QA_Numbers/" + folder + "/hAntiLambdaCounter").c_str(), "hAntiLambdaCounter", kTH1D, {{1,0,1}}); - histos.add(("QA_Numbers/" + folder + "/hValidLeadJets").c_str(), "hValidLeadJets", kTH1D, {{1,0,1}}); + // histos.add(("QA_Numbers/" + folder + "/hEventsWithV0").c_str(), "hEventsWithV0", kTH1D, {{1,0,1}}); // In the current derived data, all saved events have a V0 + // histos.add(("QA_Numbers/" + folder + "/hLambdaCounter").c_str(), "hLambdaCounter", kTH1D, {{1,0,1}}); + // histos.add(("QA_Numbers/" + folder + "/hAntiLambdaCounter").c_str(), "hAntiLambdaCounter", kTH1D, {{1,0,1}}); + // histos.add(("QA_Numbers/" + folder + "/hValidLeadJets").c_str(), "hValidLeadJets", kTH1D, {{1,0,1}}); }; // Execute local lambda to register histogram families: addRingObservableFamily("Ring"); @@ -489,27 +450,12 @@ struct lambdajetpolarizationionsderived { 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::collisionId; Preslice perColV0s = o2::aod::lambdajetpol::collisionId; - void processPolarizationData(o2::aod::RingCollisions const& collisions, o2::aod::RingJets const& jets, o2::aod::RingLaV0s const& v0s){ - // Custom grouping - // (TODO: test using global index's custom grouping utility as in the sigma0builder) - // std::vector> v0Grouped(collisions.size()); - // for (const auto& v0 : v0s) {v0Grouped[v0.collisionId()].push_back(v0.globalIndex());} - // std::vector> jetsGrouped(collisions.size()); - // for (const auto& jet : jets) {jetsGrouped[jet.collisionId()].push_back(jet.globalIndex());} - // Only really need the leading jet for now: - // -1 means "no jet for this collision" - // std::vector leadingJetIndex(collisions.size(), -1); - // std::vector leadingJetPt(collisions.size(), -1.f); - // for (const auto& jet : jets) { - // int collId = jet.collisionId(); - // float pt = jet.pt(); // or whatever pT accessor you use - // if (pt > leadingJetPt[collId]) { - // leadingJetPt[collId] = pt; - // leadingJetIndex[collId] = jet.globalIndex(); - // } - // } + Preslice perColLeadPs = o2::aod::lambdajetpol::collisionId; + void processPolarizationData(o2::aod::RingCollisions const& collisions, o2::aod::RingJets const& jets, o2::aod::RingLaV0s const& v0s, + o2::aod::RingLeadP const& leadPs){ for (auto const& collision : collisions) { const auto collId = collision.collisionId(); @@ -520,39 +466,49 @@ struct lambdajetpolarizationionsderived { auto jetsInColl = jets.sliceBy(perColJets, collId); auto v0sInColl = v0s.sliceBy(perColV0s, collId); - // Alternative custom grouping block: - // int jetIndex = leadingJetIndex[collision.globalIndex()]; - // if (jetIndex < 0) continue; - // auto leadingJet = jets.rawIteratorAt(jetIndex); - // 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!) if (!jetsInColl.size() || !v0sInColl.size()) continue; - - // Get leading jet: - double leadingJetPt = -1; + // (TODO: either add a separate loop for events that have a leading particle, but no leading jet, + // or be aware that there are events saved with a leading particle that don't have a single valid jet in them) + // (by the way, no need to check a leadPsInColl, because if there is a jet in the collision, + // there surely is a leading particle in it) + + // Get leading jet and second to leading jet: + double leadingJetPt = -1.; + double subleadingJetPt = -1.; o2::aod::RingJets::iterator leadingJet; + std::optional subleadingJet; + // std::optional avoids undefined behaviour from a default-constructed iterator: + // (will work if subleadingJet was not found!) 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 handled by std::optional! + // Now update updating the leading jet: leadingJetPt = jetpt; leadingJet = jet; } + else if (jetpt > subleadingJetPt){ // Update subleading only: + subleadingJetPt = jetpt; + subleadingJet = jet; + } } - // Now you can use: + // For leading jet (always exists): const double leadingJetEta = leadingJet.jetEta(); const double leadingJetPhi = leadingJet.jetPhi(); - // Convert to 3-vector components for inner product: const double jetPx = leadingJetPt * std::cos(leadingJetPhi); const double jetPy = leadingJetPt * std::sin(leadingJetPhi); const double jetPz = leadingJetPt * std::sinh(leadingJetEta); - XYZVector leadingJetVec(jetPx, jetPy, jetPz); - XYZVector leadingJetUnitVec = leadingJetVec.Unit(); + // XYZVector leadingJetVec(jetPx, jetPy, jetPz); + XYZVector leadingJetUnitVec = XYZVector(jetPx, jetPy, 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) @@ -569,20 +525,72 @@ struct lambdajetpolarizationionsderived { leadingJetUnitVec = rotatedPerpVec; } - // TODO: add centrality selection procedure and options (one configurable for no centrality separation at all too!) - // TODO: add Lambda candidate selection. Think of a statistical method like signal extraction (if possible) for ring polarization - // TODO: add calculations with second to leading jet too. - // TODO: Add calculations with leading particle + // ----------------------------------------------------------------------- + // Find the leading particle for this collision. + // pT = -1 means "not found". + double leadPPt = -1.; + double leadPEta = 0.; // safe dummy values -- only used when leadPPt > 0 + double leadPPhi = 0.; + // std::optional avoids the unassigned-iterator trap again: + std::optional leadingParticleOpt; + auto leadPsInColl = leadPs.sliceBy(perColLeadPs, collId); + for (auto const& lp : leadPsInColl) { + // Table should contain exactly one entry per collision, + // but we break immediately to be safe: + leadingParticleOpt = lp; + break; + } + // Extract kinematics only if we actually found an entry: + // (Physically, if there is at least one jet there should always be a + // leading particle, but we guard against it anyway) + if (leadingParticleOpt.has_value()) { + leadPPt = leadingParticleOpt->leadParticlePt(); + leadPEta = leadingParticleOpt->leadParticleEta(); + leadPPhi = leadingParticleOpt->leadParticlePhi(); + } + + // Defining some bools to help: + bool hasValidLeadP = leadPPt > 0.; + bool hasValidSubJet = subleadingJetPt > 0.; + + // --- Subleading jet (only valid when subleadingJetPt > 0) --- + // We still build the variables here to keep the V0 loop clean. + // Their values are irrelevant when subleadingJetPt <= 0. + double subleadingJetEta = 0.; + double subleadingJetPhi = 0.; + XYZVector subJetUnitVec(1., 0., 0.); // dummy unit vector: overwritten below + if (hasValidSubJet) { + subleadingJetEta = subleadingJet->jetEta(); + subleadingJetPhi = subleadingJet->jetPhi(); + const double subJetPx = subleadingJetPt * std::cos(subleadingJetPhi); + const double subJetPy = subleadingJetPt * std::sin(subleadingJetPhi); + const double subJetPz = subleadingJetPt * std::sinh(subleadingJetEta); + subJetUnitVec = XYZVector(subJetPx, subJetPy, subJetPz).Unit(); + } + + // --- Leading particle (only valid when leadPPt > 0) --- + XYZVector leadPUnitVec(1., 0., 0.); // dummy: overwritten below + if (hasValidLeadP) { + const double leadPPx = leadPPt * std::cos(leadPPhi); + const double leadPPy = leadPPt * std::sin(leadPPhi); + const double leadPPz = leadPPt * std::sinh(leadPEta); + leadPUnitVec = XYZVector(leadPPx, leadPPy, leadPPz).Unit(); + } - // Custom grouping alternative: - // for (size_t i = 0; i < v0Grouped[coll.globalIndex()].size(); i++) { - // auto v0 = v0s.rawIteratorAt(v0Grouped[collision.globalIndex()][i]); - // } + // Calculating per-event bools only once: + const bool kinematicJetCheck = std::abs(leadingJetEta) < 0.5; + const bool kinematic2ndJetCheck = (subleadingJetPt > 0.) && (std::abs(subleadingJetEta) < 0.5); + const bool kinematicLeadPCheck = (leadPPt > 0.) && (std::abs(leadPEta) < 0.5); + // TODO: add centrality selection procedure and options (one configurable for no centrality separation at all too!) for (auto const& v0 : v0sInColl) { const bool isLambda = v0.isLambda(); const bool isAntiLambda = v0.isAntiLambda(); - if (isLambda && isAntiLambda) continue; // For now, removing the ambiguous candidates from the analysis. Derived data permits handling both. + // 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 + // 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 double v0pt = v0.v0Pt(); const double v0eta = v0.v0Eta(); const double v0phi = v0.v0Phi(); @@ -598,7 +606,7 @@ struct lambdajetpolarizationionsderived { protonLikeEta = v0.posEta(); protonLikePhi = v0.posPhi(); } - else if (isAntiLambda){ // (TODO: add a split histogram where you consider Lambda and AntiLambda polarization separately) + else if (isAntiLambda){ // (TODO: add a split histogram where you consider Lambda and AntiLambda polarization separately?) if (!analyseAntiLambda) continue; v0LambdaLikeMass = v0.massAntiLambda(); protonLikePt = v0.negPt(); @@ -627,13 +635,48 @@ struct lambdajetpolarizationionsderived { if (!forcePolSignQA) ringObservable *= (isLambda) ? polPrefactorLambda : polPrefactorAntiLambda; else ringObservable *= (isLambda) ? polPrefactorLambda : -1.0*polPrefactorAntiLambda; - // // Calculating error bars: - // double ringObservableSquared = ringObservable*ringObservable; - // Angular variables: double deltaPhiJet = wrapToPiFast(v0phi - leadingJetPhi); // Wrapped to [-PI, pi), for convenience double deltaThetaJet = ROOT::Math::VectorUtil::Angle(leadingJetUnitVec, lambdaLike3Vec); // 3D angular separation + ////////////////////////////////////////// + // Ring observable: Subleading jet proxy + ////////////////////////////////////////// + double ringObservable2ndJet = 0.; + double deltaPhi2ndJet = 0.; + double deltaTheta2ndJet = 0.; + if (hasValidSubJet) { + // Cross product + XYZVector cross2ndJet = subJetUnitVec.Cross(lambdaLike3Vec); + double crossProductNorm2ndJet = cross2ndJet.R(); + double ringObservable2ndJet = protonLikeStarUnit3Vec.Dot(cross2ndJet) / crossProductNorm2ndJet; + // Adding prefactor + if (!forcePolSignQA) ringObservable2ndJet *= (isLambda) ? polPrefactorLambda : polPrefactorAntiLambda; + else ringObservable2ndJet *= (isLambda) ? polPrefactorLambda : -1.0 * polPrefactorAntiLambda; + // Angular variables + double deltaPhi2ndJet = wrapToPiFast(v0phi - subleadingJetPhi); + double deltaTheta2ndJet = ROOT::Math::VectorUtil::Angle(subJetUnitVec, lambdaLike3Vec); + } + + //////////////////////////////////////////// + // Ring observable: Leading particle proxy + //////////////////////////////////////////// + double ringObservableLeadP = 0.; + double deltaPhiLeadP = 0.; + double deltaThetaLeadP = 0.; + if (hasValidLeadP) { + // Cross product + XYZVector crossLeadP = leadPUnitVec.Cross(lambdaLike3Vec); + double crossProductNormLeadP = crossLeadP.R(); + double ringObservableLeadP = protonLikeStarUnit3Vec.Dot(crossLeadP) / crossProductNormLeadP; + // Adding prefactor + if (!forcePolSignQA) ringObservableLeadP *= (isLambda) ? polPrefactorLambda : polPrefactorAntiLambda; + else ringObservableLeadP *= (isLambda) ? polPrefactorLambda : -1.0 * polPrefactorAntiLambda; + // Angular variables + double deltaPhiLeadP = wrapToPiFast(v0phi - leadPPhi); + double deltaThetaLeadP = ROOT::Math::VectorUtil::Angle(leadPUnitVec, lambdaLike3Vec); + } + // 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) * double PolStarX, PolStarY, PolStarZ; @@ -652,42 +695,40 @@ struct lambdajetpolarizationionsderived { // Fill ring histograms: (1D, lambda 2D correlations and jet 2D correlations): RING_OBSERVABLE_FILL_LIST(APPLY_HISTO_FILL, "Ring") // Notice the usage of macros! If you change the variable names, this WILL break the code! - // RING_OBSERVABLE_SQUARED_FILL_LIST(APPLY_HISTO_FILL, "Ring") // No, there should NOT be any ";" here! Read the macro definition for an explanation + // No, there should NOT be any ";" here! Read the macro definition for an explanation + if (hasValidLeadP) {RING_OBSERVABLE_LEADP_FILL_LIST(APPLY_HISTO_FILL, "Ring")} + if (hasValidSubJet) {RING_OBSERVABLE_2NDJET_FILL_LIST(APPLY_HISTO_FILL, "Ring")} POLARIZATION_PROFILE_FILL_LIST(APPLY_HISTO_FILL, "Ring") // 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_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts") - // RING_OBSERVABLE_SQUARED_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts") POLARIZATION_PROFILE_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts") + if (hasValidLeadP) {RING_OBSERVABLE_LEADP_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts")} + if (hasValidSubJet) {RING_OBSERVABLE_2NDJET_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts")} } // Extra selection criteria on jet candidates: - const bool kinematicJetCheck = std::abs(leadingJetEta) < 0.5; if (kinematicJetCheck){ // This is redundant for jets with R=0.4, but for jets with R<0.4 the leading jet may be farther in eta. RING_OBSERVABLE_FILL_LIST(APPLY_HISTO_FILL, "JetKinematicCuts") - // RING_OBSERVABLE_SQUARED_FILL_LIST(APPLY_HISTO_FILL, "JetKinematicCuts") POLARIZATION_PROFILE_FILL_LIST(APPLY_HISTO_FILL, "JetKinematicCuts") } // Extra selection criteria on both Lambda and jet candidates: if (kinematicLambdaCheck && kinematicJetCheck){ RING_OBSERVABLE_FILL_LIST(APPLY_HISTO_FILL, "JetAndLambdaKinematicCuts") - // RING_OBSERVABLE_SQUARED_FILL_LIST(APPLY_HISTO_FILL, "JetAndLambdaKinematicCuts") POLARIZATION_PROFILE_FILL_LIST(APPLY_HISTO_FILL, "JetAndLambdaKinematicCuts") } + + // Same variations for the leading particle and for the subleading jet: + if (kinematicLeadPCheck){RING_OBSERVABLE_LEADP_FILL_LIST(APPLY_HISTO_FILL, "JetKinematicCuts")} + if (kinematic2ndJetCheck){RING_OBSERVABLE_2NDJET_FILL_LIST(APPLY_HISTO_FILL, "JetKinematicCuts")} + if (kinematicLambdaCheck && kinematicLeadPCheck){RING_OBSERVABLE_LEADP_FILL_LIST(APPLY_HISTO_FILL, "JetAndLambdaKinematicCuts")} + if (kinematicLambdaCheck && kinematic2ndJetCheck){RING_OBSERVABLE_2NDJET_FILL_LIST(APPLY_HISTO_FILL, "JetAndLambdaKinematicCuts")} } // end v0s loop } // end collisions } - - // // Filling final histograms for QA based on previous 1D TProfiles, after all processing has been done - // void finalize(){ - // RING_1DSIGNIFICANCE_LIST(APPLY_RING_SIGNIFICANCE, "Ring") - // RING_1DSIGNIFICANCE_LIST(APPLY_RING_SIGNIFICANCE, "RingKinematicCuts") - // RING_1DSIGNIFICANCE_LIST(APPLY_RING_SIGNIFICANCE, "JetKinematicCuts") - // RING_1DSIGNIFICANCE_LIST(APPLY_RING_SIGNIFICANCE, "JetAndLambdaKinematicCuts") - // } PROCESS_SWITCH(lambdajetpolarizationionsderived, processPolarizationData, "Process derived data in Run 3 Data", true); }; From 3ba13b4fd0524a96f0dbbb3f7f967094743cdb1d Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Mon, 2 Mar 2026 13:50:49 -0300 Subject: [PATCH 30/40] Initializing some variables for safer code (TableProducer). Adding integrated Ring vs Centrality and a single histogram comparing the 4 kinematic ring cuts on Lambdas and Jets. Fixing shadowed variables in the hasValidSubJet and hasValidLeadP blocks that calculated the ring observable --- .../Strangeness/lambdaJetPolarizationIons.cxx | 7 +- .../lambdaJetPolarizationIonsDerived.cxx | 98 ++++++++++++++----- 2 files changed, 79 insertions(+), 26 deletions(-) diff --git a/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx b/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx index 634e68dcf21..3b2c8d72904 100644 --- a/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx +++ b/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx @@ -1346,7 +1346,7 @@ struct lambdajetpolarizationions { // Loop over reconstructed tracks: std::vector fjParticles; - int leadingParticleIdx; // An index inside fjParticles + 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){ // Require that tracks pass selection criteria @@ -1359,8 +1359,9 @@ struct lambdajetpolarizationions { fjParticles.emplace_back(candidate); // Calculating leading particle - if (track.pt() > leadingParticlePt){ - leadingParticlePt = track.pt(); + float pt = candidate.pt(); + if (pt > leadingParticlePt){ + leadingParticlePt = pt; leadingParticleIdx = fjParticles.size() - 1; } } diff --git a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx index 822db0b66f9..779c872fffe 100644 --- a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx @@ -150,7 +150,8 @@ constexpr double polPrefactorAntiLambda = 3.0/antiLambdaWeakDecayConstant; X(FOLDER "/p2dRingObservableMassVsCent", v0LambdaLikeMass, centrality, ringObservable) \ /* 3D Profiles: Angle vs Mass vs Centrality */ \ X(FOLDER "/p3dRingObservableDeltaPhiVsMassVsCent", deltaPhiJet, v0LambdaLikeMass, centrality, ringObservable) \ - X(FOLDER "/p3dRingObservableDeltaThetaVsMassVsCent", deltaThetaJet, v0LambdaLikeMass, centrality, ringObservable) + X(FOLDER "/p3dRingObservableDeltaThetaVsMassVsCent", deltaThetaJet, v0LambdaLikeMass, centrality, ringObservable) \ + X(FOLDER "/pRingIntVsCentrality", centrality, ringObservable) // (TODO: add counters for regular TH2Ds about centrality) // For leading particle @@ -406,6 +407,8 @@ struct lambdajetpolarizationionsderived { histos.add((folder + "/p3dRingObservableDeltaPhiVsMassVsCent").c_str(), "p3dRingObservableDeltaPhiVsMassVsCent;#Delta#varphi;m_{p#pi};Centrality;<#it{R}>", 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}); + // --- TProfile1D: Integrated vs Centrality: + histos.add((folder + "/pRingIntVsCentrality").c_str(), "pRingIntVsCentrality; Centrality (%);<#it{R}>", kTProfile, {axisConfigurations.axisCentrality}); // =============================== // Polarization observable QAs @@ -444,6 +447,25 @@ struct lambdajetpolarizationionsderived { addRingObservableFamily("RingKinematicCuts"); 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"); + + // 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, "|Jet_{#eta}|<0.5"); + histos.get(HIST("pRingCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(4, "#Lambda + Jet 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, "|Jet_{#eta}|<0.5"); + histos.get(HIST("pRingCutsLeadingP"))->GetXaxis()->SetBinLabel(4, "#Lambda + Jet cuts"); } // Initializing a random number generator for the worker (for perpendicular-to-jet direction QAs): @@ -532,25 +554,25 @@ struct lambdajetpolarizationionsderived { double leadPEta = 0.; // safe dummy values -- only used when leadPPt > 0 double leadPPhi = 0.; // std::optional avoids the unassigned-iterator trap again: - std::optional leadingParticleOpt; + std::optional leadingParticlePtr; auto leadPsInColl = leadPs.sliceBy(perColLeadPs, collId); for (auto const& lp : leadPsInColl) { // Table should contain exactly one entry per collision, // but we break immediately to be safe: - leadingParticleOpt = lp; + leadingParticlePtr = lp; break; } // Extract kinematics only if we actually found an entry: // (Physically, if there is at least one jet there should always be a // leading particle, but we guard against it anyway) - if (leadingParticleOpt.has_value()) { - leadPPt = leadingParticleOpt->leadParticlePt(); - leadPEta = leadingParticleOpt->leadParticleEta(); - leadPPhi = leadingParticleOpt->leadParticlePhi(); + if (leadingParticlePtr.has_value()) { + leadPPt = leadingParticlePtr->leadParticlePt(); + leadPEta = leadingParticlePtr->leadParticleEta(); + leadPPhi = leadingParticlePtr->leadParticlePhi(); } - // Defining some bools to help: - bool hasValidLeadP = leadPPt > 0.; + // Defining some bools to help (useful when no subleading jet was found): + bool hasValidLeadP = leadPPt > 0.; // Should ALWAYS be true. There is no leading jet without a leading particle and we check for leading jets! bool hasValidSubJet = subleadingJetPt > 0.; // --- Subleading jet (only valid when subleadingJetPt > 0) --- @@ -649,13 +671,13 @@ struct lambdajetpolarizationionsderived { // Cross product XYZVector cross2ndJet = subJetUnitVec.Cross(lambdaLike3Vec); double crossProductNorm2ndJet = cross2ndJet.R(); - double ringObservable2ndJet = protonLikeStarUnit3Vec.Dot(cross2ndJet) / crossProductNorm2ndJet; + ringObservable2ndJet = protonLikeStarUnit3Vec.Dot(cross2ndJet) / crossProductNorm2ndJet; // Adding prefactor if (!forcePolSignQA) ringObservable2ndJet *= (isLambda) ? polPrefactorLambda : polPrefactorAntiLambda; else ringObservable2ndJet *= (isLambda) ? polPrefactorLambda : -1.0 * polPrefactorAntiLambda; // Angular variables - double deltaPhi2ndJet = wrapToPiFast(v0phi - subleadingJetPhi); - double deltaTheta2ndJet = ROOT::Math::VectorUtil::Angle(subJetUnitVec, lambdaLike3Vec); + deltaPhi2ndJet = wrapToPiFast(v0phi - subleadingJetPhi); + deltaTheta2ndJet = ROOT::Math::VectorUtil::Angle(subJetUnitVec, lambdaLike3Vec); } //////////////////////////////////////////// @@ -668,13 +690,13 @@ struct lambdajetpolarizationionsderived { // Cross product XYZVector crossLeadP = leadPUnitVec.Cross(lambdaLike3Vec); double crossProductNormLeadP = crossLeadP.R(); - double ringObservableLeadP = protonLikeStarUnit3Vec.Dot(crossLeadP) / crossProductNormLeadP; + ringObservableLeadP = protonLikeStarUnit3Vec.Dot(crossLeadP) / crossProductNormLeadP; // Adding prefactor if (!forcePolSignQA) ringObservableLeadP *= (isLambda) ? polPrefactorLambda : polPrefactorAntiLambda; else ringObservableLeadP *= (isLambda) ? polPrefactorLambda : -1.0 * polPrefactorAntiLambda; // Angular variables - double deltaPhiLeadP = wrapToPiFast(v0phi - leadPPhi); - double deltaThetaLeadP = ROOT::Math::VectorUtil::Angle(leadPUnitVec, lambdaLike3Vec); + deltaPhiLeadP = wrapToPiFast(v0phi - leadPPhi); + deltaThetaLeadP = ROOT::Math::VectorUtil::Angle(leadPUnitVec, lambdaLike3Vec); } // Calculating polarization observables (in the Lambda frame, because that is easier -- does not require boosts): @@ -696,36 +718,66 @@ struct lambdajetpolarizationionsderived { // Fill ring histograms: (1D, lambda 2D correlations and jet 2D correlations): RING_OBSERVABLE_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 (hasValidLeadP) {RING_OBSERVABLE_LEADP_FILL_LIST(APPLY_HISTO_FILL, "Ring")} - if (hasValidSubJet) {RING_OBSERVABLE_2NDJET_FILL_LIST(APPLY_HISTO_FILL, "Ring")} + histos.fill(HIST("pRingCuts"), 0, ringObservable); // First bin of comparison + + // Checks with other jet proxies: + if (hasValidLeadP) { + RING_OBSERVABLE_LEADP_FILL_LIST(APPLY_HISTO_FILL, "Ring") + histos.fill(HIST("pRingCutsLeadingP"), 0, ringObservableLeadP); + } + if (hasValidSubJet) { + RING_OBSERVABLE_2NDJET_FILL_LIST(APPLY_HISTO_FILL, "Ring") + histos.fill(HIST("pRingCutsSubLeadingJet"), 0, ringObservable2ndJet); + } POLARIZATION_PROFILE_FILL_LIST(APPLY_HISTO_FILL, "Ring") // 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_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts") + histos.fill(HIST("pRingCuts"), 1, ringObservable); POLARIZATION_PROFILE_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts") - if (hasValidLeadP) {RING_OBSERVABLE_LEADP_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts")} - if (hasValidSubJet) {RING_OBSERVABLE_2NDJET_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts")} + if (hasValidLeadP) { + RING_OBSERVABLE_LEADP_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts") + histos.fill(HIST("pRingCutsLeadingP"), 1, ringObservableLeadP); + } + if (hasValidSubJet) { + RING_OBSERVABLE_2NDJET_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts") + histos.fill(HIST("pRingCutsSubLeadingJet"), 1, ringObservable2ndJet); + } } // Extra selection criteria on jet candidates: if (kinematicJetCheck){ // This is redundant for jets with R=0.4, but for jets with R<0.4 the leading jet may be farther in eta. RING_OBSERVABLE_FILL_LIST(APPLY_HISTO_FILL, "JetKinematicCuts") + histos.fill(HIST("pRingCuts"), 2, ringObservable); POLARIZATION_PROFILE_FILL_LIST(APPLY_HISTO_FILL, "JetKinematicCuts") } // 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); POLARIZATION_PROFILE_FILL_LIST(APPLY_HISTO_FILL, "JetAndLambdaKinematicCuts") } // Same variations for the leading particle and for the subleading jet: - if (kinematicLeadPCheck){RING_OBSERVABLE_LEADP_FILL_LIST(APPLY_HISTO_FILL, "JetKinematicCuts")} - if (kinematic2ndJetCheck){RING_OBSERVABLE_2NDJET_FILL_LIST(APPLY_HISTO_FILL, "JetKinematicCuts")} - if (kinematicLambdaCheck && kinematicLeadPCheck){RING_OBSERVABLE_LEADP_FILL_LIST(APPLY_HISTO_FILL, "JetAndLambdaKinematicCuts")} - if (kinematicLambdaCheck && kinematic2ndJetCheck){RING_OBSERVABLE_2NDJET_FILL_LIST(APPLY_HISTO_FILL, "JetAndLambdaKinematicCuts")} + if (kinematicLeadPCheck){ + RING_OBSERVABLE_LEADP_FILL_LIST(APPLY_HISTO_FILL, "JetKinematicCuts") + histos.fill(HIST("pRingCutsLeadingP"), 2, ringObservableLeadP); + } + if (kinematic2ndJetCheck){ + RING_OBSERVABLE_2NDJET_FILL_LIST(APPLY_HISTO_FILL, "JetKinematicCuts") + histos.fill(HIST("pRingCutsSubLeadingJet"), 2, ringObservable2ndJet); + } + if (kinematicLambdaCheck && kinematicLeadPCheck){ + RING_OBSERVABLE_LEADP_FILL_LIST(APPLY_HISTO_FILL, "JetAndLambdaKinematicCuts") + histos.fill(HIST("pRingCutsLeadingP"), 3, ringObservableLeadP); + } + if (kinematicLambdaCheck && kinematic2ndJetCheck){ + RING_OBSERVABLE_2NDJET_FILL_LIST(APPLY_HISTO_FILL, "JetAndLambdaKinematicCuts") + histos.fill(HIST("pRingCutsSubLeadingJet"), 3, ringObservable2ndJet); + } } // end v0s loop } // end collisions } From 171f8ac9c120652c01e97db42d0991729d3cfe87 Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Mon, 2 Mar 2026 17:10:03 -0300 Subject: [PATCH 31/40] Adding dynamic columns for readability. Adding more centrality information in derived data. Removing useless histograms from derived data consumer output and cleaning folder structure --- PWGLF/DataModel/lambdaJetPolarizationIons.h | 48 +- .../Strangeness/lambdaJetPolarizationIons.cxx | 68 +- .../lambdaJetPolarizationIonsDerived.cxx | 633 ++++++++---------- 3 files changed, 355 insertions(+), 394 deletions(-) diff --git a/PWGLF/DataModel/lambdaJetPolarizationIons.h b/PWGLF/DataModel/lambdaJetPolarizationIons.h index 012cc6eb314..3487b75f90d 100644 --- a/PWGLF/DataModel/lambdaJetPolarizationIons.h +++ b/PWGLF/DataModel/lambdaJetPolarizationIons.h @@ -30,7 +30,12 @@ namespace lambdajetpol // DECLARE_SOA_COLUMN(CollIdx, collIdx, uint64_t); // Using a regular SOA column instead of an index column for convenience DECLARE_SOA_INDEX_COLUMN(Collision, collision); -DECLARE_SOA_COLUMN(Centrality, centrality, float); +DECLARE_SOA_COLUMN(CentFT0M, centFT0M, float); +DECLARE_SOA_COLUMN(CentFT0C, centFT0C, float); +DECLARE_SOA_COLUMN(CentFT0CVariant1, centFT0CVariant1, float); +DECLARE_SOA_COLUMN(CentMFT, centMFT, float); +DECLARE_SOA_COLUMN(CentNGlobal, centNGlobal, float); +DECLARE_SOA_COLUMN(CentFV0A, centFV0A, float); DECLARE_SOA_COLUMN(JetPt, jetPt, float); DECLARE_SOA_COLUMN(JetEta, jetEta, float); @@ -57,7 +62,20 @@ DECLARE_SOA_COLUMN(NegPt, negPt, float); DECLARE_SOA_COLUMN(NegEta, negEta, float); DECLARE_SOA_COLUMN(NegPhi, negPhi, float); -// (TODO: add dynamic columns with jet px, py, pz) +// Dynamic columns for jets (Px,Py,Pz): +DECLARE_SOA_DYNAMIC_COLUMN(JetPx, jetPx, //! Jet px + [](float jetPt, float jetPhi) -> float {return jetPt * std::cos(jetPhi);}); +DECLARE_SOA_DYNAMIC_COLUMN(JetPy, jetPy, //! Jet py + [](float jetPt, float jetPhi) -> float {return jetPt * std::sin(jetPhi);}); +DECLARE_SOA_DYNAMIC_COLUMN(JetPz, jetPz, //! Jet pz + [](float jetPt, float jetEta) -> float {return jetPt * std::sinh(jetEta);}); +// Same for leading particles: +DECLARE_SOA_DYNAMIC_COLUMN(LeadParticlePx, leadParticlePx, //! Leading particle px + [](float leadParticlePt, float leadParticlePhi) -> float {return leadParticlePt * std::cos(leadParticlePhi);}); +DECLARE_SOA_DYNAMIC_COLUMN(LeadParticlePy, leadParticlePy, //! Leading particle py + [](float leadParticlePt, float leadParticlePhi) -> float {return leadParticlePt * std::sin(leadParticlePhi);}); +DECLARE_SOA_DYNAMIC_COLUMN(LeadParticlePz, leadParticlePz, //! Leading particle pz + [](float leadParticlePt, float leadParticleEta) -> float {return leadParticlePt * std::sinh(leadParticleEta);}); } // namespace lambdajetpol @@ -66,13 +84,23 @@ DECLARE_SOA_TABLE(RingJets, "AOD", "RINGJETS", // Renamed to follow convention o lambdajetpol::JetPt, lambdajetpol::JetEta, lambdajetpol::JetPhi, - lambdajetpol::JetNConstituents); + lambdajetpol::JetNConstituents, + // Dynamic columns + lambdajetpol::JetPx, // Explicitly binding to static columns + lambdajetpol::JetPy, + lambdajetpol::JetPz + ); DECLARE_SOA_TABLE(RingLeadP, "AOD", "RINGLEADP", // Leading particle table lambdajetpol::CollisionId, lambdajetpol::LeadParticlePt, lambdajetpol::LeadParticleEta, - lambdajetpol::LeadParticlePhi); + lambdajetpol::LeadParticlePhi, + // Dynamic columns + lambdajetpol::LeadParticlePx, + lambdajetpol::LeadParticlePy, + lambdajetpol::LeadParticlePz + ); DECLARE_SOA_TABLE(RingLaV0s, "AOD", "RINGLAV0S", // Had to write this in a shorter form because the derived data did not accept long names lambdajetpol::CollisionId, @@ -88,11 +116,19 @@ DECLARE_SOA_TABLE(RingLaV0s, "AOD", "RINGLAV0S", // Had to write this in a short lambdajetpol::PosPhi, lambdajetpol::NegPt, lambdajetpol::NegEta, - lambdajetpol::NegPhi); + lambdajetpol::NegPhi + ); DECLARE_SOA_TABLE(RingCollisions, "AOD", "RINGCOLLISIONS", lambdajetpol::CollisionId, - lambdajetpol::Centrality); + lambdajetpol::CentFT0M, + lambdajetpol::CentFT0C, + lambdajetpol::CentFT0CVariant1, + lambdajetpol::CentMFT, + lambdajetpol::CentNGlobal, + lambdajetpol::CentFV0A + ); + } // namespace o2::aod #endif // PWGLF_DATAMODEL_LAMBDAJETPOL_H_ \ No newline at end of file diff --git a/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx b/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx index 3b2c8d72904..8ec479144e9 100644 --- a/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx +++ b/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx @@ -190,7 +190,9 @@ struct lambdajetpolarizationions { 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 centralityEstimator{"centralityEstimator", kCentFT0M, "Run 3 centrality estimator (0:CentFT0C, 1:CentFT0M, 2:CentFT0CVariant1, 3:CentMFT, 4:CentNGlobal, 5:CentFV0A)"}; // Default is FT0M + Configurable centralityEstimatorForQA{"centralityEstimatorForQA", kCentFT0M, "Run 3 centrality estimator (0:CentFT0C, 1:CentFT0M, 2:CentFT0CVariant1, 3:CentMFT, 4:CentNGlobal, 5: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"}; @@ -417,7 +419,7 @@ struct lambdajetpolarizationions { } axisConfigurations; // Jet selection configuration: - // (TODO: Add a configurable to select charged jets, neutral jets, full jets, photon-tagged jets and so on) + // (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! @@ -429,27 +431,17 @@ struct lambdajetpolarizationions { 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 // Using an enum for readability: - Configurable jetType{"jetType", kChargedJet, "Jet type: 0: Charged Jet, 1: Full Jet, 2: Photon-tagged, 3: Z-tagged"}; - // (TODO: create a reasonable track selection for full jets and photon/Z-tagged jet tracks, including detector angular acceptance parameters for EMCal) + 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!) + // (TODO: Check which of these configurables might be useful for the photon-tagged and regular analyses) // // Configurables from JE PWG: - // // (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!) // Configurable jetEWSPtMin{"jetEWSPtMin", 0.0, "minimum event-wise subtracted jet pT"}; // Configurable jetEWSPtMax{"jetEWSPtMax", 1000.0, "maximum event-wise subtracted jet pT"}; // Configurable jetGhostArea{"jetGhostArea", 0.005, "jet ghost area"}; // Configurable ghostRepeat{"ghostRepeat", 0, "set to 0 to gain speed if you dont need area calculation"}; // Configurable DoTriggering{"DoTriggering", false, "used for the charged jet trigger to remove the eta constraint on the jet axis"}; // Configurable jetAreaFractionMin{"jetAreaFractionMin", -99.0, "used to make a cut on the jet areas"}; - - // (TODO: Check which of these configurables might be useful for the photon-tagged and regular analyses) - // // event level configurables - // Configurable trackOccupancyInTimeRangeMax{"trackOccupancyInTimeRangeMax", 999999, "maximum occupancy of tracks in neighbouring collisions in a given time range"}; - // Configurable triggerMasks{"triggerMasks", "", "possible JE Trigger masks: fJetChLowPt,fJetChHighPt,fTrackLowPt,fTrackHighPt,fJetD0ChLowPt,fJetD0ChHighPt,fJetLcChLowPt,fJetLcChHighPt,fEMCALReadout,fJetFullHighPt,fJetFullLowPt,fJetNeutralHighPt,fJetNeutralLowPt,fGammaVeryHighPtEMCAL,fGammaVeryHighPtDCAL,fGammaHighPtEMCAL,fGammaHighPtDCAL,fGammaLowPtEMCAL,fGammaLowPtDCAL,fGammaVeryLowPtEMCAL,fGammaVeryLowPtDCAL"}; - // Configurable skipMBGapEvents{"skipMBGapEvents", true, "decide to run over MB gap events or not"}; - // Configurable applyRCTSelections{"applyRCTSelections", true, "decide to apply RCT selections"}; - // // track level configurables - // Configurable trackSelections{"trackSelections", "globalTracks", "set track selections"}; - // Configurable particleSelections{"particleSelections", "PhysicalPrimary", "set particle selections"}; // // cluster level configurables // Configurable clusterDefinitionS{"clusterDefinition", "kV3Default", "cluster definition to be selected, e.g. V3Default"}; // Configurable clusterEtaMin{"clusterEtaMin", -0.71, "minimum cluster eta"}; // For ECMAL: |eta| < 0.7, phi = 1.40 - 3.26 @@ -515,7 +507,7 @@ struct lambdajetpolarizationions { JetBkgSubUtils backgroundSub; - void init(InitContext const&){ // (TODO: add all useful histograms here! Add flags for QA plots and the such too) + void init(InitContext const&){ // setting CCDB service ccdb->setURL(ccdbConfigurations.ccdbUrl); ccdb->setCaching(true); @@ -918,12 +910,12 @@ struct lambdajetpolarizationions { template auto getCentrality(TCollision const& collision) { - if (centralityEstimator == kCentFT0M) return collision.centFT0M(); - else if (centralityEstimator == kCentFT0C) return collision.centFT0C(); - else if (centralityEstimator == kCentFT0CVariant1) return collision.centFT0CVariant1(); - else if (centralityEstimator == kCentMFT) return collision.centMFT(); - else if (centralityEstimator == kCentNGlobal) return collision.centNGlobal(); - else if (centralityEstimator == kCentFV0A) return collision.centFV0A(); + if (centralityEstimatorForQA == kCentFT0M) return collision.centFT0M(); + else if (centralityEstimatorForQA == kCentFT0C) return collision.centFT0C(); + else if (centralityEstimatorForQA == kCentFT0CVariant1) return collision.centFT0CVariant1(); + else if (centralityEstimatorForQA == kCentMFT) return collision.centMFT(); + else if (centralityEstimatorForQA == kCentNGlobal) return collision.centNGlobal(); + else if (centralityEstimatorForQA == kCentFV0A) return collision.centFV0A(); return -1.f; } @@ -1034,7 +1026,7 @@ struct lambdajetpolarizationions { ///////////////////////////////////////////// // Helper functions for event and candidate selection: - template // (TODO: add fillHists and doEventQA capabilities from derivedlambdakzeroanalysis) + template bool isEventAccepted(TCollision const& collision, TBC const& bc, float centrality, 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()) return false; @@ -1148,7 +1140,7 @@ struct lambdajetpolarizationions { // Lambda selections: template - bool passesGenericV0Cuts(TV0 const& v0){ // (TODO: cache the posTrackExtra and neg track objects outside the V0cuts and hypothesis dependent cuts and then pass them by reference? May be quicker!) + bool passesGenericV0Cuts(TV0 const& v0){ // Base topological variables (high rejection, low cost checks) if (v0.v0radius() < v0Selections.v0radius) return false; V0SelCounter.fill(); @@ -1237,7 +1229,6 @@ struct lambdajetpolarizationions { return true; } - // (TODO: implement Armenteros cut in later function to distinguish between Lambda or antiLambda) // Tests the hypothesis of the V0 being a Lambda or of it being an antiLambda. template bool passesLambdaLambdaBarHypothesis(TV0 const& v0, TCollision const& collision, bool Lambda_hypothesis){ @@ -1308,9 +1299,6 @@ struct lambdajetpolarizationions { // 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 // (This function is only called in about 1-3% of the Lambda-Like V0s which remain ambiguous after all other cuts) - // (TODO: add a histogram that tracks the amount of ambiguous candidates distinguished - // by Armenteros, so that we can reconstruct the full ambiguous candidates number from - // the number of ambiguous even after Armenteros and the total number before the cut) // int isCandidateArmenterosLambda(const float alpha, const float qt){ // // Remove K0s band // if (std::abs(alpha) < v0Selections.armK0AlphaThreshold && qt < v0Selections.armK0QtThreshold) return kIsArmenterosK0; @@ -1601,7 +1589,7 @@ struct lambdajetpolarizationions { // Had to include DauTracks in subscription, even though I don't loop in it, for the indices // to resolve, avoiding " Exception while running: Index pointing to Tracks is not bound!" void processV0sData(SelCollisions::iterator const& collision, V0CandidatesWithTOF const& fullV0s, aod::BCsWithTimestamps const& bcs, DauTracks const& V0DauTracks){ - float centrality = getCentrality(collision); + 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) @@ -1619,9 +1607,15 @@ struct lambdajetpolarizationions { if (v0Selections.rejectTPCsectorBoundary) initCCDB(bc); // Substituted call from collision to bc for raw data // Fill event table: - tableCollisions(collIdx, centrality); // (TODO: add InteractionRate info and other useful cuts for later on in the analysis!) + tableCollisions(collIdx, + collision.centFT0M(), + collision.centFT0C(), + collision.centFT0CVariant1(), + collision.centMFT(), + collision.centNGlobal(), + collision.centFV0A() + ); // (TODO: add InteractionRate info and other useful cuts for later on in the analysis?) - // bool hasValidV0 = false; // Bool to know if event information can be saved. for (auto const& v0 : fullV0s){ V0SelCounter.resetForNewV0(); V0SelCounter.fill(); // Fill for all v0 candidates @@ -1636,8 +1630,7 @@ struct lambdajetpolarizationions { if (analyseLambda) isLambda = passesLambdaLambdaBarHypothesis(v0, collision, true); if (analyseAntiLambda) isAntiLambda = passesLambdaLambdaBarHypothesis(v0, collision, false); - if (!isLambda && !isAntiLambda) continue; // Candidate is not considered to be a Lambda (TODO: expand this to a full if block with QA about rejections) - // hasValidV0 = true; + if (!isLambda && !isAntiLambda) continue; // Candidate is not considered to be a Lambda if (doArmenterosQA) histos.fill(HIST("GeneralQA/h2dArmenterosFullSelected"), v0.alpha(), v0.qtarm()); // cross-check if (isLambda && !isAntiLambda) histos.fill(HIST("GeneralQA/h2dArmenterosFullSelectedLambda"), v0.alpha(), v0.qtarm()); @@ -1792,8 +1785,7 @@ struct lambdajetpolarizationions { } } if (isAntiLambda && analyseAntiLambda) { - // histos.add("h2dNbrOfAntiLambdaVsCentrality", "h2dNbrOfAntiLambdaVsCentrality", kTH2D, {axisConfigurations.axisCentrality, {10, -0.5f, 9.5f}}); - // histos.fill(HIST("h2dNbrOfAntiLambdaVsCentrality"), centrality, v0pt, v0.mAntiLambda()); // (TODO: add the proper call to this fill) + // histos.fill(HIST("h2dNbrOfAntiLambdaVsCentrality"), centrality, NbrAntiLambda); // (TODO: add the proper call to this fill) histos.fill(HIST("h3dMassAntiLambda"), centrality, v0pt, v0.mAntiLambda()); histos.fill(HIST("hMassAntiLambda"), v0.mAntiLambda()); histos.fill(HIST("AntiLambda/hPosDCAToPV"), v0.dcapostopv()); @@ -1841,12 +1833,6 @@ struct lambdajetpolarizationions { } } // end CompleteTopoQA } // end V0s loop - // Only fills collision when there is a valid V0 in it: (TODO: could probably do the same for the jets table) - // if (hasValidV0){ - // LOG(INFO) << "Filling tableCollisions"; - // Current logic now fills tables independently of collision having V0s, for the Jets table to match correctly, at the START of the code - // tableCollisions(collIdx, centrality); - // } } PROCESS_SWITCH(lambdajetpolarizationions, processJetsData, "Process jets and produce derived data in Run 3 Data", true); diff --git a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx index 779c872fffe..9eff95bed89 100644 --- a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx @@ -67,91 +67,69 @@ constexpr double antiLambdaWeakDecayConstant = -0.758; // DPG 2025 update constexpr double polPrefactorLambda = 3.0/lambdaWeakDecayConstant; constexpr double polPrefactorAntiLambda = 3.0/antiLambdaWeakDecayConstant; +enum CentEstimator { + kCentFT0C = 0, + kCentFT0M, + kCentFT0CVariant1, + kCentMFT, + kCentNGlobal, + kCentFV0A +}; + // Helper macro to avoid writing the histogram fills 4 times for about 20 histograms: -#define RING_OBSERVABLE_FILL_LIST(X, FOLDER) \ - /* 1D observable histograms */ \ - X(FOLDER "/hRingObservableDeltaPhi", deltaPhiJet, ringObservable) \ - X(FOLDER "/hRingObservableDeltaTheta", deltaThetaJet, ringObservable) \ - X(FOLDER "/hRingObservableIntegrated", 0., ringObservable) \ - /* Counters */ \ - X(FOLDER "/hDeltaPhi", deltaPhiJet) \ - X(FOLDER "/hDeltaTheta", deltaThetaJet) \ - X(FOLDER "/hIntegrated", 0.) \ - /* Lambda pT variation -- Youpeng's proposal */ \ - X(FOLDER "/hRingObservableLambdaPt", v0pt, ringObservable) \ - X(FOLDER "/hLambdaPt", v0pt) \ - /* 2D Lambda correlations */ \ - X(FOLDER "/h2dRingObservableDeltaPhiVsLambdaPt", deltaPhiJet, v0pt, ringObservable) \ - X(FOLDER "/h2dRingObservableDeltaThetaVsLambdaPt", deltaThetaJet, v0pt, ringObservable) \ - /* Counters */ \ - X(FOLDER "/h2dDeltaPhiVsLambdaPt", deltaPhiJet, v0pt) \ - X(FOLDER "/h2dDeltaThetaVsLambdaPt", deltaThetaJet, v0pt) \ - /* 2D Jet correlations */ \ - X(FOLDER "/h2dRingObservableDeltaPhiVsLeadJetPt", deltaPhiJet, leadingJetPt, ringObservable) \ - X(FOLDER "/h2dRingObservableDeltaThetaVsLeadJetPt", deltaThetaJet, leadingJetPt, ringObservable) \ - /* Counters */ \ - X(FOLDER "/h2dDeltaPhiVsLeadJetPt", deltaPhiJet, leadingJetPt) \ - X(FOLDER "/h2dDeltaThetaVsLeadJetPt", deltaThetaJet, leadingJetPt) \ - /* Additional plots for instant gratification - 1D Profiles */ \ - X(FOLDER "/pRingObservableDeltaPhi", deltaPhiJet, ringObservable) \ - X(FOLDER "/pRingObservableDeltaTheta", deltaThetaJet, ringObservable) \ - X(FOLDER "/pRingObservableIntegrated", 0., ringObservable) \ - X(FOLDER "/pRingObservableLambdaPt", v0pt, ringObservable) \ - /* 2D Profiles */ \ - X(FOLDER "/p2dRingObservableDeltaPhiVsLambdaPt", deltaPhiJet, v0pt, ringObservable) \ - X(FOLDER "/p2dRingObservableDeltaThetaVsLambdaPt", deltaThetaJet, v0pt, ringObservable) \ - X(FOLDER "/p2dRingObservableDeltaPhiVsLeadJetPt", deltaPhiJet, leadingJetPt, ringObservable) \ - X(FOLDER "/p2dRingObservableDeltaThetaVsLeadJetPt", deltaThetaJet, leadingJetPt, ringObservable) \ - /* 1D Mass */ \ - X(FOLDER "/hMass", v0LambdaLikeMass) \ - X(FOLDER "/hRingObservableMass", v0LambdaLikeMass, ringObservable) \ - X(FOLDER "/hMassSigExtract", v0LambdaLikeMass) \ - /* 2D: Observable vs Mass */ \ - X(FOLDER "/h2dRingObservableDeltaPhiVsMass", deltaPhiJet, v0LambdaLikeMass, ringObservable) \ - X(FOLDER "/h2dRingObservableDeltaThetaVsMass", deltaThetaJet, v0LambdaLikeMass, ringObservable) \ - /* Counters */ \ - X(FOLDER "/h2dDeltaPhiVsMass", deltaPhiJet, v0LambdaLikeMass) \ - X(FOLDER "/h2dDeltaThetaVsMass", deltaThetaJet, v0LambdaLikeMass) \ - /* 3D: Observable vs Mass vs Lambda pT */ \ - X(FOLDER "/h3dRingObservableDeltaPhiVsMassVsLambdaPt", deltaPhiJet, v0LambdaLikeMass, v0pt, ringObservable) \ - X(FOLDER "/h3dRingObservableDeltaThetaVsMassVsLambdaPt", deltaThetaJet, v0LambdaLikeMass, v0pt, ringObservable) \ - /* Counters */ \ - X(FOLDER "/h3dDeltaPhiVsMassVsLambdaPt", deltaPhiJet, v0LambdaLikeMass, v0pt) \ - X(FOLDER "/h3dDeltaThetaVsMassVsLambdaPt", deltaThetaJet, v0LambdaLikeMass, v0pt) \ - /* 3D: Observable vs Mass vs Lead Jet pT */ \ - X(FOLDER "/h3dRingObservableDeltaPhiVsMassVsLeadJetPt", deltaPhiJet, v0LambdaLikeMass, leadingJetPt, ringObservable) \ - X(FOLDER "/h3dRingObservableDeltaThetaVsMassVsLeadJetPt", deltaThetaJet, v0LambdaLikeMass, leadingJetPt, ringObservable) \ - /* Counters */ \ - X(FOLDER "/h3dDeltaPhiVsMassVsLeadJetPt", deltaPhiJet, v0LambdaLikeMass, leadingJetPt) \ - X(FOLDER "/h3dDeltaThetaVsMassVsLeadJetPt", deltaThetaJet, v0LambdaLikeMass, leadingJetPt) \ - /* 2D: Observable vs Mass vs Centrality (projected as 2D Mass vs Cent for integrated observable) */ \ - X(FOLDER "/h2dRingObservableMassVsCent", v0LambdaLikeMass, centrality, ringObservable) \ - /* 3D: Observable vs Mass vs Centrality */ \ - X(FOLDER "/h3dRingObservableDeltaPhiVsMassVsCent", deltaPhiJet, v0LambdaLikeMass, centrality, ringObservable) \ - X(FOLDER "/h3dRingObservableDeltaThetaVsMassVsCent", deltaThetaJet, v0LambdaLikeMass, centrality, ringObservable) \ - /* Counters */ \ - X(FOLDER "/h3dDeltaPhiVsMassVsCent", deltaPhiJet, v0LambdaLikeMass, centrality) \ - X(FOLDER "/h3dDeltaThetaVsMassVsCent", deltaThetaJet, v0LambdaLikeMass, centrality) \ - /* TProfile of Ring vs Mass */ \ - X(FOLDER "/pRingObservableMass", v0LambdaLikeMass, ringObservable) \ - /* TProfile of Ring vs Mass -- Leading Particle and 2nd-to-leading jet - QA */ \ - X(FOLDER "/pRingObservableLeadPMass", v0LambdaLikeMass, ringObservableLeadP) \ - X(FOLDER "/pRingObservable2ndJetMass", v0LambdaLikeMass, ringObservable2ndJet) \ - /* 2D Profiles: Angle vs Mass */ \ - X(FOLDER "/p2dRingObservableDeltaPhiVsMass", deltaPhiJet, v0LambdaLikeMass, ringObservable) \ - X(FOLDER "/p2dRingObservableDeltaThetaVsMass", deltaThetaJet, v0LambdaLikeMass, ringObservable) \ - /* 3D Profiles: Angle vs Mass vs Lambda pT */ \ +#define RING_OBSERVABLE_FILL_LIST(X, FOLDER) \ + /* Counters */ \ + X(FOLDER "/QA/hDeltaPhi", deltaPhiJet) \ + X(FOLDER "/QA/hDeltaTheta", deltaThetaJet) \ + X(FOLDER "/QA/hIntegrated", 0.) \ + /* Lambda pT variation -- Youpeng's proposal */ \ + X(FOLDER "/QA/hLambdaPt", v0pt) \ + /* Counters */ \ + X(FOLDER "/QA/h2dDeltaPhiVsLambdaPt", deltaPhiJet, v0pt) \ + X(FOLDER "/QA/h2dDeltaThetaVsLambdaPt", deltaThetaJet, v0pt) \ + /* Additional plots for instant gratification - 1D Profiles */ \ + X(FOLDER "/pRingObservableDeltaPhi", deltaPhiJet, ringObservable) \ + X(FOLDER "/pRingObservableDeltaTheta", deltaThetaJet, ringObservable) \ + X(FOLDER "/pRingObservableIntegrated", 0., ringObservable) \ + X(FOLDER "/pRingObservableLambdaPt", v0pt, ringObservable) \ + /* 2D Profiles */ \ + X(FOLDER "/p2dRingObservableDeltaPhiVsLambdaPt", deltaPhiJet, v0pt, ringObservable) \ + X(FOLDER "/p2dRingObservableDeltaThetaVsLambdaPt", deltaThetaJet, v0pt, ringObservable) \ + X(FOLDER "/p2dRingObservableDeltaPhiVsLeadJetPt", deltaPhiJet, leadingJetPt, ringObservable) \ + X(FOLDER "/p2dRingObservableDeltaThetaVsLeadJetPt", deltaThetaJet, leadingJetPt, ringObservable) \ + /* 1D Mass */ \ + X(FOLDER "/QA/hMass", v0LambdaLikeMass) \ + X(FOLDER "/QA/hRingObservableNumMass", v0LambdaLikeMass, ringObservable) \ + X(FOLDER "/hMassSigExtract", v0LambdaLikeMass) \ + /* Counters */ \ + X(FOLDER "/QA/h2dDeltaPhiVsMass", deltaPhiJet, v0LambdaLikeMass) \ + X(FOLDER "/QA/h2dDeltaThetaVsMass", deltaThetaJet, v0LambdaLikeMass) \ + X(FOLDER "/QA/h3dDeltaPhiVsMassVsLambdaPt", deltaPhiJet, v0LambdaLikeMass, v0pt) \ + X(FOLDER "/QA/h3dDeltaThetaVsMassVsLambdaPt", deltaThetaJet, v0LambdaLikeMass, v0pt) \ + X(FOLDER "/QA/h3dDeltaPhiVsMassVsLeadJetPt", deltaPhiJet, v0LambdaLikeMass, leadingJetPt) \ + X(FOLDER "/QA/h3dDeltaThetaVsMassVsLeadJetPt", deltaThetaJet, v0LambdaLikeMass, leadingJetPt) \ + X(FOLDER "/QA/h3dDeltaPhiVsMassVsCent", deltaPhiJet, v0LambdaLikeMass, centrality) \ + X(FOLDER "/QA/h3dDeltaThetaVsMassVsCent", deltaThetaJet, v0LambdaLikeMass, centrality) \ + /* TProfile of Ring vs Mass */ \ + X(FOLDER "/pRingObservableMass", v0LambdaLikeMass, ringObservable) \ + /* TProfile of Ring vs Mass -- Leading Particle and 2nd-to-leading jet - QA */ \ + X(FOLDER "/pRingObservableLeadPMass", v0LambdaLikeMass, ringObservableLeadP) \ + X(FOLDER "/pRingObservable2ndJetMass", v0LambdaLikeMass, ringObservable2ndJet) \ + /* 2D Profiles: Angle vs Mass */ \ + X(FOLDER "/p2dRingObservableDeltaPhiVsMass", deltaPhiJet, v0LambdaLikeMass, ringObservable) \ + X(FOLDER "/p2dRingObservableDeltaThetaVsMass", deltaThetaJet, v0LambdaLikeMass, ringObservable) \ + /* 3D Profiles: Angle vs Mass vs Lambda pT */ \ X(FOLDER "/p3dRingObservableDeltaPhiVsMassVsLambdaPt", deltaPhiJet, v0LambdaLikeMass, v0pt, ringObservable) \ X(FOLDER "/p3dRingObservableDeltaThetaVsMassVsLambdaPt", deltaThetaJet, v0LambdaLikeMass, v0pt, ringObservable) \ - /* 3D Profiles: Angle vs Mass vs Lead Jet pT */ \ - X(FOLDER "/p3dRingObservableDeltaPhiVsMassVsLeadJetPt", deltaPhiJet, v0LambdaLikeMass, leadingJetPt, ringObservable) \ - X(FOLDER "/p3dRingObservableDeltaThetaVsMassVsLeadJetPt", deltaThetaJet, v0LambdaLikeMass, leadingJetPt, ringObservable) \ - /* 2D Profile: Mass vs Centrality */ \ - X(FOLDER "/p2dRingObservableMassVsCent", v0LambdaLikeMass, centrality, ringObservable) \ - /* 3D Profiles: Angle vs Mass vs Centrality */ \ + /* 3D Profiles: Angle vs Mass vs Lead Jet pT */ \ + X(FOLDER "/p3dRingObservableDeltaPhiVsMassVsLeadJetPt", deltaPhiJet, v0LambdaLikeMass, leadingJetPt, ringObservable) \ + X(FOLDER "/p3dRingObservableDeltaThetaVsMassVsLeadJetPt",deltaThetaJet, v0LambdaLikeMass, leadingJetPt, ringObservable) \ + /* 2D Profile: Mass vs Centrality */ \ + X(FOLDER "/p2dRingObservableMassVsCent", v0LambdaLikeMass, centrality, ringObservable) \ + /* 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 "/pRingIntVsCentrality", centrality, ringObservable) // (TODO: add counters for regular TH2Ds about centrality) // For leading particle @@ -173,21 +151,21 @@ constexpr double polPrefactorAntiLambda = 3.0/antiLambdaWeakDecayConstant; /* =============================== */ \ /* 1D TProfiles vs v0phi */ \ /* =============================== */ \ - X(FOLDER "/pPxStarPhi", v0phiToFillHists, PolStarX) \ - X(FOLDER "/pPyStarPhi", v0phiToFillHists, PolStarY) \ - X(FOLDER "/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 "/pPxStarDeltaPhi", deltaPhiJet, PolStarX) \ - X(FOLDER "/pPyStarDeltaPhi", deltaPhiJet, PolStarY) \ - X(FOLDER "/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 "/p2dPxStarDeltaPhiVsLambdaPt", deltaPhiJet, v0pt, PolStarX) \ - X(FOLDER "/p2dPyStarDeltaPhiVsLambdaPt", deltaPhiJet, v0pt, PolStarY) \ - X(FOLDER "/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) // 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 @@ -204,6 +182,9 @@ struct lambdajetpolarizationionsderived { Configurable analyseLambda{"analyseLambda", true, "process Lambda-like candidates"}; Configurable analyseAntiLambda{"analyseAntiLambda", false, "process AntiLambda-like candidates"}; 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:CentFT0CVariant1, 3:CentMFT, 4:CentNGlobal, 5:CentFV0A)"}; // Default is FT0M // QAs that purposefully break the analysis // -- All of these tests should give us zero signal if the source is truly Lambda Polarization from vortices @@ -267,38 +248,40 @@ struct lambdajetpolarizationionsderived { // Helper to register one full histogram family (kinematic cut variation of ring observable) auto addRingObservableFamily = [&](const std::string& folder){ // =============================== - // 1D observable histograms + // QA histograms: angle and pT distributions + // (No mass dependency -- useful to check kinematic sculpting from cuts) // =============================== - histos.add((folder + "/hRingObservableDeltaPhi").c_str(), "hRingObservableDeltaPhi", kTH1D, {axisConfigurations.axisDeltaPhi}); // Not quite the ring observable itself: this is just the numerator of = \sum R_i / N_\Lambda (error bars WILL be wrong without TProfile) - histos.add((folder + "/hRingObservableDeltaTheta").c_str(), "hRingObservableDeltaTheta", kTH1D, {axisConfigurations.axisDeltaTheta}); - histos.add((folder + "/hRingObservableIntegrated").c_str(), "hRingObservableIntegrated", kTH1D, {{1, -0.5, 0.5}}); - // Counters (denominators) - histos.add((folder + "/hDeltaPhi").c_str(), "hDeltaPhi", kTH1D, {axisConfigurations.axisDeltaPhi}); - histos.add((folder + "/hDeltaTheta").c_str(), "hDeltaTheta", kTH1D, {axisConfigurations.axisDeltaTheta}); - histos.add((folder + "/hIntegrated").c_str(), "hIntegrated", kTH1D, {{1, -0.5, 0.5}}); + 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/hIntegrated").c_str(), "hIntegrated", kTH1D, {{1, -0.5, 0.5}}); // =============================== // Lambda pT dependence // =============================== - histos.add((folder + "/hRingObservableLambdaPt").c_str(), "hRingObservableLambdaPt", kTH1D, {axisConfigurations.axisPt}); - histos.add((folder + "/hLambdaPt").c_str(), "hLambdaPt", kTH1D, {axisConfigurations.axisPt}); + 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}); // =============================== - // 2D Lambda correlations + // Polarization observable QAs + // (not Ring: actual polarization!) // =============================== - histos.add((folder + "/h2dRingObservableDeltaPhiVsLambdaPt").c_str(), "h2dRingObservableDeltaPhiVsLambdaPt", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisPt}); - histos.add((folder + "/h2dRingObservableDeltaThetaVsLambdaPt").c_str(), "h2dRingObservableDeltaThetaVsLambdaPt", kTH2D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisPt}); - // Counters - histos.add((folder + "/h2dDeltaPhiVsLambdaPt").c_str(), "h2dDeltaPhiVsLambdaPt", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisPt}); - histos.add((folder + "/h2dDeltaThetaVsLambdaPt").c_str(), "h2dDeltaThetaVsLambdaPt", kTH2D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisPt}); + // Will implement these as TProfiles, as polarization is also a measure like P_\Lambda = (3/\alpha_\Lambda) * , so the error is similar // =============================== - // 2D Jet correlations + // 1D TProfiles // =============================== - histos.add((folder + "/h2dRingObservableDeltaPhiVsLeadJetPt").c_str(), "h2dRingObservableDeltaPhiVsLeadJetPt", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisJetPt}); - histos.add((folder + "/h2dRingObservableDeltaThetaVsLeadJetPt").c_str(), "h2dRingObservableDeltaThetaVsLeadJetPt", kTH2D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisJetPt}); - // Counters - histos.add((folder + "/h2dDeltaPhiVsLeadJetPt").c_str(), "h2dDeltaPhiVsLeadJetPt", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisJetPt}); - histos.add((folder + "/h2dDeltaThetaVsLeadJetPt").c_str(), "h2dDeltaThetaVsLeadJetPt", kTH2D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisJetPt}); + 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}); + // =============================== + // 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}); - // Additional plots for instant gratification: + // 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 // -- If any efficiency corrections arise, you can fill with the kTH1D as (deltaPhiJet, ringObservable, weight) @@ -313,7 +296,6 @@ 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}); - // 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}); @@ -339,50 +321,37 @@ struct lambdajetpolarizationionsderived { // Multi-dimensional histograms for signal extraction // (Mass-dependent polarization extraction) // =============================== - // (TODO: possibly remove all TH2Ds that are not counter histograms and deal only with TProfiles!) // Simple invariant mass plot for QA: - histos.add((folder + "/hMass").c_str(), "hMass", kTH1D, {axisConfigurations.axisLambdaMass}); + histos.add((folder + "/QA/hMass").c_str(), "hMass", kTH1D, {axisConfigurations.axisLambdaMass}); histos.add((folder + "/hMassSigExtract").c_str(), "hMassSigExtract", kTH1D, {axisConfigurations.axisLambdaMassSigExtract}); - // 1D Mass dependence of observable: - // Important to know if the signal varies with mass, or if the sideband signal subtraction will probably work well enough! - histos.add((folder + "/hRingObservableMass").c_str(), "hRingObservableMass", kTH1D, {axisConfigurations.axisLambdaMassSigExtract}); - // --- 2D: Angular observable vs Invariant Mass --- - // Ring observable weighted with R - histos.add((folder + "/h2dRingObservableDeltaPhiVsMass").c_str(), "h2dRingObservableDeltaPhiVsMass", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract}); - histos.add((folder + "/h2dRingObservableDeltaThetaVsMass").c_str(), "h2dRingObservableDeltaThetaVsMass", kTH2D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract}); - // --- Counters (denominators) --- - histos.add((folder + "/h2dDeltaPhiVsMass").c_str(), "h2dDeltaPhiVsMass", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract}); - histos.add((folder + "/h2dDeltaThetaVsMass").c_str(), "h2dDeltaThetaVsMass", kTH2D,{axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract}); - // --- 3D: Angular observable vs Mass vs Lambda pT --- - histos.add((folder + "/h3dRingObservableDeltaPhiVsMassVsLambdaPt").c_str(), "h3dRingObservableDeltaPhiVsMassVsLambdaPt", kTH3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisPtSigExtract}); - histos.add((folder + "/h3dRingObservableDeltaThetaVsMassVsLambdaPt").c_str(), "h3dRingObservableDeltaThetaVsMassVsLambdaPt", kTH3D, {axisConfigurations.axisDeltaTheta,axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisPtSigExtract}); - // Counters - histos.add((folder + "/h3dDeltaPhiVsMassVsLambdaPt").c_str(), "h3dDeltaPhiVsMassVsLambdaPt", kTH3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisPtSigExtract}); - histos.add((folder + "/h3dDeltaThetaVsMassVsLambdaPt").c_str(), "h3dDeltaThetaVsMassVsLambdaPt", kTH3D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisPtSigExtract}); - // --- 3D: Angular observable vs Mass vs Lead Jet pT --- - histos.add((folder + "/h3dRingObservableDeltaPhiVsMassVsLeadJetPt").c_str(), "h3dRingObservableDeltaPhiVsMassVsLeadJetPt", kTH3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisJetPtSigExtract}); - histos.add((folder + "/h3dRingObservableDeltaThetaVsMassVsLeadJetPt").c_str(), "h3dRingObservableDeltaThetaVsMassVsLeadJetPt", kTH3D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisJetPtSigExtract}); - // --- Counters --- - histos.add((folder + "/h3dDeltaPhiVsMassVsLeadJetPt").c_str(), "h3dDeltaPhiVsMassVsLeadJetPt", kTH3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisJetPtSigExtract}); - histos.add((folder + "/h3dDeltaThetaVsMassVsLeadJetPt").c_str(), "h3dDeltaThetaVsMassVsLeadJetPt", kTH3D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisJetPtSigExtract}); - - ///////////////////////////////////// - /// TProfiles with the proper errors for quick glancing - ///////////////////////////////////// + // 1D Mass dependence of observable numerator: + histos.add((folder + "/QA/hRingObservableNumMass").c_str(), "hRingObservableNumMass", 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}); + // --- 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}); + // --- 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}); + + // =============================== + // 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(), "pRingObservableLeadPMass;m_{p#pi};<#it{R}>", kTProfile, {axisConfigurations.axisLambdaMassSigExtract}); - // TProfile2D: vs Mass (DeltaPhi) + // TProfile2D: vs Mass (DeltaPhi) histos.add((folder + "/p2dRingObservableDeltaPhiVsMass").c_str(), "p2dRingObservableDeltaPhiVsMass;#Delta#varphi;m_{p#pi};<#it{R}>", kTProfile2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract}); - // TProfile2D: vs Mass (DeltaTheta) + // TProfile2D: vs Mass (DeltaTheta) histos.add((folder + "/p2dRingObservableDeltaThetaVsMass").c_str(), "p2dRingObservableDeltaThetaVsMass;#Delta#theta;m_{p#pi};<#it{R}>", kTProfile2D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract}); - - // --- TProfile3D: vs DeltaPhi vs Mass vs LambdaPt --- + // --- 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 --- + // --- 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}); - // --- TProfile3D: vs DeltaPhi vs Mass vs LeadJetPt --- + // --- 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}); // --- 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}); @@ -390,46 +359,18 @@ struct lambdajetpolarizationionsderived { // =============================== // Mass histograms with centrality // =============================== - // 2D Mass dependence of observable vs Centrality: - // Important to know if the signal varies with mass, or if the sideband signal subtraction will probably work well enough! - histos.add((folder + "/h2dRingObservableMassVsCent").c_str(), "h2dRingObservableMassVsCent", kTH2D, {axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisCentrality}); - // --- 3D: Angular observable vs Invariant Mass --- - histos.add((folder + "/h3dRingObservableDeltaPhiVsMassVsCent").c_str(), "h3dRingObservableDeltaPhiVsMassVsCent", kTH3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisCentrality}); - histos.add((folder + "/h3dRingObservableDeltaThetaVsMassVsCent").c_str(), "h3dRingObservableDeltaThetaVsMassVsCent", kTH3D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisCentrality}); - // --- Counters (denominators) --- - histos.add((folder + "/h3dDeltaPhiVsMassVsCent").c_str(), "h3dDeltaPhiVsMassVsCent", kTH3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisCentrality}); - histos.add((folder + "/h3dDeltaThetaVsMassVsCent").c_str(), "h3dDeltaThetaVsMassVsCent", kTH3D,{axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisCentrality}); - + // 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}); // Useful TProfiles: - // --- TProfile2D: vs Mass vs Centrality --- + // --- TProfile1D: Integrated vs Centrality: + histos.add((folder + "/pRingIntVsCentrality").c_str(), "pRingIntVsCentrality; 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 --- + // --- 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}); - // --- TProfile3D: vs DeltaTheta vs Mass vs Centrality --- + // --- 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}); - // --- TProfile1D: Integrated vs Centrality: - histos.add((folder + "/pRingIntVsCentrality").c_str(), "pRingIntVsCentrality; Centrality (%);<#it{R}>", kTProfile, {axisConfigurations.axisCentrality}); - - // =============================== - // 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 - // =============================== - // 1D TProfiles - // =============================== - histos.add((folder + "/pPxStarPhi").c_str(), "pPxStarPhi;#varphi_{#Lambda};_{x}", kTProfile, {axisConfigurations.axisDeltaPhi}); - histos.add((folder + "/pPyStarPhi").c_str(), "pPyStarPhi;#varphi_{#Lambda};_{y}", kTProfile, {axisConfigurations.axisDeltaPhi}); - histos.add((folder + "/pPzStarPhi").c_str(), "pPzStarPhi;#varphi_{#Lambda};_{z}", kTProfile, {axisConfigurations.axisDeltaPhi}); - histos.add((folder + "/pPxStarDeltaPhi").c_str(), "pPxStarDeltaPhi;#Delta#varphi_{jet};_{x}", kTProfile, {axisConfigurations.axisDeltaPhi}); - histos.add((folder + "/pPyStarDeltaPhi").c_str(), "pPyStarDeltaPhi;#Delta#varphi_{jet};_{y}", kTProfile, {axisConfigurations.axisDeltaPhi}); - histos.add((folder + "/pPzStarDeltaPhi").c_str(), "pPzStarDeltaPhi;#Delta#varphi_{jet};_{z}", kTProfile, {axisConfigurations.axisDeltaPhi}); - // =============================== - // 2D TProfiles (Lambda correlations) - // =============================== - histos.add((folder + "/p2dPxStarDeltaPhiVsLambdaPt").c_str(), "p2dPxStarDeltaPhiVsLambdaPt;#Delta#varphi_{jet};#it{p}_{T}^{#Lambda};_{x}", kTProfile2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisPtSigExtract}); - histos.add((folder + "/p2dPyStarDeltaPhiVsLambdaPt").c_str(), "p2dPyStarDeltaPhiVsLambdaPt;#Delta#varphi_{jet};#it{p}_{T}^{#Lambda};_{y}", kTProfile2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisPtSigExtract}); - histos.add((folder + "/p2dPzStarDeltaPhiVsLambdaPt").c_str(), "p2dPzStarDeltaPhiVsLambdaPt;#Delta#varphi_{jet};#it{p}_{T}^{#Lambda};_{z}", kTProfile2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisPtSigExtract}); // =============================== // QA histograms - Useful numbers @@ -437,10 +378,8 @@ struct lambdajetpolarizationionsderived { // (TODO: implement these!) // (TODO: implement momentum imbalance checks for jets!) // Added to a separate folder for further control (changed the usage of the "folder" string): - // histos.add(("QA_Numbers/" + folder + "/hEventsWithV0").c_str(), "hEventsWithV0", kTH1D, {{1,0,1}}); // In the current derived data, all saved events have a V0 - // histos.add(("QA_Numbers/" + folder + "/hLambdaCounter").c_str(), "hLambdaCounter", kTH1D, {{1,0,1}}); - // histos.add(("QA_Numbers/" + folder + "/hAntiLambdaCounter").c_str(), "hAntiLambdaCounter", kTH1D, {{1,0,1}}); // 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 }; // Execute local lambda to register histogram families: addRingObservableFamily("Ring"); @@ -457,15 +396,28 @@ struct lambdajetpolarizationionsderived { // 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, "|Jet_{#eta}|<0.5"); - histos.get(HIST("pRingCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(4, "#Lambda + Jet cuts"); + 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, "|Jet_{#eta}|<0.5"); - histos.get(HIST("pRingCutsLeadingP"))->GetXaxis()->SetBinLabel(4, "#Lambda + Jet cuts"); + histos.get(HIST("pRingCutsLeadingP"))->GetXaxis()->SetBinLabel(3, "|LeadP_{#eta}|<0.5"); + histos.get(HIST("pRingCutsLeadingP"))->GetXaxis()->SetBinLabel(4, "#Lambda + LeadP cuts"); + } + + // Helper to get centrality (same from TableProducer, thanks to templating!): + template + auto getCentrality(TCollision const& collision) + { + if (centralityEstimator == kCentFT0M) return collision.centFT0M(); + else if (centralityEstimator == kCentFT0C) return collision.centFT0C(); + else if (centralityEstimator == kCentFT0CVariant1) return collision.centFT0CVariant1(); + else if (centralityEstimator == kCentMFT) return collision.centMFT(); + else if (centralityEstimator == kCentNGlobal) return collision.centNGlobal(); + else if (centralityEstimator == kCentFV0A) return collision.centFV0A(); + return -1.f; } // Initializing a random number generator for the worker (for perpendicular-to-jet direction QAs): @@ -478,41 +430,67 @@ struct lambdajetpolarizationionsderived { Preslice perColLeadPs = o2::aod::lambdajetpol::collisionId; void processPolarizationData(o2::aod::RingCollisions const& collisions, o2::aod::RingJets const& jets, o2::aod::RingLaV0s const& v0s, o2::aod::RingLeadP const& leadPs){ - for (auto const& collision : collisions) { const auto collId = collision.collisionId(); - const double centrality = collision.centrality(); + const double centrality = getCentrality(collision); - // Slice jets and V0s belonging to this collision + // 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 jetsInColl = jets.sliceBy(perColJets, collId); - auto v0sInColl = v0s.sliceBy(perColV0s, collId); + 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!) - if (!jetsInColl.size() || !v0sInColl.size()) continue; - // (TODO: either add a separate loop for events that have a leading particle, but no leading jet, - // or be aware that there are events saved with a leading particle that don't have a single valid jet in them) - // (by the way, no need to check a leadPsInColl, because if there is a jet in the collision, - // there surely is a leading particle in it) - - // Get leading jet and second to leading jet: - double leadingJetPt = -1.; - double subleadingJetPt = -1.; - o2::aod::RingJets::iterator leadingJet; - std::optional subleadingJet; + // 1) Require at least one V0: + if (!v0sInColl.size()) continue; + + // 2) We require at least a leading particle, then we get the leading jet only if it exists: + // (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) + 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(); + break; + } + // Discard events with no leading particle (FastJet didn't even run in these cases!): + if (leadPPt < 0.) continue; + + // Build leading particle unit vector, outside the V0 loop for performance: + XYZVector leadPUnitVec = XYZVector(leadPPx, leadPPy, leadPPz).Unit(); + + // 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: - // (will work if subleadingJet was not found!) + // (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 handled by std::optional! - // Now update updating the leading jet: + subleadingJet = leadingJet; // may still be std::nullopt on first pass -- that is fine! + // Now update the leading jet: leadingJetPt = jetpt; leadingJet = jet; } @@ -522,89 +500,50 @@ struct lambdajetpolarizationionsderived { } } - // For leading jet (always exists): - const double leadingJetEta = leadingJet.jetEta(); - const double leadingJetPhi = leadingJet.jetPhi(); - // Convert to 3-vector components for inner product: - const double jetPx = leadingJetPt * std::cos(leadingJetPhi); - const double jetPy = leadingJetPt * std::sin(leadingJetPhi); - const double jetPz = leadingJetPt * std::sinh(leadingJetEta); - // XYZVector leadingJetVec(jetPx, jetPy, jetPz); - XYZVector leadingJetUnitVec = XYZVector(jetPx, jetPy, 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: + // Some useful bools to check if we have a leading jet and a subleading jet: + const bool hasValidLeadingJet = leadingJetPt > 0.; + const bool hasValidSubJet = subleadingJetPt > 0.; + + // 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 = leadingJet->jetEta(); + 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); - XYZVector rotatedPerpVec = perpVec * std::cos(randomAngle) + leadingJetUnitVec.Cross(perpVec) * std::sin(randomAngle); - leadingJetUnitVec = rotatedPerpVec; - } - - // ----------------------------------------------------------------------- - // Find the leading particle for this collision. - // pT = -1 means "not found". - double leadPPt = -1.; - double leadPEta = 0.; // safe dummy values -- only used when leadPPt > 0 - double leadPPhi = 0.; - // std::optional avoids the unassigned-iterator trap again: - std::optional leadingParticlePtr; - auto leadPsInColl = leadPs.sliceBy(perColLeadPs, collId); - for (auto const& lp : leadPsInColl) { - // Table should contain exactly one entry per collision, - // but we break immediately to be safe: - leadingParticlePtr = lp; - break; - } - // Extract kinematics only if we actually found an entry: - // (Physically, if there is at least one jet there should always be a - // leading particle, but we guard against it anyway) - if (leadingParticlePtr.has_value()) { - leadPPt = leadingParticlePtr->leadParticlePt(); - leadPEta = leadingParticlePtr->leadParticleEta(); - leadPPhi = leadingParticlePtr->leadParticlePhi(); + double randomAngle = randomGen.Uniform(0., o2::constants::math::TwoPI); + leadingJetUnitVec = perpVec * std::cos(randomAngle) + leadingJetUnitVec.Cross(perpVec) * std::sin(randomAngle); + } } - // Defining some bools to help (useful when no subleading jet was found): - bool hasValidLeadP = leadPPt > 0.; // Should ALWAYS be true. There is no leading jet without a leading particle and we check for leading jets! - bool hasValidSubJet = subleadingJetPt > 0.; - - // --- Subleading jet (only valid when subleadingJetPt > 0) --- - // We still build the variables here to keep the V0 loop clean. - // Their values are irrelevant when subleadingJetPt <= 0. - double subleadingJetEta = 0.; - double subleadingJetPhi = 0.; - XYZVector subJetUnitVec(1., 0., 0.); // dummy unit vector: overwritten below + float subleadingJetEta = 0.; + float subleadingJetPhi = 0.; + XYZVector subJetUnitVec(1., 0., 0.); if (hasValidSubJet) { subleadingJetEta = subleadingJet->jetEta(); subleadingJetPhi = subleadingJet->jetPhi(); - const double subJetPx = subleadingJetPt * std::cos(subleadingJetPhi); - const double subJetPy = subleadingJetPt * std::sin(subleadingJetPhi); - const double subJetPz = subleadingJetPt * std::sinh(subleadingJetEta); - subJetUnitVec = XYZVector(subJetPx, subJetPy, subJetPz).Unit(); + // Using internal getters to make code cleaner: + subJetUnitVec = XYZVector(subleadingJet->jetPx(), subleadingJet->jetPy(), subleadingJet->jetPz()).Unit(); } - // --- Leading particle (only valid when leadPPt > 0) --- - XYZVector leadPUnitVec(1., 0., 0.); // dummy: overwritten below - if (hasValidLeadP) { - const double leadPPx = leadPPt * std::cos(leadPPhi); - const double leadPPy = leadPPt * std::sin(leadPPhi); - const double leadPPz = leadPPt * std::sinh(leadPEta); - leadPUnitVec = XYZVector(leadPPx, leadPPy, leadPPz).Unit(); - } - - // Calculating per-event bools only once: - const bool kinematicJetCheck = std::abs(leadingJetEta) < 0.5; - const bool kinematic2ndJetCheck = (subleadingJetPt > 0.) && (std::abs(subleadingJetEta) < 0.5); - const bool kinematicLeadPCheck = (leadPPt > 0.) && (std::abs(leadPEta) < 0.5); + // (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; - // TODO: add centrality selection procedure and options (one configurable for no centrality separation at all too!) for (auto const& v0 : v0sInColl) { const bool isLambda = v0.isLambda(); const bool isAntiLambda = v0.isAntiLambda(); @@ -613,14 +552,14 @@ struct lambdajetpolarizationionsderived { // 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 double v0pt = v0.v0Pt(); - const double v0eta = v0.v0Eta(); - const double v0phi = v0.v0Phi(); - - double v0LambdaLikeMass = 0; // Initialized just to catch any stray behavior - double protonLikePt = 0; - double protonLikeEta = 0; - double protonLikePhi = 0; + const float v0pt = v0.v0Pt(); + const float v0eta = v0.v0Eta(); + const float v0phi = v0.v0Phi(); + + float v0LambdaLikeMass = 0; // Initialized just to catch any stray behavior + float protonLikePt = 0; + float protonLikeEta = 0; + float protonLikePhi = 0; if (isLambda){ if (!analyseLambda) continue; v0LambdaLikeMass = v0.massLambda(); @@ -638,7 +577,7 @@ struct lambdajetpolarizationionsderived { PtEtaPhiMVector lambdaLike4Vec(v0pt, v0eta, v0phi, v0LambdaLikeMass); PtEtaPhiMVector protonLike4Vec(protonLikePt, protonLikeEta, protonLikePhi, protonMass); - double lambdaRapidity = lambdaLike4Vec.Rapidity(); // For further kinematic selections + 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 @@ -648,30 +587,49 @@ struct lambdajetpolarizationionsderived { XYZVector lambdaLike3Vec = lambdaLike4Vec.Vect(); XYZVector protonLikeStarUnit3Vec = protonLike4VecStar.Vect().Unit(); - // Calculating cross product: - XYZVector cross = leadingJetUnitVec.Cross(lambdaLike3Vec); - double crossProductNorm = cross.R(); - - double ringObservable = protonLikeStarUnit3Vec.Dot(cross) / crossProductNorm; - // Adding the prefactor related to the CP-violating decay (decay constants have different signs) - if (!forcePolSignQA) ringObservable *= (isLambda) ? polPrefactorLambda : polPrefactorAntiLambda; - else ringObservable *= (isLambda) ? polPrefactorLambda : -1.0*polPrefactorAntiLambda; + //////////////////////////////////////////// + // Ring observable: Leading particle proxy + // Always computed -- leading particle existence is guaranteed by the second check above + //////////////////////////////////////////// + // 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 - // Angular variables: - double deltaPhiJet = wrapToPiFast(v0phi - leadingJetPhi); // Wrapped to [-PI, pi), for convenience - double deltaThetaJet = ROOT::Math::VectorUtil::Angle(leadingJetUnitVec, lambdaLike3Vec); // 3D angular separation + ////////////////////////////////////////// + // Ring observable: Leading jet proxy + // Only computed when a leading jet exists in this collision. + ////////////////////////////////////////// + float ringObservable = 0.; + float deltaPhiJet = 0.; + float deltaThetaJet = 0.; + if (hasValidLeadingJet) { + // Cross product + XYZVector cross = leadingJetUnitVec.Cross(lambdaLike3Vec); + ringObservable = protonLikeStarUnit3Vec.Dot(cross) / cross.R(); + // Adding prefactor + if (!forcePolSignQA) ringObservable *= (isLambda) ? polPrefactorLambda : polPrefactorAntiLambda; + else ringObservable *= (isLambda) ? polPrefactorLambda : -1.0 * polPrefactorAntiLambda; + // Angular variables + deltaPhiJet = wrapToPiFast(v0phi - leadingJetPhi); + deltaThetaJet = ROOT::Math::VectorUtil::Angle(leadingJetUnitVec, lambdaLike3Vec); + } ////////////////////////////////////////// // Ring observable: Subleading jet proxy + // Only computed when a subleading jet exists in this collision. ////////////////////////////////////////// - double ringObservable2ndJet = 0.; - double deltaPhi2ndJet = 0.; - double deltaTheta2ndJet = 0.; + float ringObservable2ndJet = 0.; + float deltaPhi2ndJet = 0.; + float deltaTheta2ndJet = 0.; if (hasValidSubJet) { - // Cross product XYZVector cross2ndJet = subJetUnitVec.Cross(lambdaLike3Vec); - double crossProductNorm2ndJet = cross2ndJet.R(); - ringObservable2ndJet = protonLikeStarUnit3Vec.Dot(cross2ndJet) / crossProductNorm2ndJet; + ringObservable2ndJet = protonLikeStarUnit3Vec.Dot(cross2ndJet) / cross2ndJet.R(); // Adding prefactor if (!forcePolSignQA) ringObservable2ndJet *= (isLambda) ? polPrefactorLambda : polPrefactorAntiLambda; else ringObservable2ndJet *= (isLambda) ? polPrefactorLambda : -1.0 * polPrefactorAntiLambda; @@ -680,28 +638,9 @@ struct lambdajetpolarizationionsderived { deltaTheta2ndJet = ROOT::Math::VectorUtil::Angle(subJetUnitVec, lambdaLike3Vec); } - //////////////////////////////////////////// - // Ring observable: Leading particle proxy - //////////////////////////////////////////// - double ringObservableLeadP = 0.; - double deltaPhiLeadP = 0.; - double deltaThetaLeadP = 0.; - if (hasValidLeadP) { - // Cross product - XYZVector crossLeadP = leadPUnitVec.Cross(lambdaLike3Vec); - double crossProductNormLeadP = crossLeadP.R(); - ringObservableLeadP = protonLikeStarUnit3Vec.Dot(crossLeadP) / crossProductNormLeadP; - // Adding prefactor - if (!forcePolSignQA) ringObservableLeadP *= (isLambda) ? polPrefactorLambda : polPrefactorAntiLambda; - else ringObservableLeadP *= (isLambda) ? polPrefactorLambda : -1.0 * polPrefactorAntiLambda; - // Angular variables - deltaPhiLeadP = wrapToPiFast(v0phi - leadPPhi); - deltaThetaLeadP = ROOT::Math::VectorUtil::Angle(leadPUnitVec, lambdaLike3Vec); - } - // 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) * - double PolStarX, PolStarY, PolStarZ; + float PolStarX, PolStarY, PolStarZ; if (isLambda){ // Notice there is no need to check analyseLambda again due to previous checks. PolStarX = polPrefactorLambda * protonLikeStarUnit3Vec.X(); PolStarY = polPrefactorLambda * protonLikeStarUnit3Vec.Y(); @@ -713,33 +652,32 @@ struct lambdajetpolarizationionsderived { PolStarZ = polPrefactorAntiLambda * protonLikeStarUnit3Vec.Z(); } - double v0phiToFillHists = wrapToPiFast(v0phi); // A short wrap to reuse some predefined axes + 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_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("pRingCuts"), 0, ringObservable); // First bin of comparison - - // Checks with other jet proxies: - if (hasValidLeadP) { - RING_OBSERVABLE_LEADP_FILL_LIST(APPLY_HISTO_FILL, "Ring") - histos.fill(HIST("pRingCutsLeadingP"), 0, ringObservableLeadP); + 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 + POLARIZATION_PROFILE_FILL_LIST(APPLY_HISTO_FILL, "Ring") + + if (hasValidLeadingJet) { + RING_OBSERVABLE_FILL_LIST(APPLY_HISTO_FILL, "Ring") + histos.fill(HIST("pRingCuts"), 0, ringObservable); } if (hasValidSubJet) { RING_OBSERVABLE_2NDJET_FILL_LIST(APPLY_HISTO_FILL, "Ring") histos.fill(HIST("pRingCutsSubLeadingJet"), 0, ringObservable2ndJet); } - POLARIZATION_PROFILE_FILL_LIST(APPLY_HISTO_FILL, "Ring") // 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_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts") - histos.fill(HIST("pRingCuts"), 1, ringObservable); + RING_OBSERVABLE_LEADP_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts") + histos.fill(HIST("pRingCutsLeadingP"), 1, ringObservableLeadP); POLARIZATION_PROFILE_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts") - if (hasValidLeadP) { - RING_OBSERVABLE_LEADP_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts") - histos.fill(HIST("pRingCutsLeadingP"), 1, ringObservableLeadP); + if (hasValidLeadingJet) { + RING_OBSERVABLE_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts") + histos.fill(HIST("pRingCuts"), 1, ringObservable); } if (hasValidSubJet) { RING_OBSERVABLE_2NDJET_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts") @@ -748,12 +686,13 @@ struct lambdajetpolarizationionsderived { } // Extra selection criteria on jet candidates: - if (kinematicJetCheck){ // This is redundant for jets with R=0.4, but for jets with R<0.4 the leading jet may be farther in eta. + // (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); POLARIZATION_PROFILE_FILL_LIST(APPLY_HISTO_FILL, "JetKinematicCuts") } - + // Extra selection criteria on both Lambda and jet candidates: if (kinematicLambdaCheck && kinematicJetCheck){ RING_OBSERVABLE_FILL_LIST(APPLY_HISTO_FILL, "JetAndLambdaKinematicCuts") From 7cd4fe7aa8b818554e1addd4d2661603900a4679 Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Tue, 3 Mar 2026 20:22:20 -0300 Subject: [PATCH 32/40] Adding extra V0 selection variables to DataModel. Cleaning up TableProducer, adding JetSelection QA, adding minLeadParticlePt logic, fixing unconstrained angles in JetKinematicsQA histograms. Adding jet direction smudging to JetPolarizationIonsDerived. --- PWGLF/DataModel/lambdaJetPolarizationIons.h | 27 +- .../Strangeness/lambdaJetPolarizationIons.cxx | 295 ++++++++++-------- .../lambdaJetPolarizationIonsDerived.cxx | 39 ++- 3 files changed, 222 insertions(+), 139 deletions(-) diff --git a/PWGLF/DataModel/lambdaJetPolarizationIons.h b/PWGLF/DataModel/lambdaJetPolarizationIons.h index 3487b75f90d..773b0c50183 100644 --- a/PWGLF/DataModel/lambdaJetPolarizationIons.h +++ b/PWGLF/DataModel/lambdaJetPolarizationIons.h @@ -29,6 +29,7 @@ namespace lambdajetpol { // DECLARE_SOA_COLUMN(CollIdx, collIdx, uint64_t); // Using a regular SOA column instead of an index column for convenience +// Collision information: DECLARE_SOA_INDEX_COLUMN(Collision, collision); DECLARE_SOA_COLUMN(CentFT0M, centFT0M, float); DECLARE_SOA_COLUMN(CentFT0C, centFT0C, float); @@ -37,6 +38,7 @@ DECLARE_SOA_COLUMN(CentMFT, centMFT, float); DECLARE_SOA_COLUMN(CentNGlobal, centNGlobal, float); DECLARE_SOA_COLUMN(CentFV0A, centFV0A, float); +// Jet (and jet proxies) information: DECLARE_SOA_COLUMN(JetPt, jetPt, float); DECLARE_SOA_COLUMN(JetEta, jetEta, float); DECLARE_SOA_COLUMN(JetPhi, jetPhi, float); @@ -46,6 +48,7 @@ DECLARE_SOA_COLUMN(LeadParticlePt, leadParticlePt, float); DECLARE_SOA_COLUMN(LeadParticleEta, leadParticleEta, float); DECLARE_SOA_COLUMN(LeadParticlePhi, leadParticlePhi, float); +// V0 information: DECLARE_SOA_COLUMN(V0Pt, v0Pt, float); DECLARE_SOA_COLUMN(V0Eta, v0Eta, float); DECLARE_SOA_COLUMN(V0Phi, v0Phi, float); @@ -62,6 +65,17 @@ 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(V0CosPA, v0cosPA, float); +DECLARE_SOA_COLUMN(V0Radius, v0radius, float); +DECLARE_SOA_COLUMN(DcaV0Daughters, dcaV0daughters, float); +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 [](float jetPt, float jetPhi) -> float {return jetPt * std::cos(jetPhi);}); @@ -102,7 +116,7 @@ DECLARE_SOA_TABLE(RingLeadP, "AOD", "RINGLEADP", // Leading particle table lambdajetpol::LeadParticlePz ); -DECLARE_SOA_TABLE(RingLaV0s, "AOD", "RINGLAV0S", // Had to write this in a shorter form because the derived data did not accept long names +DECLARE_SOA_TABLE(RingLaV0s, "AOD", "RINGLAV0S", lambdajetpol::CollisionId, lambdajetpol::V0Pt, lambdajetpol::V0Eta, @@ -116,7 +130,16 @@ DECLARE_SOA_TABLE(RingLaV0s, "AOD", "RINGLAV0S", // Had to write this in a short lambdajetpol::PosPhi, lambdajetpol::NegPt, lambdajetpol::NegEta, - lambdajetpol::NegPhi + lambdajetpol::NegPhi, + lambdajetpol::PosTPCNSigmaPr, + lambdajetpol::PosTPCNSigmaPi, + lambdajetpol::NegTPCNSigmaPr, + lambdajetpol::NegTPCNSigmaPi, + lambdajetpol::V0CosPA, + lambdajetpol::V0Radius, + lambdajetpol::DcaV0Daughters, + lambdajetpol::DcaPosToPV, + lambdajetpol::DcaNegToPV ); DECLARE_SOA_TABLE(RingCollisions, "AOD", "RINGCOLLISIONS", diff --git a/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx b/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx index 8ec479144e9..50109fa8759 100644 --- a/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx +++ b/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx @@ -26,44 +26,54 @@ // cicero.domenico.muncinelli@cern.ch // -#include "PWGJE/Core/JetBkgSubUtils.h" -#include "PWGJE/Core/JetDerivedDataUtilities.h" -#include "PWGJE/Core/JetUtilities.h" -#include "PWGJE/DataModel/Jet.h" -#include "PWGJE/DataModel/JetReducedData.h" -#include "PWGLF/DataModel/LFStrangenessTables.h" -#include "PWGLF/DataModel/LFStrangenessPIDTables.h" // For V0TOFPIDs and NSigmas getters. Better for considering the daughters as coming from V0s instead of from PV? -#include "PWGLF/DataModel/mcCentrality.h" -#include "Common/DataModel/Centrality.h" +// O2 Framework +#include +#include +#include +#include +#include +#include + +// O2 CCDB / Conditions +#include +#include +#include "DataFormatsParameters/GRPMagField.h" +// O2 Reconstruction Data Formats +#include + +// O2 Common Core #include "Common/Core/RecoDecay.h" #include "Common/Core/TrackSelection.h" #include "Common/Core/trackUtilities.h" + +// O2 Common DataModel +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/CollisionAssociationTables.h" #include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/McCollisionExtra.h" #include "Common/DataModel/Multiplicity.h" // for pp - +#include "Common/DataModel/PIDResponseTPC.h" // For PID in raw data: // #include "Common/DataModel/PIDResponseTOF.h" // Maybe switch this around with LFStrangenessPIDTables? - -#include "Common/DataModel/McCollisionExtra.h" -#include "Common/DataModel/PIDResponseTPC.h" // #include "Common/DataModel/Qvectors.h" #include "Common/DataModel/TrackSelectionTables.h" -#include -#include -#include "DataFormatsParameters/GRPMagField.h" -#include -#include -#include -#include -#include -#include +// PWGJE +#include "PWGJE/Core/JetBkgSubUtils.h" +#include "PWGJE/Core/JetDerivedDataUtilities.h" +#include "PWGJE/Core/JetUtilities.h" +#include "PWGJE/DataModel/Jet.h" +#include "PWGJE/DataModel/JetReducedData.h" -// Custom data model: +// PWGLF +#include "PWGLF/DataModel/LFStrangenessPIDTables.h" +// For V0TOFPIDs and NSigmas getters. Better for considering the daughters as coming from V0s instead of from PV? +#include "PWGLF/DataModel/LFStrangenessTables.h" #include "PWGLF/DataModel/lambdaJetPolarizationIons.h" +#include "PWGLF/DataModel/mcCentrality.h" -// Jets: +// External Libraries (FastJet) #include #include #include @@ -73,64 +83,47 @@ #include #include +// ROOT Math +#include "Math/GenVector/Boost.h" +#include "Math/Vector3D.h" +#include "Math/Vector4D.h" -////// Others not included from strangederivedbuilder.cxx: -// #include "CommonConstants/PhysicsConstants.h" -// #include "DCAFitter/DCAFitterN.h" -// #include "DataFormatsParameters/GRPMagField.h" -// #include "DataFormatsParameters/GRPObject.h" -// #include "DetectorsBase/GeometryManager.h" -// #include "DetectorsBase/Propagator.h" -// #include "Framework/O2DatabasePDGPlugin.h" -// #include "Framework/RunningWorkflowInfo.h" -// #include "Framework/StaticFor.h" - +// Standard Library #include #include #include #include -////////////////////////////////////////////// -// From Youpeng's: -#include "Common/DataModel/CollisionAssociationTables.h" -#include "Framework/ASoA.h" - -#include "Math/GenVector/Boost.h" -#include "Math/Vector3D.h" -#include "Math/Vector4D.h" -// #include "TProfile2D.h" -// #include -// // #include -// #include -// #include -////////////////////////////////////////////// - using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; using std::array; using namespace o2::aod::rctsel; -// Aliases for joined tables: +///// Aliases for joined tables +/// Collisions: using SelCollisions = soa::Join; // Added PVMults to get MultNTracksPVeta1 as centrality estimator using SelCollisionsSimple = soa::Join; // Simpler, for jets -// using DauTracks = soa::Join; - // Actually used subscriptions (smaller memory usage): -using DauTracks = soa::Join; + +/// V0s and Daughter tracks: // using V0Candidates = soa::Join; // using V0CandidatesSimple = soa::Join; // No TOF +/// To run in RAW data: +// using V0Candidates = aod::V0Datas; +using V0CandidatesWithTOF = soa::Join; // Tables created by o2-analysis-lf-strangenesstofpid +// using DauTracks = soa::Join; + // Actually used subscriptions (smaller memory usage): +using DauTracks = soa::Join; +/// Jets: using PseudoJetTracks = soa::Join; // Simpler tracks access. (Not using TracksIU and TracksCovIU. Did not use their info for now) // , aod::pidTOFFullPi, aod::pidTOFFullKa, aod::pidTOFFullPr>; // Not using TOF right now due to some possible mismatches + +/// MC: // using SimCollisions = soa::Join; // using DauTracksMC = soa::Join; -// To run in RAW data: -using V0Candidates = aod::V0Datas; -using V0CandidatesWithTOF = soa::Join; // Tables created by o2-analysis-lf-strangenesstofpid - enum CentEstimator { @@ -180,9 +173,6 @@ struct lambdajetpolarizationions { // Define histogram registries: HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - // HistogramRegistry registryData{"registryData", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; - // HistogramRegistry registryMC{"registryMC", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; - // HistogramRegistry registryQC{"registryQC", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; // master analysis switches Configurable analyseLambda{"analyseLambda", true, "process Lambda-like candidates"}; @@ -275,7 +265,7 @@ struct lambdajetpolarizationions { Configurable lambdaLifetimeCut{"lambdaLifetimeCut", 30., "lifetime cut (c*tau) for Lambda (cm)"}; // invariant mass selection - Configurable compMassRejection{"compMassRejection", -1, "Competing mass rejection (GeV/#it{c}^{2})"}; // This was creating some bumps in Youpeng's inv mass spectra. Turned off for now. + 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. // Track quality Configurable minTPCrows{"minTPCrows", 70, "minimum TPC crossed rows"}; @@ -402,13 +392,6 @@ struct lambdajetpolarizationions { // Jet QA axes: ConfigurableAxis JetsPerEvent{"JetsPerEvent", {20, 0, 20}, "Jets per event"}; - // ConfigurableAxis axisLeadingParticlePt{"axisLeadingParticlePt",{VARIABLE_WIDTH, - // 0.0f, 0.5f, 1.0f, 1.5f, 2.0f, // very low pT (coarse on purpose) - // 3.0f, 4.0f, 5.0f, 6.0f, 8.0f, 10.0f, // trigger / intermediate region - // 12.0f, 15.0f, 20.0f, 25.0f, 30.0f, // high pT leading particles - // 40.0f, 50.0f, 60.0f, 80.0f, 100.0f, - // 120.0f, 150.0f, 200.0f}, // ultra-high pT safety net - // "Leading particle p_{T} (GeV/c)"}; 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})"}; @@ -455,6 +438,8 @@ struct lambdajetpolarizationions { // Configurable hadronicCorrectionType{"hadronicCorrectionType", 0, "0 = no correction, 1 = CorrectedOneTrack1, 2 = CorrectedOneTrack2, 3 = CorrectedAllTracks1, 4 = CorrectedAllTracks2"}; // Configurable doEMCALEventSelection{"doEMCALEventSelection", true, "apply the selection to the event alias_bit for full and neutral jets"}; // Configurable doEMCALEventSelectionChargedJets{"doEMCALEventSelectionChargedJets", false, "apply the selection to the event alias_bit for charged jets"}; + + Configurable minLeadParticlePt{"minLeadParticlePt", 2.0f, "Minimum Pt for a lead track to be considered a valid proxy for a jet"}; // For OO, about 2 or 3 should be enough (z~0.3 of jet), and for PbPb maybe 8 GeV } jetConfigurations; // Creating a short map to make sure the proper FastJet enums are used (safeguard against possible updates in FastJet indices): @@ -656,20 +641,43 @@ struct lambdajetpolarizationions { addHypothesis(Lambda, analyseLambda); addHypothesis(AntiLambda, analyseAntiLambda); - auto hSelectionV0s = histos.add("GeneralQA/hSelectionV0s","V0 #rightarrow #Lambda / #bar{#Lambda} selection flow",kTH1D, + auto hSelectionV0s = histos.add("GeneralQA/hSelectionV0s","V0 #rightarrow #Lambda / #bar{#Lambda} selection flow", kTH1D, {{(int)v0LambdaSelectionLabels.size(),-0.5,(double)v0LambdaSelectionLabels.size()-0.5}}); for(size_t i=0; iGetXaxis()->SetBinLabel(i+1,lbl.c_str()); // First non-underflow bin is bin 1 } + //////////////////////////////////////////////// + // Jet track candidate selection flow (analogous to hSelectionV0s): + // Each label's "enabled" flag reflects whether the corresponding configurable + // makes that cut active, so disabled stages are shown in grey in the output. + std::vector jetTrackSelectionLabels = { + {"All track candidates", true}, + {"ITS clusters (min)", pseudoJetCandidateTrackSelections.minITSnCls >= 0}, + {"TPC crossed rows (min)", pseudoJetCandidateTrackSelections.minNCrossedRowsTPC > 0}, + {"TPC #chi^{2}/N_{cls} (max)", pseudoJetCandidateTrackSelections.maxChi2TPC < 1.e8f}, + {"ITS #chi^{2}/N_{cls} (max)", pseudoJetCandidateTrackSelections.maxChi2ITS < 1.e8f}, + {"p_{T} min", pseudoJetCandidateTrackSelections.minCandidatePt > 0.f}, + {"|#eta| cut", pseudoJetCandidateTrackSelections.etaCut < 1.5f}, + {"DCA_{z} to PV", pseudoJetCandidateTrackSelections.doDCAcuts.value}, + {"DCA_{xy} to PV (parametric)", pseudoJetCandidateTrackSelections.doDCAcuts.value}, + }; + auto hSelectionJetTracks = histos.add("GeneralQA/hSelectionJetTracks", "Charged pseudojet candidate selection flow", kTH1D, + {{(int)jetTrackSelectionLabels.size(), -0.5, (double)jetTrackSelectionLabels.size() - 0.5}}); + for (size_t i = 0; i < jetTrackSelectionLabels.size(); ++i) { + auto lbl = jetTrackSelectionLabels[i].label; + if (!jetTrackSelectionLabels[i].enabled) lbl = "#color[16]{(off) " + lbl + "}"; + hSelectionJetTracks->GetXaxis()->SetBinLabel(i + 1, lbl.c_str()); + } + //////////////////////////////////////////////// // Histograms versus mass: if (analyseLambda) { - histos.add("h2dNbrOfLambdaVsCentrality", "h2dNbrOfLambdaVsCentrality", kTH2D, {axisConfigurations.axisCentrality, {10, -0.5f, 9.5f}}); - histos.add("h3dMassLambda", "h3dMassLambda", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisLambdaMass}); + 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("h3dMassLambdaHadronic", "h3dMassLambdaHadronic", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisLambdaMass}); + 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}); @@ -706,10 +714,10 @@ struct lambdajetpolarizationions { } } if (analyseAntiLambda) { - histos.add("h2dNbrOfAntiLambdaVsCentrality", "h2dNbrOfAntiLambdaVsCentrality", kTH2D, {axisConfigurations.axisCentrality, {10, -0.5f, 9.5f}}); - histos.add("h3dMassAntiLambda", "h3dMassAntiLambda", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisLambdaMass}); + 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("h3dMassAntiLambdaHadronic", "h3dMassAntiLambdaHadronic", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisLambdaMass}); + 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}); @@ -745,9 +753,18 @@ struct lambdajetpolarizationions { } } - if (analyseLambda) histos.add("hMassLambda", "hMassLambda", kTH1D, {axisConfigurations.axisLambdaMass}); - if (analyseAntiLambda) histos.add("hMassAntiLambda", "hMassAntiLambda", kTH1D, {axisConfigurations.axisLambdaMass}); - if (analyseLambda && analyseAntiLambda) histos.add("hAmbiguousLambdaCandidates", "hAmbiguousLambdaCandidates", kTH1D, {{1, 0, 1}}); + if (analyseLambda) { + histos.add("hMassLambda", "hMassLambda", kTH1D, {axisConfigurations.axisLambdaMass}); + histos.add("Lambda/hLambdasPerEvent", "hLambdasPerEvent", kTH1D, {{15,0,15}}); + } + if (analyseAntiLambda) { + histos.add("hMassAntiLambda", "hMassAntiLambda", kTH1D, {axisConfigurations.axisLambdaMass}); + histos.add("AntiLambda/hAntiLambdasPerEvent", "hAntiLambdasPerEvent", kTH1D, {{15,0,15}}); + }; + if (analyseLambda && analyseAntiLambda) { + histos.add("hAmbiguousLambdaCandidates", "hAmbiguousLambdaCandidates", kTH1D, {{1, 0, 1}}); + histos.add("hAmbiguousPerEvent", "hAmbiguousPerEvent", kTH1D, {{15,0,15}}); + } // QA histograms if requested if (doV0KinematicQA) { @@ -829,7 +846,7 @@ struct lambdajetpolarizationions { } } - // Check if doing the right thing in AP space please + // 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}); @@ -956,6 +973,16 @@ struct lambdajetpolarizationions { }; V0SelectionFlowCounter V0SelCounter{0, &histos}; + // Minimal helper to fill hSelectionJetTracks, mirroring V0SelectionFlowCounter. + // Reset once per track candidate, fill once per passed cut stage. + struct JetTrackSelectionFlowCounter { + int binValue = -1; // Same convention as V0: starts at -1, first fill goes to bin x=0 + HistogramRegistry* histos = nullptr; + void resetForNewTrack() { binValue = -1; } + void fill() { histos->fill(HIST("GeneralQA/hSelectionJetTracks"), ++binValue); } + }; + JetTrackSelectionFlowCounter JetTrackSelCounter{0, &histos}; + // Short inlined helper to simplify QA inline void fillEventSelectionQA(int bin, float centrality){ histos.fill(HIST("hEventSelection"), bin); @@ -1117,23 +1144,32 @@ struct lambdajetpolarizationions { if (pseudoJetCandidateTrackSelections.minITSnCls >= 0){ if (track.itsNCls() < pseudoJetCandidateTrackSelections.minITSnCls) return false; } + JetTrackSelCounter.fill(); // bin: ITS clusters (min) + if (track.tpcNClsCrossedRows() < pseudoJetCandidateTrackSelections.minNCrossedRowsTPC) return false; + JetTrackSelCounter.fill(); if (track.tpcChi2NCl() > pseudoJetCandidateTrackSelections.maxChi2TPC) return false; + JetTrackSelCounter.fill(); if (track.itsChi2NCl() > pseudoJetCandidateTrackSelections.maxChi2ITS) return false; + JetTrackSelCounter.fill(); // Kinematics: const float pt = track.pt(); if (pt < pseudoJetCandidateTrackSelections.minCandidatePt) return false; + JetTrackSelCounter.fill(); if (std::fabs(track.eta()) > pseudoJetCandidateTrackSelections.etaCut) return false; + 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(); // 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(); } return true; } @@ -1337,6 +1373,9 @@ 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" + // Require that tracks pass selection criteria if (!isCandidateForChargedPseudojetAccepted(track)) continue; @@ -1357,10 +1396,9 @@ struct lambdajetpolarizationions { if (fjParticles.size() < 1) return; auto const& leadingParticle = fjParticles[leadingParticleIdx]; - tableLeadParticles(collIdx, - leadingParticle.pt(), - leadingParticle.eta(), - leadingParticle.phi()); + if (leadingParticle.pt() > jetConfigurations.minLeadParticlePt){ // If not, leading particle is probably a bad proxy + tableLeadParticles(collIdx, leadingParticle.pt(), leadingParticle.eta(), leadingParticle.phi()); + } // Start jet clusterization: // Cluster particles using the anti-kt algorithm @@ -1429,7 +1467,7 @@ struct lambdajetpolarizationions { if (jetMinusBkg.pt() < jetConfigurations.minJetPt) continue; float cosTheta = cosThetaJets(leadingJetSub, jetMinusBkg); - float deltaPhi = leadingJetSub.phi() - jetMinusBkg.phi(); + float deltaPhi = RecoDecay::constrainAngle(leadingJetSub.phi() - jetMinusBkg.phi(), -o2::constants::math::PI); float deltaEta = leadingJetSub.eta() - jetMinusBkg.eta(); float deltaR = std::sqrt(deltaPhi*deltaPhi + deltaEta*deltaEta); @@ -1440,15 +1478,15 @@ struct lambdajetpolarizationions { // 2D correlations: histos.fill(HIST("JetKinematicsQA/h2dJetsPerEventvsLeadJetPt"), selectedJets, leadingJetSub.pt()); - histos.fill(HIST("JetKinematicsQA/h2dJetsPerEventvsJetPt"), selectedJets, jet.pt()); + histos.fill(HIST("JetKinematicsQA/h2dJetsPerEventvsJetPt"), selectedJets, jetMinusBkg.pt()); histos.fill(HIST("JetKinematicsQA/h2dCosThetaToLeadvsDeltaPhiToLead"), cosTheta, deltaPhi); histos.fill(HIST("JetKinematicsQA/h2dCosThetaToLeadvsDeltaEtaToLead"), cosTheta, deltaEta); histos.fill(HIST("JetKinematicsQA/h2dCosThetaToLeadvsDeltaRToLead"), cosTheta, deltaR); histos.fill(HIST("JetKinematicsQA/h2dDeltaPhiToLeadvsDeltaEtaToLead"), deltaPhi, deltaEta); - histos.fill(HIST("JetKinematicsQA/h2dJetPtvsDeltaPhiToLead"), jet.pt(), deltaPhi); // Can't really get the energy of the jet, just the pt to make this comparison - histos.fill(HIST("JetKinematicsQA/h2dJetEnergyvsDeltaPhiToLead"), jet.E(), deltaPhi); // Just a different scale - histos.fill(HIST("JetKinematicsQA/h2dJetEnergyvsCosThetaToLead"), jet.E(), cosTheta); + histos.fill(HIST("JetKinematicsQA/h2dJetPtvsDeltaPhiToLead"), jetMinusBkg.pt(), deltaPhi); // Can't really get the energy of the jet, just the pt to make this comparison + histos.fill(HIST("JetKinematicsQA/h2dJetEnergyvsDeltaPhiToLead"), jetMinusBkg.E(), deltaPhi); // Just a different scale + histos.fill(HIST("JetKinematicsQA/h2dJetEnergyvsCosThetaToLead"), jetMinusBkg.E(), cosTheta); histos.fill(HIST("JetKinematicsQA/h2dJetsPerEventvsDeltaPhiToLead"), selectedJets, deltaPhi); histos.fill(HIST("JetKinematicsQA/h2dJetsPerEventvsDeltaEtaToLead"), selectedJets, deltaEta); @@ -1459,7 +1497,7 @@ struct lambdajetpolarizationions { histos.fill(HIST("JetVsLeadingParticleQA/hLeadingParticleEta"), leadingParticle.eta()); histos.fill(HIST("JetVsLeadingParticleQA/hLeadingParticlePhi"), leadingParticle.phi()); - float deltaPhiParticleToJet = leadingJetSub.phi() - 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 @@ -1483,11 +1521,10 @@ struct lambdajetpolarizationions { histos.fill(HIST("JetVsLeadingParticleQA/h2dLeadParticlePtvsDeltaPhiParticleToLead"), leadingParticle.pt(), deltaPhiParticleToJet); } } - else{ // Otherwise, simple jet clustering + else{ // Otherwise, simple jet clustering (TODO: this is the fall back for kConstituentBased while not implemented) fastjet::ClusterSequence clustSeq(fjParticles, jetDef); // Jet pt must be larger than threshold: std::vector jets = fastjet::sorted_by_pt(clustSeq.inclusive_jets(jetConfigurations.minJetPt)); - // INCREMENT JET COUNT const int jetsInEvent = jets.size(); histos.fill(HIST("hJetsPerEvent"), jetsInEvent); // Fills even in empty events, as this is a useful number to know! @@ -1504,10 +1541,6 @@ struct lambdajetpolarizationions { const float jet_eta = jet.eta(); if (std::fabs(jet_eta) > (0.9f - jetConfigurations.radiusJet)) continue; - // Store jet: - // jets_pt.emplace_back(jet.pt()); - // jets_eta.emplace_back(jet.eta()); - // jets_phi.emplace_back(jet.phi()); // In the [0,2pi) range tableJets(collIdx, jet.pt(), jet_eta, // Using eta instead of rapidity @@ -1524,7 +1557,7 @@ struct lambdajetpolarizationions { float cosTheta = cosThetaJets(leadingJet, jet); // Calculate angular separation in projected angles: - float deltaPhi = leadingJet.phi() - jet.phi(); + float deltaPhi = RecoDecay::constrainAngle(leadingJet.phi() - jet.phi(), -o2::constants::math::PI); float deltaEta = leadingJet.eta() - jet_eta; float deltaR = std::sqrt(deltaPhi*deltaPhi + deltaEta*deltaEta); // 2D angular distance in the eta-phi plane @@ -1560,7 +1593,7 @@ struct lambdajetpolarizationions { histos.fill(HIST("JetVsLeadingParticleQA/hLeadingParticleEta"), leadingParticle.eta()); histos.fill(HIST("JetVsLeadingParticleQA/hLeadingParticlePhi"), leadingParticle.phi()); - double deltaPhiParticleToJet = leadingJet.phi() - 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 @@ -1588,7 +1621,8 @@ struct lambdajetpolarizationions { // Had to include DauTracks in subscription, even though I don't loop in it, for the indices // to resolve, avoiding " Exception while running: Index pointing to Tracks is not bound!" - void processV0sData(SelCollisions::iterator const& collision, V0CandidatesWithTOF const& fullV0s, aod::BCsWithTimestamps const& bcs, DauTracks const& V0DauTracks){ + // Added the compiler option [[maybe_unused]] to avoid triggering any warnings because of this + void processV0sData(SelCollisions::iterator const& collision, V0CandidatesWithTOF const& fullV0s, aod::BCsWithTimestamps const& bcs, [[maybe_unused]] DauTracks const& V0DauTracks){ 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 @@ -1616,6 +1650,9 @@ struct lambdajetpolarizationions { collision.centFV0A() ); // (TODO: add InteractionRate info and other useful cuts for later on in the analysis?) + uint NLambdas = 0; // Counting particles per event + uint NAntiLambdas = 0; + uint NAmbiguous = 0; for (auto const& v0 : fullV0s){ V0SelCounter.resetForNewV0(); V0SelCounter.fill(); // Fill for all v0 candidates @@ -1631,6 +1668,9 @@ struct lambdajetpolarizationions { if (analyseAntiLambda) isAntiLambda = passesLambdaLambdaBarHypothesis(v0, collision, false); if (!isLambda && !isAntiLambda) continue; // Candidate is not considered to be a Lambda + + if (isLambda) NLambdas++; + if (isAntiLambda) NAntiLambdas++; if (doArmenterosQA) histos.fill(HIST("GeneralQA/h2dArmenterosFullSelected"), v0.alpha(), v0.qtarm()); // cross-check if (isLambda && !isAntiLambda) histos.fill(HIST("GeneralQA/h2dArmenterosFullSelectedLambda"), v0.alpha(), v0.qtarm()); @@ -1638,6 +1678,7 @@ struct lambdajetpolarizationions { // int lambdaIdx = -1; // No need to pass armenteros if (isLambda && isAntiLambda) { + 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 @@ -1654,39 +1695,19 @@ struct lambdajetpolarizationions { // // Extra competing mass rejection of Lambdas // (TODO: test competing mass cuts) // v0.mLambda() - // Dealing with ambiguous tracks: // (TODO: for now, a simple QA plot to understand how many enter this stage is enough) - // Saving the Lambdas into a derived data column: auto const v0pt = v0.pt(); - // LOG(INFO) << "Filling tableV0s"; - // LOG(INFO) << collIdx; - // LOG(INFO) << v0pt; - // LOG(INFO) << v0.eta(); - // LOG(INFO) << v0.phi(); - // LOG(INFO) << isLambda; - // LOG(INFO) << isAntiLambda; - // LOG(INFO) << v0.mLambda(); - // LOG(INFO) << v0.mAntiLambda(); - // LOG(INFO) << v0.positivept(); - // LOG(INFO) << v0.positiveeta(); - // LOG(INFO) << v0.positivephi(); - // LOG(INFO) << v0.negativept(); - // LOG(INFO) << v0.negativeeta(); - // LOG(INFO) << v0.negativephi(); + const auto posTrackExtra = v0.template posTrack_as(); + const auto negTrackExtra = v0.template negTrack_as(); tableV0s(collIdx, - v0pt, - v0.eta(), // Using eta instead of rapidity - v0.phi(), - isLambda, - isAntiLambda, - v0.mLambda(), - v0.mAntiLambda(), - v0.positivept(), - v0.positiveeta(), - v0.positivephi(), - v0.negativept(), - v0.negativeeta(), - v0.negativephi() + v0pt, v0.eta(), v0.phi(), // Using eta instead of rapidity + isLambda, isAntiLambda, + v0.mLambda(), v0.mAntiLambda(), + v0.positivept(), v0.positiveeta(), v0.positivephi(), + v0.negativept(), v0.negativeeta(), v0.negativephi(), + v0.v0cosPA(), v0.v0radius(), v0.dcaV0daughters(), v0.dcapostopv(), v0.dcanegtopv(), + posTrackExtra.tpcNSigmaPr(), posTrackExtra.tpcNSigmaPi(), + negTrackExtra.tpcNSigmaPr(), negTrackExtra.tpcNSigmaPi() ); if (doEventQA && !validV0AlreadyFound) fillEventSelectionQA(lastBinEvSel, centrality); // hasRingV0 passes validV0AlreadyFound = true; @@ -1727,8 +1748,6 @@ struct lambdajetpolarizationions { if (doCompleteTopoQA){ // Remaking these variables outside of the passesLambdaLambdaBarHypothesis. Loses performance, but that should be OK for QA - const auto posTrackExtra = v0.template posTrack_as(); - const auto negTrackExtra = v0.template negTrack_as(); histos.fill(HIST("hPosDCAToPV"), v0.dcapostopv()); histos.fill(HIST("hNegDCAToPV"), v0.dcanegtopv()); histos.fill(HIST("hDCADaughters"), v0.dcaV0daughters()); @@ -1739,8 +1758,8 @@ struct lambdajetpolarizationions { histos.fill(HIST("h2dPositivePtVsPhi"), v0.positivept(), computePhiMod(v0.positivephi(), 1)); histos.fill(HIST("h2dNegativePtVsPhi"), v0.negativept(), computePhiMod(v0.negativephi(), -1)); if (isLambda && analyseLambda) { - histos.fill(HIST("h3dMassLambda"), centrality, v0pt, v0.mLambda()); histos.fill(HIST("hMassLambda"), v0.mLambda()); + histos.fill(HIST("Lambda/h3dMassLambda"), centrality, v0pt, v0.mLambda()); histos.fill(HIST("Lambda/hPosDCAToPV"), v0.dcapostopv()); histos.fill(HIST("Lambda/hNegDCAToPV"), v0.dcanegtopv()); histos.fill(HIST("Lambda/hDCADaughters"), v0.dcaV0daughters()); @@ -1785,9 +1804,8 @@ struct lambdajetpolarizationions { } } if (isAntiLambda && analyseAntiLambda) { - // histos.fill(HIST("h2dNbrOfAntiLambdaVsCentrality"), centrality, NbrAntiLambda); // (TODO: add the proper call to this fill) - histos.fill(HIST("h3dMassAntiLambda"), centrality, v0pt, v0.mAntiLambda()); histos.fill(HIST("hMassAntiLambda"), v0.mAntiLambda()); + histos.fill(HIST("AntiLambda/h3dMassAntiLambda"), centrality, v0pt, v0.mAntiLambda()); histos.fill(HIST("AntiLambda/hPosDCAToPV"), v0.dcapostopv()); histos.fill(HIST("AntiLambda/hNegDCAToPV"), v0.dcanegtopv()); histos.fill(HIST("AntiLambda/hDCADaughters"), v0.dcaV0daughters()); @@ -1833,6 +1851,13 @@ struct lambdajetpolarizationions { } } // end CompleteTopoQA } // 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); } PROCESS_SWITCH(lambdajetpolarizationions, processJetsData, "Process jets and produce derived data in Run 3 Data", true); diff --git a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx index 9eff95bed89..79733ef2bd0 100644 --- a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx @@ -189,7 +189,9 @@ struct lambdajetpolarizationionsderived { // 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 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 blocks: @@ -523,10 +525,43 @@ struct lambdajetpolarizationionsderived { // 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)) + // 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 displacemente in \theta becomes a large displacement in \eta space + double smearSigma = 0.05 * jetRForSmuding; + 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)) + // 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. + } } float subleadingJetEta = 0.; From 8a45f25f1e7688479251dd82dfd6ed3f2fb12a11 Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Tue, 3 Mar 2026 21:06:11 -0300 Subject: [PATCH 33/40] Removing unrelated code from older branch --- PWGLF/Tasks/Strangeness/lambdaInvMassTest.cxx | 1653 ----------------- 1 file changed, 1653 deletions(-) delete mode 100644 PWGLF/Tasks/Strangeness/lambdaInvMassTest.cxx diff --git a/PWGLF/Tasks/Strangeness/lambdaInvMassTest.cxx b/PWGLF/Tasks/Strangeness/lambdaInvMassTest.cxx deleted file mode 100644 index dfd033b466d..00000000000 --- a/PWGLF/Tasks/Strangeness/lambdaInvMassTest.cxx +++ /dev/null @@ -1,1653 +0,0 @@ -/// \file lambdaInvMassTest.cxx -/// \brief Lambda analysis task using derived data as a test -/// -/// \author Cicero Domenico Muncinelli , Campinas State University, Brazil -// -// Lambda Invariant Mass test task -// ================ -// -// This code loops over a V0Cores table and produces some -// standard analysis output. It is meant to be run over -// derived data. -// This is NOT meant to be in the ALICE O2 repository, -// ever! It is just a small test for me to get up to -// speed with O2 analyses! -// -// Comments, questions, complaints, suggestions? -// Please write to: -// cicero.domenico.muncinelli@cern.ch -// -// This code is heavily based on the derivedlambdakzeroanalysis.cxx code! - -#include "PWGLF/DataModel/LFStrangenessMLTables.h" -#include "PWGLF/DataModel/LFStrangenessPIDTables.h" -#include "PWGLF/DataModel/LFStrangenessTables.h" -#include "PWGUD/Core/SGSelector.h" - -#include "Common/CCDB/ctpRateFetcher.h" -#include "Common/Core/TrackSelection.h" -#include "Common/Core/trackUtilities.h" -#include "Common/DataModel/Centrality.h" -#include "Common/DataModel/EventSelection.h" -#include "Common/DataModel/Multiplicity.h" -#include "Common/DataModel/TrackSelectionTables.h" -#include "Tools/ML/MlResponse.h" -#include "Tools/ML/model.h" // This actually needs ONNX to be installed in the system! Or, at least, you should link the libraries properly in the CMakeLists.txt - -#include "CommonConstants/MathConstants.h" -#include "CommonConstants/PhysicsConstants.h" -#include "DataFormatsParameters/GRPMagField.h" -#include "Framework/ASoAHelpers.h" -#include "Framework/AnalysisDataModel.h" -#include "Framework/AnalysisTask.h" -#include "Framework/runDataProcessing.h" -#include "ReconstructionDataFormats/Track.h" - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -using namespace o2; -using namespace o2::framework; -using namespace o2::framework::expressions; -using std::array; - -using namespace o2::aod::rctsel; - -using DauTracks = soa::Join; -using DauMCTracks = soa::Join; -// using V0Candidates = soa::Join; -using V0Candidates = soa::Join; -// using V0McCandidates = soa::Join; -// using V0McCandidates = soa::Join; -using V0McCandidates = soa::Join; - -// simple checkers, but ensure 64 bit integers -#define BITSET(var, nbit) ((var) |= (static_cast(1) << static_cast(nbit))) -#define BITCHECK(var, nbit) ((var) & (static_cast(1) << static_cast(nbit))) - -enum CentEstimator { - kCentFT0C = 0, - kCentFT0M, - kCentFT0CVariant1, - kCentMFT, - kCentNGlobal, - kCentFV0A -}; - -// bool doPlainTopoQA = true; - -struct lambdaInvMassTest{ - HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - - bool isRun3 = true; - - // master analysis switches - Configurable analyseK0Short{"analyseK0Short", false, "process K0Short-like candidates"}; - Configurable analyseLambda{"analyseLambda", true, "process Lambda-like candidates"}; - Configurable analyseAntiLambda{"analyseAntiLambda", false, "process AntiLambda-like candidates"}; - Configurable calculateFeeddownMatrix{"calculateFeeddownMatrix", true, "fill feeddown matrix if MC"}; - - Configurable doPPAnalysis{"doPPAnalysis", true, "if in pp, set to true"}; - Configurable irSource{"irSource", "T0VTX", "Estimator of the interaction rate (Recommended: pp --> T0VTX, Pb-Pb --> ZNC hadronic)"}; - Configurable centralityEstimator{"centralityEstimator", kCentFT0C, "Run 3 centrality estimator (0:CentFT0C, 1:CentFT0M, 2:CentFT0CVariant1, 3:CentMFT, 4:CentNGlobal, 5:CentFV0A)"}; - - Configurable doEventQA{"doEventQA", false, "do event QA histograms"}; - Configurable doCompleteTopoQA{"doCompleteTopoQA", false, "do topological variable QA histograms"}; - Configurable doTPCQA{"doTPCQA", false, "do TPC QA histograms"}; - Configurable doTOFQA{"doTOFQA", false, "do TOF QA histograms"}; - Configurable doDetectPropQA{"doDetectPropQA", 0, "do Detector/ITS map QA: 0: no, 1: 4D, 2: 5D with mass; 3: plain in 3D"}; - Configurable doEtaPhiQA{"doEtaPhiQA", false, "do Eta/Phi QA histograms"}; - - Configurable doPlainTopoQA{"doPlainTopoQA", true, "do simple 1D QA of candidates"}; - Configurable qaMinPt{"qaMinPt", 0.0f, "minimum pT for QA plots"}; - Configurable qaMaxPt{"qaMaxPt", 1000.0f, "maximum pT for QA plots"}; - Configurable qaCentrality{"qaCentrality", false, "qa centrality flag: check base raw values"}; - - // for MC - Configurable doMCAssociation{"doMCAssociation", true, "if MC, do MC association"}; // Will not do MC, so can keep this on regardless - Configurable doTreatPiToMuon{"doTreatPiToMuon", false, "Take pi decay into muon into account in MC"}; - Configurable doCollisionAssociationQA{"doCollisionAssociationQA", true, "check collision association"}; - - // // Defining a configurable axis for easier manipulation later on: - // ConfigurableAxis axisPtQA{"axisPtQA", {VARIABLE_WIDTH, 0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, - // 0.6f, 0.7f, 0.8f, 0.9f, 1.0f, 1.1f, 1.2f, 1.3f, 1.4f, 1.5f, 1.6f, 1.7f, 1.8f, 1.9f, 2.0f, 2.2f, - // 2.4f, 2.6f, 2.8f, 3.0f, 3.2f, 3.4f, 3.6f, 3.8f, 4.0f, 4.4f, 4.8f, 5.2f, 5.6f, 6.0f, 6.5f, 7.0f, - // 7.5f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 17.0f, 19.0f, 21.0f, 23.0f, 25.0f, - // 30.0f, 35.0f, 40.0f, 50.0f}, "pt axis for QA histograms"}; - // // ConfigurableAxis axisInvMassLambda{"axisInvMassLambda", {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}, "Invariant mass axis for Lambda"}; - - // My own version of these axes: - // Configurable nBinsInvMass{"nBinsInvMass", 100, "Number of bins of invariant mass axis"}; - // Configurable minInvMass{"minInvMass", 0.7f, "Lower bound of invariant mass axis"}; - // Configurable maxInvMass{"maxInvMass", 1.3f, "Upper bound of invariant mass axis"}; - // From David's code: - // ConfigurableAxis axisLambdaMass{"axisLambdaMass", {200, 1.101f, 1.131f}, ""}; - - // Defining a configurable object group for the event selections: - struct : ConfigurableGroup { - std::string prefix = "eventSelections"; // JSON group name - Configurable requireSel8{"requireSel8", true, "require sel8 event selection"}; - Configurable requireTriggerTVX{"requireTriggerTVX", true, "require FT0 vertex (acceptable FT0C-FT0A time difference) at trigger level"}; - Configurable rejectITSROFBorder{"rejectITSROFBorder", true, "reject events at ITS ROF border (Run 3 only)"}; - Configurable rejectTFBorder{"rejectTFBorder", true, "reject events at TF border (Run 3 only)"}; - 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)"}; - 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 rejectSameBunchPileup{"rejectSameBunchPileup", true, "reject collisions in case of pileup with another collision in the same foundBC (Run 3 only)"}; - 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)"}; - Configurable requireINEL0{"requireINEL0", true, "require INEL>0 event selection"}; - Configurable requireINEL1{"requireINEL1", false, "require INEL>1 event selection"}; - - Configurable maxZVtxPosition{"maxZVtxPosition", 10., "max Z vtx position"}; - - Configurable useEvtSelInDenomEff{"useEvtSelInDenomEff", false, "Consider event selections in the recoed <-> gen collision association for the denominator (or numerator) of the acc. x eff. (or signal loss)?"}; - Configurable applyZVtxSelOnMCPV{"applyZVtxSelOnMCPV", false, "Apply Z-vtx cut on the PV of the generated collision?"}; - Configurable useFT0CbasedOccupancy{"useFT0CbasedOccupancy", false, "Use sum of FT0-C amplitudes for estimating occupancy? (if not, use track-based definition)"}; - // 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 - Configurable minIR{"minIR", -1, "minimum IR collisions"}; - Configurable maxIR{"maxIR", -1, "maximum IR collisions"}; - - // Run 2 specific event selections - Configurable requireSel7{"requireSel7", true, "require sel7 event selection (Run 2 only: event selection decision based on V0A & V0C)"}; - Configurable requireINT7{"requireINT7", true, "require INT7 trigger selection (Run 2 only)"}; - Configurable rejectIncompleteDAQ{"rejectIncompleteDAQ", true, "reject events with incomplete DAQ (Run 2 only)"}; - Configurable requireConsistentSPDAndTrackVtx{"requireConsistentSPDAndTrackVtx", true, "reject events with inconsistent in SPD and Track vertices (Run 2 only)"}; - Configurable rejectPileupFromSPD{"rejectPileupFromSPD", true, "reject events with pileup according to SPD vertexer (Run 2 only)"}; - Configurable rejectV0PFPileup{"rejectV0PFPileup", false, "reject events tagged as OOB pileup according to V0 past-future info (Run 2 only)"}; - Configurable rejectPileupInMultBins{"rejectPileupInMultBins", true, "reject events tagged as pileup according to multiplicity-differential pileup checks (Run 2 only)"}; - Configurable rejectPileupMV{"rejectPileupMV", true, "reject events tagged as pileup according to according to multi-vertexer (Run 2 only)"}; - Configurable rejectTPCPileup{"rejectTPCPileup", false, "reject events tagged as pileup according to pileup in TPC (Run 2 only)"}; - Configurable requireNoV0MOnVsOffPileup{"requireNoV0MOnVsOffPileup", false, "reject events tagged as OOB pileup according to online-vs-offline VOM correlation (Run 2 only)"}; - Configurable requireNoSPDOnVsOffPileup{"requireNoSPDOnVsOffPileup", false, "reject events tagged as pileup according to online-vs-offline SPD correlation (Run 2 only)"}; - Configurable requireNoSPDClsVsTklBG{"requireNoSPDClsVsTklBG", true, "reject events tagged as beam-gas and pileup according to cluster-vs-tracklet correlation (Run 2 only)"}; - - Configurable useSPDTrackletsCent{"useSPDTrackletsCent", false, "Use SPD tracklets for estimating centrality? If not, use V0M-based centrality (Run 2 only)"}; - } eventSelections; - - static constexpr float DefaultLifetimeCuts[1][2] = {{30., 20.}}; - - // Defining the configurable object that is going to be used: v0Selections, for selections later on - struct : ConfigurableGroup { - std::string prefix = "v0Selections"; // JSON group name - Configurable v0TypeSelection{"v0TypeSelection", 1, "select on a certain V0 type (leave negative if no selection desired)"}; - - // Selection criteria: acceptance - Configurable rapidityCut{"rapidityCut", 0.5, "rapidity"}; - Configurable daughterEtaCut{"daughterEtaCut", 0.8, "max eta for daughters"}; - - // Standard 5 topological criteria - Configurable v0cospa{"v0cospa", 0.97, "min V0 CosPA"}; - Configurable dcav0dau{"dcav0dau", 1.0, "max DCA V0 Daughters (cm)"}; - Configurable dcanegtopv{"dcanegtopv", .05, "min DCA Neg To PV (cm)"}; - Configurable dcapostopv{"dcapostopv", .05, "min DCA Pos To PV (cm)"}; - Configurable v0radius{"v0radius", 1.2, "minimum V0 radius (cm)"}; - Configurable v0radiusMax{"v0radiusMax", 1E5, "maximum V0 radius (cm)"}; - Configurable> lifetimecut{"lifetimecut", {DefaultLifetimeCuts[0], 2, {"lifetimecutLambda", "lifetimecutK0S"}}, "lifetimecut"}; - - // // Additional selection on the AP plot (exclusive for K0Short) - // // original equation: lArmPt*5>TMath::Abs(lArmAlpha) - // Configurable armPodCut{"armPodCut", 5.0f, "pT * (cut) > |alpha|, AP cut. Negative: no cut"}; - - // Track quality - Configurable minTPCrows{"minTPCrows", 70, "minimum TPC crossed rows"}; - Configurable minITSclusters{"minITSclusters", -1, "minimum ITS clusters"}; - Configurable minTPCrowsOverFindableClusters{"minTPCrowsOverFindableClusters", -1, "minimum nbr of TPC crossed rows over findable clusters"}; - Configurable minTPCfoundOverFindableClusters{"minTPCfoundOverFindableClusters", -1, "minimum nbr of found over findable TPC clusters"}; - Configurable maxFractionTPCSharedClusters{"maxFractionTPCSharedClusters", 1e+09, "maximum fraction of TPC shared clusters"}; - Configurable maxITSchi2PerNcls{"maxITSchi2PerNcls", 1e+09, "maximum ITS chi2 per clusters"}; - Configurable maxTPCchi2PerNcls{"maxTPCchi2PerNcls", 1e+09, "maximum TPC chi2 per clusters"}; - Configurable skipTPConly{"skipTPConly", false, "skip V0s comprised of at least one TPC only prong"}; - Configurable requirePosITSonly{"requirePosITSonly", false, "require that positive track is ITSonly (overrides TPC quality)"}; - Configurable requireNegITSonly{"requireNegITSonly", false, "require that negative track is ITSonly (overrides TPC quality)"}; - Configurable rejectPosITSafterburner{"rejectPosITSafterburner", false, "reject positive track formed out of afterburner ITS tracks"}; - Configurable rejectNegITSafterburner{"rejectNegITSafterburner", false, "reject negative track formed out of afterburner ITS tracks"}; - Configurable requirePosITSafterburnerOnly{"requirePosITSafterburnerOnly", false, "require positive track formed out of afterburner ITS tracks"}; - Configurable requireNegITSafterburnerOnly{"requireNegITSafterburnerOnly", false, "require negative track formed out of afterburner ITS tracks"}; - Configurable rejectTPCsectorBoundary{"rejectTPCsectorBoundary", false, "reject tracks close to the TPC sector boundaries"}; - Configurable phiLowCut{"phiLowCut", "0.06/x+pi/18.0-0.06", "Low azimuth cut parametrisation"}; - Configurable phiHighCut{"phiHighCut", "0.1/x+pi/18.0+0.06", "High azimuth cut parametrisation"}; - - // PID (TPC/TOF) - Configurable tpcPidNsigmaCut{"tpcPidNsigmaCut", 5, "tpcPidNsigmaCut"}; - Configurable tofPidNsigmaCutLaPr{"tofPidNsigmaCutLaPr", 1e+6, "tofPidNsigmaCutLaPr"}; - Configurable tofPidNsigmaCutLaPi{"tofPidNsigmaCutLaPi", 1e+6, "tofPidNsigmaCutLaPi"}; - Configurable tofPidNsigmaCutK0Pi{"tofPidNsigmaCutK0Pi", 1e+6, "tofPidNsigmaCutK0Pi"}; - - // PID (TOF) - Configurable maxDeltaTimeProton{"maxDeltaTimeProton", 1e+9, "check maximum allowed time"}; - Configurable maxDeltaTimePion{"maxDeltaTimePion", 1e+9, "check maximum allowed time"}; - } v0Selections; - - TF1* fPhiCutLow = new TF1("fPhiCutLow", v0Selections.phiLowCut.value.data(), 0, 100); - TF1* fPhiCutHigh = new TF1("fPhiCutHigh", v0Selections.phiHighCut.value.data(), 0, 100); - - 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 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; - - RCTFlagsChecker rctFlagsChecker{rctConfigurations.cfgRCTLabel.value}; - - // Machine learning evaluation for pre-selection and corresponding information generation - o2::ml::OnnxModel mlCustomModelK0Short; - o2::ml::OnnxModel mlCustomModelLambda; - o2::ml::OnnxModel mlCustomModelAntiLambda; - o2::ml::OnnxModel mlCustomModelGamma; - - struct : ConfigurableGroup { // Kept the original configurable scores for K0Short and all else due to the line "if (lambdaScore > mlConfigurations.thresholdK0Short.value) (...)" - std::string prefix = "mlConfigurations"; // JSON group name - // ML classifiers: master flags to control whether we should use custom ML classifiers or the scores in the derived data - Configurable useK0ShortScores{"useK0ShortScores", false, "use ML scores to select K0Short"}; - Configurable useLambdaScores{"useLambdaScores", false, "use ML scores to select Lambda"}; - Configurable useAntiLambdaScores{"useAntiLambdaScores", false, "use ML scores to select AntiLambda"}; - - Configurable calculateK0ShortScores{"calculateK0ShortScores", false, "calculate K0Short ML scores"}; - Configurable calculateLambdaScores{"calculateLambdaScores", false, "calculate Lambda ML scores"}; - Configurable calculateAntiLambdaScores{"calculateAntiLambdaScores", false, "calculate AntiLambda ML scores"}; - - // ML input for ML calculation - Configurable customModelPathCCDB{"customModelPathCCDB", "", "Custom ML Model path in CCDB"}; - Configurable timestampCCDB{"timestampCCDB", -1, "timestamp of the ONNX file for ML model used to query in CCDB. Exceptions: > 0 for the specific timestamp, 0 gets the run dependent timestamp"}; - Configurable loadCustomModelsFromCCDB{"loadCustomModelsFromCCDB", false, "Flag to enable or disable the loading of custom models from CCDB"}; - Configurable enableOptimizations{"enableOptimizations", false, "Enables the ONNX extended model-optimization: sessionOptions.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_EXTENDED)"}; - - // Local paths for test purposes - Configurable localModelPathLambda{"localModelPathLambda", "Lambda_BDTModel.onnx", "(std::string) Path to the local .onnx file."}; - Configurable localModelPathAntiLambda{"localModelPathAntiLambda", "AntiLambda_BDTModel.onnx", "(std::string) Path to the local .onnx file."}; - Configurable localModelPathK0Short{"localModelPathK0Short", "KZeroShort_BDTModel.onnx", "(std::string) Path to the local .onnx file."}; - - // Thresholds for choosing to populate V0Cores tables with pre-selections - Configurable thresholdLambda{"thresholdLambda", -1.0f, "Threshold to keep Lambda candidates"}; - Configurable thresholdAntiLambda{"thresholdAntiLambda", -1.0f, "Threshold to keep AntiLambda candidates"}; - Configurable thresholdK0Short{"thresholdK0Short", -1.0f, "Threshold to keep K0Short candidates"}; - } mlConfigurations; - - // CCDB options - struct : ConfigurableGroup { - std::string prefix = "ccdbConfigurations"; // JSON group name - Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; - Configurable grpPath{"grpPath", "GLO/GRP/GRP", "Path of the grp file"}; - Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; - Configurable lutPath{"lutPath", "GLO/Param/MatLUT", "Path of the Lut parametrization"}; - Configurable geoPath{"geoPath", "GLO/Config/GeometryAligned", "Path of the geometry file"}; - Configurable mVtxPath{"mVtxPath", "GLO/Calib/MeanVertex", "Path of the mean vertex file"}; - - // manual - Configurable useCustomMagField{"useCustomMagField", false, "Use custom magnetic field value"}; - Configurable customMagField{"customMagField", 5.0f, "Manually set magnetic field"}; - } ccdbConfigurations; - - o2::ccdb::CcdbApi ccdbApi; - Service ccdb; - ctpRateFetcher rateFetcher; - int mRunNumber; - float magField; - std::map metadata; - o2::parameters::GRPMagField* grpmag = nullptr; - - // CCDB options - struct : ConfigurableGroup { - 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 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 axisK0Mass{"axisK0Mass", {200, 0.4f, 0.6f}, ""}; - ConfigurableAxis axisLambdaMass{"axisLambdaMass", {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)"}; - 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)"}; - ConfigurableAxis axisDCAdau{"axisDCAdau", {20, 0.0f, 2.0f}, "DCA (cm)"}; - 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"}; - ConfigurableAxis axisTPCsignal{"axisTPCsignal", {200, 0.0f, 200.0f}, "TPC signal"}; - ConfigurableAxis axisNsigmaTOF{"axisNsigmaTOF", {200, -10.0f, 10.0f}, "N sigma TOF"}; - ConfigurableAxis axisTOFdeltaT{"axisTOFdeltaT", {200, -5000.0f, 5000.0f}, "TOF Delta T (ps)"}; - ConfigurableAxis axisPhi{"axisPhi", {18, 0.0f, constants::math::TwoPI}, "Azimuth angle (rad)"}; - ConfigurableAxis axisPhiMod{"axisPhiMod", {100, 0.0f, constants::math::TwoPI / 18}, "Azimuth angle wrt TPC sector (rad.)"}; - ConfigurableAxis axisEta{"axisEta", {10, -1.0f, 1.0f}, "#eta"}; - 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"}; - - // UPC axes - ConfigurableAxis axisSelGap{"axisSelGap", {4, -1.5, 2.5}, "Gap side"}; - - // 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"}; - - // MC coll assoc QA axis - ConfigurableAxis axisMonteCarloNch{"axisMonteCarloNch", {300, 0.0f, 3000.0f}, "N_{ch} MC"}; - } axisConfigurations; - - // These will not be used, but the variables were needed for some cut David did: - // UPC selections - SGSelector sgSelector; - struct : ConfigurableGroup { - std::string prefix = "upcCuts"; // JSON group name - Configurable fv0Cut{"fv0Cut", 100., "FV0A threshold"}; - Configurable ft0Acut{"ft0Acut", 200., "FT0A threshold"}; - Configurable ft0Ccut{"ft0Ccut", 100., "FT0C threshold"}; - Configurable zdcCut{"zdcCut", 10., "ZDC threshold"}; - // Configurable gapSel{"gapSel", 2, "Gap selection"}; - } upcCuts; - - - // For manual sliceBy - // Preslice> perMcCollision = aod::v0data::straMCCollisionId; - PresliceUnsorted> perMcCollision = aod::v0data::straMCCollisionId; - PresliceUnsorted> perMcCollisionRun2 = aod::v0data::straMCCollisionId; - - enum Selection : uint64_t { selCosPA = 0, - selRadius, - selRadiusMax, - selDCANegToPV, - selDCAPosToPV, - selDCAV0Dau, - selK0ShortRapidity, - selLambdaRapidity, - selTPCPIDPositivePion, - selTPCPIDNegativePion, - selTPCPIDPositiveProton, - selTPCPIDNegativeProton, - selTOFDeltaTPositiveProtonLambda, - selTOFDeltaTPositivePionLambda, - selTOFDeltaTPositivePionK0Short, - selTOFDeltaTNegativeProtonLambda, - selTOFDeltaTNegativePionLambda, - selTOFDeltaTNegativePionK0Short, - selTOFNSigmaPositiveProtonLambda, // Nsigma - selTOFNSigmaPositivePionLambda, // Nsigma - selTOFNSigmaPositivePionK0Short, // Nsigma - selTOFNSigmaNegativeProtonLambda, // Nsigma - selTOFNSigmaNegativePionLambda, // Nsigma - selTOFNSigmaNegativePionK0Short, // Nsigma - selK0ShortCTau, - selLambdaCTau, - selK0ShortArmenteros, - selPosGoodTPCTrack, // at least min # TPC rows - selNegGoodTPCTrack, // at least min # TPC rows - selPosGoodITSTrack, // at least min # ITS clusters - selNegGoodITSTrack, // at least min # ITS clusters - selPosItsOnly, - selNegItsOnly, - selPosNotTPCOnly, - selNegNotTPCOnly, - selConsiderK0Short, // for mc tagging - selConsiderLambda, // for mc tagging - selConsiderAntiLambda, // for mc tagging - selPhysPrimK0Short, // for mc tagging - selPhysPrimLambda, // for mc tagging - selPhysPrimAntiLambda, // for mc tagging - }; - - uint64_t maskTopological; - uint64_t maskTopoNoV0Radius; - uint64_t maskTopoNoDCANegToPV; - uint64_t maskTopoNoDCAPosToPV; - uint64_t maskTopoNoCosPA; - uint64_t maskTopoNoDCAV0Dau; - uint64_t maskTrackProperties; - - uint64_t maskK0ShortSpecific; - uint64_t maskLambdaSpecific; - uint64_t maskAntiLambdaSpecific; - - uint64_t maskSelectionK0Short; - uint64_t maskSelectionLambda; - uint64_t maskSelectionAntiLambda; - - uint64_t secondaryMaskSelectionLambda; - uint64_t secondaryMaskSelectionAntiLambda; - - - void init(InitContext const&){ - // const AxisSpec axisCounter{1, 0, +1, ""}; - // // invMassAx = AxisSpec{nBinsInvMass, minInvMass, maxInvMass}; - - // histos.add("eventCounter", "eventCounter", kTH1F, {axisCounter}); - // // histos.add("LambdaInvMass1D", "Test LambdaInvMass 1D", kTH1F, {axisLambdaMass}); - histos.add("hMassLambda", "hMassLambda", kTH1D, {axisConfigurations.axisLambdaMass}); - // histos.add("ptQAHist", "ptQAHist", kTH1F, {axisPtQA}); - - - /////////////////////////////////////////////////////////// - // Event Counters - histos.add("hEventSelection", "hEventSelection", kTH1D, {{21, -0.5f, +20.5f}}); - if (isRun3) { - 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"); - if (doPPAnalysis) { - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(17, "INEL>0"); - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(18, "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(19, "Below min IR"); - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(20, "Above max IR"); - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(21, "RCT flags"); - } else { - 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, "sel7 cut"); - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(4, "kINT7"); - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(5, "kIsTriggerTVX"); - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(6, "kNoIncompleteDAQ"); - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(7, "posZ cut"); - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(8, "kNoInconsistentVtx"); - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(9, "kNoPileupFromSPD"); - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(10, "kNoV0PFPileup"); - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(11, "kNoPileupInMultBins"); - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(12, "kNoPileupMV"); - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(13, "kNoPileupTPC"); - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(14, "kNoV0MOnVsOfPileup"); - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(15, "kNoSPDOnVsOfPileup"); - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(16, "kNoSPDClsVsTklBG"); - if (doPPAnalysis) { - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(17, "INEL>0"); - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(18, "INEL>1"); - } - } - - histos.add("hEventCentrality", "hEventCentrality", kTH1D, {{101, 0.0f, 101.0f}}); - histos.add("hCentralityVsNch", "hCentralityVsNch", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisNch}); - if (doEventQA) { - if (isRun3) { - histos.add("hEventSelectionVsCentrality", "hEventSelectionVsCentrality", kTH2D, {{21, -0.5f, +20.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"); - if (doPPAnalysis) { - histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(17, "INEL>0"); - histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(18, "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(19, "Below min IR"); - histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(20, "Above max IR"); - histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(21, "RCT flags"); - - histos.add("hCentralityVsNGlobal", "hCentralityVsNGlobal", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisNch}); - histos.add("hEventCentVsMultFT0M", "hEventCentVsMultFT0M", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisMultFT0M}); - histos.add("hEventCentVsMultFT0C", "hEventCentVsMultFT0C", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisMultFT0C}); - histos.add("hEventCentVsMultNGlobal", "hEventCentVsMultNGlobal", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisNch}); - histos.add("hEventCentVsMultFV0A", "hEventCentVsMultFV0A", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisMultFV0A}); - histos.add("hEventMultFT0MvsMultNGlobal", "hEventMultFT0MvsMultNGlobal", kTH2D, {axisConfigurations.axisMultFT0M, axisConfigurations.axisNch}); - histos.add("hEventMultFT0CvsMultNGlobal", "hEventMultFT0CvsMultNGlobal", kTH2D, {axisConfigurations.axisMultFT0C, axisConfigurations.axisNch}); - histos.add("hEventMultFV0AvsMultNGlobal", "hEventMultFV0AvsMultNGlobal", kTH2D, {axisConfigurations.axisMultFV0A, axisConfigurations.axisNch}); - histos.add("hEventMultPVvsMultNGlobal", "hEventMultPVvsMultNGlobal", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisNch}); - histos.add("hEventMultFT0CvsMultFV0A", "hEventMultFT0CvsMultFV0A", 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}}); - if (isRun3) { - histos.add("hEventPVzMC", "hEventPVzMC", kTH1D, {{100, -20.0f, +20.0f}}); - histos.add("hCentralityVsPVzMC", "hCentralityVsPVzMC", kTH2D, {{101, 0.0f, 101.0f}, {100, -20.0f, +20.0f}}); - } - - histos.add("hEventOccupancy", "hEventOccupancy", kTH1D, {axisConfigurations.axisOccupancy}); - histos.add("hCentralityVsOccupancy", "hCentralityVsOccupancy", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisOccupancy}); - - histos.add("hGapSide", "Gap side; Entries", kTH1D, {{5, -0.5, 4.5}}); - histos.add("hSelGapSide", "Selected gap side; Entries", kTH1D, {axisConfigurations.axisSelGap}); - histos.add("hEventCentralityVsSelGapSide", ";Centrality (%); Selected gap side", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisSelGap}); - - 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}); - - // for QA and test purposes - auto hRawCentrality = histos.add("hRawCentrality", "hRawCentrality", kTH1D, {axisConfigurations.axisRawCentrality}); - - for (int ii = 1; ii < 101; ii++) { - float value = 100.5f - static_cast(ii); - hRawCentrality->SetBinContent(ii, value); - } - - auto hSelectionV0s = histos.add("GeneralQA/hSelectionV0s", "hSelectionV0s", kTH1D, {{static_cast(selPhysPrimAntiLambda) + 3, -0.5f, static_cast(selPhysPrimAntiLambda) + 2.5f}}); - hSelectionV0s->GetXaxis()->SetBinLabel(1, "All"); - hSelectionV0s->GetXaxis()->SetBinLabel(selCosPA + 2, "cosPA"); - hSelectionV0s->GetXaxis()->SetBinLabel(selRadius + 2, "Radius min."); - hSelectionV0s->GetXaxis()->SetBinLabel(selRadiusMax + 2, "Radius max."); - hSelectionV0s->GetXaxis()->SetBinLabel(selDCANegToPV + 2, "DCA neg. to PV"); - hSelectionV0s->GetXaxis()->SetBinLabel(selDCAPosToPV + 2, "DCA pos. to PV"); - hSelectionV0s->GetXaxis()->SetBinLabel(selDCAV0Dau + 2, "DCA V0 dau."); - hSelectionV0s->GetXaxis()->SetBinLabel(selK0ShortRapidity + 2, "K^{0}_{S} rapidity"); - hSelectionV0s->GetXaxis()->SetBinLabel(selLambdaRapidity + 2, "#Lambda rapidity"); - hSelectionV0s->GetXaxis()->SetBinLabel(selTPCPIDPositivePion + 2, "TPC PID #pi^{+}"); - hSelectionV0s->GetXaxis()->SetBinLabel(selTPCPIDNegativePion + 2, "TPC PID #pi^{-}"); - hSelectionV0s->GetXaxis()->SetBinLabel(selTPCPIDPositiveProton + 2, "TPC PID p"); - hSelectionV0s->GetXaxis()->SetBinLabel(selTPCPIDNegativeProton + 2, "TPC PID #bar{p}"); - hSelectionV0s->GetXaxis()->SetBinLabel(selTOFDeltaTPositiveProtonLambda + 2, "TOF #Delta t p from #Lambda"); - hSelectionV0s->GetXaxis()->SetBinLabel(selTOFDeltaTPositivePionLambda + 2, "TOF #Delta t #pi^{+} from #Lambda"); - hSelectionV0s->GetXaxis()->SetBinLabel(selTOFDeltaTPositivePionK0Short + 2, "TOF #Delta t #pi^{+} from K^{0}_{S}"); - hSelectionV0s->GetXaxis()->SetBinLabel(selTOFDeltaTNegativeProtonLambda + 2, "TOF #Delta t #bar{p} from #Lambda"); - hSelectionV0s->GetXaxis()->SetBinLabel(selTOFDeltaTNegativePionLambda + 2, "TOF #Delta t #pi^{-} from #Lambda"); - hSelectionV0s->GetXaxis()->SetBinLabel(selTOFDeltaTNegativePionK0Short + 2, "TOF #Delta t #pi^{-} from K^{0}_{S}"); - hSelectionV0s->GetXaxis()->SetBinLabel(selTOFNSigmaPositiveProtonLambda + 2, "TOF PID p from #Lambda"); - hSelectionV0s->GetXaxis()->SetBinLabel(selTOFNSigmaPositivePionLambda + 2, "TOF PID #pi^{+} from #Lambda"); - hSelectionV0s->GetXaxis()->SetBinLabel(selTOFNSigmaPositivePionK0Short + 2, "TOF PID #pi^{+} from K^{0}_{S}"); - hSelectionV0s->GetXaxis()->SetBinLabel(selTOFNSigmaNegativeProtonLambda + 2, "TOF PID #bar{p} from #Lambda"); - hSelectionV0s->GetXaxis()->SetBinLabel(selTOFNSigmaNegativePionLambda + 2, "TOF PID #pi^{-} from #Lambda"); - hSelectionV0s->GetXaxis()->SetBinLabel(selTOFNSigmaNegativePionK0Short + 2, "TOF PID #pi^{-} from K^{0}_{S}"); - hSelectionV0s->GetXaxis()->SetBinLabel(selK0ShortCTau + 2, "K^{0}_{S} lifetime"); - hSelectionV0s->GetXaxis()->SetBinLabel(selLambdaCTau + 2, "#Lambda lifetime"); - hSelectionV0s->GetXaxis()->SetBinLabel(selK0ShortArmenteros + 2, "Arm. pod. cut"); - hSelectionV0s->GetXaxis()->SetBinLabel(selPosGoodTPCTrack + 2, "Pos. good TPC track"); - hSelectionV0s->GetXaxis()->SetBinLabel(selNegGoodTPCTrack + 2, "Neg. good TPC track"); - hSelectionV0s->GetXaxis()->SetBinLabel(selPosGoodITSTrack + 2, "Pos. good ITS track"); - hSelectionV0s->GetXaxis()->SetBinLabel(selNegGoodITSTrack + 2, "Neg. good ITS track"); - hSelectionV0s->GetXaxis()->SetBinLabel(selPosItsOnly + 2, "Pos. ITS-only"); - hSelectionV0s->GetXaxis()->SetBinLabel(selNegItsOnly + 2, "Neg. ITS-only"); - hSelectionV0s->GetXaxis()->SetBinLabel(selPosNotTPCOnly + 2, "Pos. not TPC-only"); - hSelectionV0s->GetXaxis()->SetBinLabel(selNegNotTPCOnly + 2, "Neg. not TPC-only"); - hSelectionV0s->GetXaxis()->SetBinLabel(selConsiderK0Short + 2, "True K^{0}_{S}"); - hSelectionV0s->GetXaxis()->SetBinLabel(selConsiderLambda + 2, "True #Lambda"); - hSelectionV0s->GetXaxis()->SetBinLabel(selConsiderAntiLambda + 2, "True #bar{#Lambda}"); - hSelectionV0s->GetXaxis()->SetBinLabel(selPhysPrimK0Short + 2, "Phys. prim. K^{0}_{S}"); - hSelectionV0s->GetXaxis()->SetBinLabel(selPhysPrimLambda + 2, "Phys. prim. #Lambda"); - hSelectionV0s->GetXaxis()->SetBinLabel(selPhysPrimAntiLambda + 2, "Phys. prim. #bar{#Lambda}"); - hSelectionV0s->GetXaxis()->SetBinLabel(selPhysPrimAntiLambda + 3, "Cand. selected"); - /////////////////////////////////////////////////////////// - - // From the analyseLambda flag: - histos.add("h2dNbrOfLambdaVsCentrality", "h2dNbrOfLambdaVsCentrality", kTH2D, {axisConfigurations.axisCentrality, {10, -0.5f, 9.5f}}); - histos.add("h3dMassLambda", "h3dMassLambda", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisLambdaMass}); - // Non-UPC info - histos.add("h3dMassLambdaHadronic", "h3dMassLambdaHadronic", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisLambdaMass}); - // Not doing ultra-peripheral! - // // UPC info - // histos.add("h3dMassLambdaSGA", "h3dMassLambdaSGA", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisLambdaMass}); - // histos.add("h3dMassLambdaSGC", "h3dMassLambdaSGC", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisLambdaMass}); - // histos.add("h3dMassLambdaDG", "h3dMassLambdaDG", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisLambdaMass}); - - // // For the doCompleteTopoQA analysis: - // 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}); - - // For the doPlainTopoQA analysis: - // All candidates received - histos.add("hPosDCAToPV", "hPosDCAToPV", kTH1D, {axisConfigurations.axisDCAtoPV}); - histos.add("hNegDCAToPV", "hNegDCAToPV", kTH1D, {axisConfigurations.axisDCAtoPV}); - histos.add("hDCADaughters", "hDCADaughters", kTH1D, {axisConfigurations.axisDCAdau}); - histos.add("hPointingAngle", "hPointingAngle", kTH1D, {axisConfigurations.axisPointingAngle}); - histos.add("hV0Radius", "hV0Radius", kTH1D, {axisConfigurations.axisV0Radius}); - histos.add("h2dPositiveITSvsTPCpts", "h2dPositiveITSvsTPCpts", kTH2D, {axisConfigurations.axisTPCrows, axisConfigurations.axisITSclus}); - histos.add("h2dNegativeITSvsTPCpts", "h2dNegativeITSvsTPCpts", kTH2D, {axisConfigurations.axisTPCrows, axisConfigurations.axisITSclus}); - histos.add("h2dPositivePtVsPhi", "h2dPositivePtVsPhi", kTH2D, {axisConfigurations.axisPtCoarse, axisConfigurations.axisPhiMod}); - histos.add("h2dNegativePtVsPhi", "h2dNegativePtVsPhi", kTH2D, {axisConfigurations.axisPtCoarse, axisConfigurations.axisPhiMod}); - - // Specifically for Lambda: - 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}); - - // Check if doing the right thing in AP space please - histos.add("GeneralQA/h2dArmenterosAll", "h2dArmenterosAll", kTH2D, {axisConfigurations.axisAPAlpha, axisConfigurations.axisAPQt}); - histos.add("GeneralQA/h2dArmenterosSelected", "h2dArmenterosSelected", kTH2D, {axisConfigurations.axisAPAlpha, axisConfigurations.axisAPQt}); - } - - template - void initCCDB(TCollision collision) - { - if (mRunNumber == collision.runNumber()) { - return; - } - - mRunNumber = collision.runNumber(); - - // machine learning initialization if requested - if (mlConfigurations.calculateLambdaScores) { - int64_t timeStampML = collision.timestamp(); - if (mlConfigurations.timestampCCDB.value != -1) - timeStampML = mlConfigurations.timestampCCDB.value; - loadMachines(timeStampML); - } - // Fetching magnetic field if requested - if (v0Selections.rejectTPCsectorBoundary) { - // In case override, don't proceed, please - no CCDB access required - if (ccdbConfigurations.useCustomMagField) { - magField = ccdbConfigurations.customMagField; - } else { - grpmag = ccdb->getForRun(ccdbConfigurations.grpmagPath, mRunNumber); - if (!grpmag) { - LOG(fatal) << "Got nullptr from CCDB for path " << ccdbConfigurations.grpmagPath << " of object GRPMagField and " << ccdbConfigurations.grpPath << " of object GRPObject for run " << mRunNumber; - } - // Fetch magnetic field from ccdb for current collision - magField = std::lround(5.f * grpmag->getL3Current() / 30000.f); - LOG(info) << "Retrieved GRP for run " << mRunNumber << " with magnetic field of " << magField << " kZG"; - } - } - } - - 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 - { - if (magField < 0) // for negative polarity field - phi = o2::constants::math::TwoPI - phi; - if (sign < 0) // for negative charge - phi = o2::constants::math::TwoPI - phi; - if (phi < 0) - LOGF(warning, "phi < 0: %g", phi); - - phi += o2::constants::math::PI / 18.0; // to center gap in the middle - return fmod(phi, o2::constants::math::PI / 9.0); - } - - bool isTrackFarFromTPCBoundary(double trackPt, double trackPhi, int sign) - // check whether the track passes close to a TPC sector boundary - { - double phiModn = computePhiMod(trackPhi, sign); - if (phiModn > fPhiCutHigh->Eval(trackPt)) - return true; // keep track - if (phiModn < fPhiCutLow->Eval(trackPt)) - return true; // keep track - return false; // reject track - } - - template - // uint64_t computeReconstructionBitmap(TV0 v0, TCollision collision, float rapidityLambda, float rapidityK0Short, float /*pT*/) - uint64_t computeReconstructionBitmap(TV0 v0, TCollision collision, float rapidityLambda, float /*pT*/) - // precalculate this information so that a check is one mask operation, not many - { - uint64_t bitMap = 0; - // Base topological variables - if (v0.v0radius() > v0Selections.v0radius) - BITSET(bitMap, selRadius); - if (v0.v0radius() < v0Selections.v0radiusMax) - BITSET(bitMap, selRadiusMax); - if (std::abs(v0.dcapostopv()) > v0Selections.dcapostopv) - BITSET(bitMap, selDCAPosToPV); - if (std::abs(v0.dcanegtopv()) > v0Selections.dcanegtopv) - BITSET(bitMap, selDCANegToPV); - if (v0.v0cosPA() > v0Selections.v0cospa) - BITSET(bitMap, selCosPA); - if (v0.dcaV0daughters() < v0Selections.dcav0dau) - BITSET(bitMap, selDCAV0Dau); - - // rapidity - if (std::abs(rapidityLambda) < v0Selections.rapidityCut) - BITSET(bitMap, selLambdaRapidity); - - auto posTrackExtra = v0.template posTrackExtra_as(); - auto negTrackExtra = v0.template negTrackExtra_as(); - - // ITS quality flags - bool posIsFromAfterburner = posTrackExtra.hasITSAfterburner(); - bool negIsFromAfterburner = negTrackExtra.hasITSAfterburner(); - - // check minimum number of ITS clusters + maximum ITS chi2 per clusters + reject or select ITS afterburner tracks if requested - if (posTrackExtra.itsNCls() >= v0Selections.minITSclusters && // check minium ITS clusters - posTrackExtra.itsChi2NCl() < v0Selections.maxITSchi2PerNcls && // check maximum ITS chi2 per clusters - (!v0Selections.rejectPosITSafterburner || !posIsFromAfterburner) && // reject afterburner track or not - (!v0Selections.requirePosITSafterburnerOnly || posIsFromAfterburner)) // keep afterburner track or not - BITSET(bitMap, selPosGoodITSTrack); - if (negTrackExtra.itsNCls() >= v0Selections.minITSclusters && // check minium ITS clusters - negTrackExtra.itsChi2NCl() < v0Selections.maxITSchi2PerNcls && // check maximum ITS chi2 per clusters - (!v0Selections.rejectNegITSafterburner || !negIsFromAfterburner) && // reject afterburner track or not - (!v0Selections.requireNegITSafterburnerOnly || negIsFromAfterburner)) // select only afterburner track or not - BITSET(bitMap, selNegGoodITSTrack); - - // TPC quality flags - if (posTrackExtra.tpcCrossedRows() >= v0Selections.minTPCrows && // check minimum TPC crossed rows - posTrackExtra.tpcChi2NCl() < v0Selections.maxTPCchi2PerNcls && // check maximum TPC chi2 per clusters - posTrackExtra.tpcCrossedRowsOverFindableCls() >= v0Selections.minTPCrowsOverFindableClusters && // check minimum fraction of TPC rows over findable - posTrackExtra.tpcFoundOverFindableCls() >= v0Selections.minTPCfoundOverFindableClusters && // check minimum fraction of found over findable TPC clusters - posTrackExtra.tpcFractionSharedCls() < v0Selections.maxFractionTPCSharedClusters && // check the maximum fraction of allowed shared TPC clusters - (!v0Selections.rejectTPCsectorBoundary || isTrackFarFromTPCBoundary(v0.positivept(), v0.positivephi(), 1))) // reject track far from TPC sector boundary or not - BITSET(bitMap, selPosGoodTPCTrack); - if (negTrackExtra.tpcCrossedRows() >= v0Selections.minTPCrows && // check minimum TPC crossed rows - negTrackExtra.tpcChi2NCl() < v0Selections.maxTPCchi2PerNcls && // check maximum TPC chi2 per clusters - negTrackExtra.tpcCrossedRowsOverFindableCls() >= v0Selections.minTPCrowsOverFindableClusters && // check minimum fraction of TPC rows over findable - negTrackExtra.tpcFoundOverFindableCls() >= v0Selections.minTPCfoundOverFindableClusters && // check minimum fraction of found over findable TPC clusters - negTrackExtra.tpcFractionSharedCls() < v0Selections.maxFractionTPCSharedClusters && // check the maximum fraction of allowed shared TPC clusters - (!v0Selections.rejectTPCsectorBoundary || isTrackFarFromTPCBoundary(v0.negativept(), v0.negativephi(), -1))) // reject track far from TPC sector boundary or not - BITSET(bitMap, selNegGoodTPCTrack); - - // TPC PID - if (std::fabs(posTrackExtra.tpcNSigmaPi()) < v0Selections.tpcPidNsigmaCut) - BITSET(bitMap, selTPCPIDPositivePion); - if (std::fabs(posTrackExtra.tpcNSigmaPr()) < v0Selections.tpcPidNsigmaCut) - BITSET(bitMap, selTPCPIDPositiveProton); - if (std::fabs(negTrackExtra.tpcNSigmaPi()) < v0Selections.tpcPidNsigmaCut) - BITSET(bitMap, selTPCPIDNegativePion); - if (std::fabs(negTrackExtra.tpcNSigmaPr()) < v0Selections.tpcPidNsigmaCut) - BITSET(bitMap, selTPCPIDNegativeProton); - - // TOF PID in DeltaT - // Positive track - if (!posTrackExtra.hasTOF() || std::fabs(v0.posTOFDeltaTLaPr()) < v0Selections.maxDeltaTimeProton) - BITSET(bitMap, selTOFDeltaTPositiveProtonLambda); - if (!posTrackExtra.hasTOF() || std::fabs(v0.posTOFDeltaTLaPi()) < v0Selections.maxDeltaTimePion) - BITSET(bitMap, selTOFDeltaTPositivePionLambda); - // Negative track - if (!negTrackExtra.hasTOF() || std::fabs(v0.negTOFDeltaTLaPr()) < v0Selections.maxDeltaTimeProton) - BITSET(bitMap, selTOFDeltaTNegativeProtonLambda); - if (!negTrackExtra.hasTOF() || std::fabs(v0.negTOFDeltaTLaPi()) < v0Selections.maxDeltaTimePion) - BITSET(bitMap, selTOFDeltaTNegativePionLambda); - - // TOF PID in NSigma - // Positive track - if (!posTrackExtra.hasTOF() || std::fabs(v0.tofNSigmaLaPr()) < v0Selections.tofPidNsigmaCutLaPr) - BITSET(bitMap, selTOFNSigmaPositiveProtonLambda); - if (!posTrackExtra.hasTOF() || std::fabs(v0.tofNSigmaALaPi()) < v0Selections.tofPidNsigmaCutLaPi) - BITSET(bitMap, selTOFNSigmaPositivePionLambda); - // Negative track - if (!negTrackExtra.hasTOF() || std::fabs(v0.tofNSigmaALaPr()) < v0Selections.tofPidNsigmaCutLaPr) - BITSET(bitMap, selTOFNSigmaNegativeProtonLambda); - if (!negTrackExtra.hasTOF() || std::fabs(v0.tofNSigmaLaPi()) < v0Selections.tofPidNsigmaCutLaPi) - BITSET(bitMap, selTOFNSigmaNegativePionLambda); - - // ITS only tag - if (posTrackExtra.tpcCrossedRows() < 1) - BITSET(bitMap, selPosItsOnly); - if (negTrackExtra.tpcCrossedRows() < 1) - BITSET(bitMap, selNegItsOnly); - - // TPC only tag - if (posTrackExtra.detectorMap() != o2::aod::track::TPC) - BITSET(bitMap, selPosNotTPCOnly); - if (negTrackExtra.detectorMap() != o2::aod::track::TPC) - BITSET(bitMap, selNegNotTPCOnly); - - // proper lifetime - if (v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * o2::constants::physics::MassLambda0 < v0Selections.lifetimecut->get("lifetimecutLambda")) - BITSET(bitMap, selLambdaCTau); - - return bitMap; - } - - bool verifyMask(uint64_t bitmap, uint64_t mask) - { - return (bitmap & mask) == mask; - } - - int computeITSclusBitmap(uint8_t itsClusMap, bool fromAfterburner) - // Focus on the 12 dominant ITS cluster configurations - { - int bitMap = 0; - - if (verifyMask(itsClusMap, ((uint8_t(1) << 0) | (uint8_t(1) << 1) | (uint8_t(1) << 2) | (uint8_t(1) << 3) | (uint8_t(1) << 4) | (uint8_t(1) << 5) | (uint8_t(1) << 6)))) { - // ITS : IB OB - // ITS : L0 L1 L2 L3 L4 L5 L6 - // ITS : x x x x x x x - bitMap = 12; - } else if (verifyMask(itsClusMap, ((uint8_t(1) << 1) | (uint8_t(1) << 2) | (uint8_t(1) << 3) | (uint8_t(1) << 4) | (uint8_t(1) << 5) | (uint8_t(1) << 6)))) { - // ITS : IB OB - // ITS : L0 L1 L2 L3 L4 L5 L6 - // ITS : x x x x x x - bitMap = 11; - } else if (verifyMask(itsClusMap, ((uint8_t(1) << 2) | (uint8_t(1) << 3) | (uint8_t(1) << 4) | (uint8_t(1) << 5) | (uint8_t(1) << 6)))) { - // ITS : IB OB - // ITS : L0 L1 L2 L3 L4 L5 L6 - // ITS : x x x x x - bitMap = 10; - } else if (verifyMask(itsClusMap, ((uint8_t(1) << 3) | (uint8_t(1) << 4) | (uint8_t(1) << 5) | (uint8_t(1) << 6)))) { - // ITS : IB OB - // ITS : L0 L1 L2 L3 L4 L5 L6 - // ITS : x x x x - bitMap = 9; - if (fromAfterburner) - bitMap = -3; - } else if (verifyMask(itsClusMap, ((uint8_t(1) << 4) | (uint8_t(1) << 5) | (uint8_t(1) << 6)))) { - // ITS : IB OB - // ITS : L0 L1 L2 L3 L4 L5 L6 - // ITS : x x x - bitMap = 8; - if (fromAfterburner) - bitMap = -2; - } else if (verifyMask(itsClusMap, ((uint8_t(1) << 5) | (uint8_t(1) << 6)))) { - // ITS : IB OB - // ITS : L0 L1 L2 L3 L4 L5 L6 - // ITS : x x - bitMap = 7; - if (fromAfterburner) - bitMap = -1; - } else if (verifyMask(itsClusMap, ((uint8_t(1) << 0) | (uint8_t(1) << 1) | (uint8_t(1) << 2) | (uint8_t(1) << 3) | (uint8_t(1) << 4) | (uint8_t(1) << 5)))) { - // ITS : IB OB - // ITS : L0 L1 L2 L3 L4 L5 L6 - // ITS : x x x x x x - bitMap = 6; - } else if (verifyMask(itsClusMap, ((uint8_t(1) << 1) | (uint8_t(1) << 2) | (uint8_t(1) << 3) | (uint8_t(1) << 4) | (uint8_t(1) << 5)))) { - // ITS : IB OB - // ITS : L0 L1 L2 L3 L4 L5 L6 - // ITS : x x x x x - bitMap = 5; - } else if (verifyMask(itsClusMap, ((uint8_t(1) << 2) | (uint8_t(1) << 3) | (uint8_t(1) << 4) | (uint8_t(1) << 5)))) { - // ITS : IB OB - // ITS : L0 L1 L2 L3 L4 L5 L6 - // ITS : x x x x - bitMap = 4; - } else if (verifyMask(itsClusMap, ((uint8_t(1) << 0) | (uint8_t(1) << 1) | (uint8_t(1) << 2) | (uint8_t(1) << 3) | (uint8_t(1) << 4)))) { - // ITS : IB OB - // ITS : L0 L1 L2 L3 L4 L5 L6 - // ITS : x x x x x - bitMap = 3; - } else if (verifyMask(itsClusMap, ((uint8_t(1) << 1) | (uint8_t(1) << 2) | (uint8_t(1) << 3) | (uint8_t(1) << 4)))) { - // ITS : IB OB - // ITS : L0 L1 L2 L3 L4 L5 L6 - // ITS : x x x x - bitMap = 2; - } else if (verifyMask(itsClusMap, ((uint8_t(1) << 0) | (uint8_t(1) << 1) | (uint8_t(1) << 2) | (uint8_t(1) << 3)))) { - // ITS : IB OB - // ITS : L0 L1 L2 L3 L4 L5 L6 - // ITS : x x x x - bitMap = 1; - } else { - // ITS : other configurations - bitMap = 0; - } - - return bitMap; - } - - uint computeDetBitmap(uint8_t detMap) - // Focus on the 4 dominant track configurations : - // Others - // ITS-TPC - // ITS-TPC-TRD - // ITS-TPC-TOF - // ITS-TPC-TRD-TOF - { - uint bitMap = 0; - - if (verifyMask(detMap, (o2::aod::track::ITS | o2::aod::track::TPC | o2::aod::track::TRD | o2::aod::track::TOF))) { - // ITS-TPC-TRD-TOF - bitMap = 4; - } else if (verifyMask(detMap, (o2::aod::track::ITS | o2::aod::track::TPC | o2::aod::track::TOF))) { - // ITS-TPC-TOF - bitMap = 3; - } else if (verifyMask(detMap, (o2::aod::track::ITS | o2::aod::track::TPC | o2::aod::track::TRD))) { - // ITS-TPC-TRD - bitMap = 2; - } else if (verifyMask(detMap, (o2::aod::track::ITS | o2::aod::track::TPC))) { - // ITS-TPC - bitMap = 1; - } - - return bitMap; - } - - // function to load models for ML-based classifiers - void loadMachines(int64_t timeStampML) - { - if (mlConfigurations.loadCustomModelsFromCCDB) { - ccdbApi.init(ccdbConfigurations.ccdbUrl); - LOG(info) << "Fetching models for timestamp: " << timeStampML; - - if (mlConfigurations.calculateLambdaScores) { - bool retrieveSuccessLambda = ccdbApi.retrieveBlob(mlConfigurations.customModelPathCCDB, ".", metadata, timeStampML, false, mlConfigurations.localModelPathLambda.value); - if (retrieveSuccessLambda) { - mlCustomModelLambda.initModel(mlConfigurations.localModelPathLambda.value, mlConfigurations.enableOptimizations.value); - } else { - LOG(fatal) << "Error encountered while fetching/loading the Lambda model from CCDB! Maybe the model doesn't exist yet for this runnumber/timestamp?"; - } - } - } - if (mlConfigurations.calculateLambdaScores) - mlCustomModelLambda.initModel(mlConfigurations.localModelPathLambda.value, mlConfigurations.enableOptimizations.value); - LOG(info) << "ML Models loaded."; - } - - template - // void analyseCandidate(TV0 v0, float pt, float centrality, uint64_t selMap, uint8_t gapSide, int& nK0Shorts, int& nLambdas, int& nAntiLambdas) - void analyseCandidate(TV0 v0, float pt, float centrality, uint64_t selMap, uint8_t gapSide, int& nLambdas) - // precalculate this information so that a check is one mask operation, not many - { - bool passLambdaSelections = false; - - // machine learning is on, go for calculation of thresholds - // FIXME THIS NEEDS ADJUSTING - std::vector inputFeatures{pt, 0.0f, 0.0f, v0.v0radius(), v0.v0cosPA(), v0.dcaV0daughters(), v0.dcapostopv(), v0.dcanegtopv()}; - - if (mlConfigurations.useLambdaScores) { - float lambdaScore = -1; - if (mlConfigurations.calculateLambdaScores) { - // evaluate machine-learning scores - float* lambdaProbability = mlCustomModelLambda.evalModel(inputFeatures); - lambdaScore = lambdaProbability[1]; - } else { - lambdaScore = v0.lambdaBDTScore(); - } - if (lambdaScore > mlConfigurations.thresholdK0Short.value) { - passLambdaSelections = true; - } - } else { - passLambdaSelections = verifyMask(selMap, maskSelectionLambda); - } - - auto posTrackExtra = v0.template posTrackExtra_as(); - auto negTrackExtra = v0.template negTrackExtra_as(); - - bool posIsFromAfterburner = posTrackExtra.itsChi2PerNcl() < 0; - bool negIsFromAfterburner = negTrackExtra.itsChi2PerNcl() < 0; - - uint posDetMap = computeDetBitmap(posTrackExtra.detectorMap()); - int posITSclusMap = computeITSclusBitmap(posTrackExtra.itsClusterMap(), posIsFromAfterburner); - uint negDetMap = computeDetBitmap(negTrackExtra.detectorMap()); - int negITSclusMap = computeITSclusBitmap(negTrackExtra.itsClusterMap(), negIsFromAfterburner); - - // __________________________________________ - // fill with no selection if plain QA requested - if (doPlainTopoQA) { - histos.fill(HIST("hPosDCAToPV"), v0.dcapostopv()); - histos.fill(HIST("hNegDCAToPV"), v0.dcanegtopv()); - histos.fill(HIST("hDCADaughters"), v0.dcaV0daughters()); - histos.fill(HIST("hPointingAngle"), std::acos(v0.v0cosPA())); - histos.fill(HIST("hV0Radius"), v0.v0radius()); - histos.fill(HIST("h2dPositiveITSvsTPCpts"), posTrackExtra.tpcCrossedRows(), posTrackExtra.itsNCls()); - histos.fill(HIST("h2dNegativeITSvsTPCpts"), negTrackExtra.tpcCrossedRows(), negTrackExtra.itsNCls()); - histos.fill(HIST("h2dPositivePtVsPhi"), v0.positivept(), computePhiMod(v0.positivephi(), 1)); - histos.fill(HIST("h2dNegativePtVsPhi"), v0.negativept(), computePhiMod(v0.negativephi(), -1)); - } - - // Fill first bin: all candidates - histos.fill(HIST("GeneralQA/hSelectionV0s"), 0); - // Loop over all bits in the enum and fill if passed - for (uint64_t i = 0; i <= selPhysPrimAntiLambda; i++) { - if (BITCHECK(selMap, i)) { - histos.fill(HIST("GeneralQA/hSelectionV0s"), i + 1); // +1 because bin 0 = "All" - } - } - - // __________________________________________ - // main analysis - if (passLambdaSelections && analyseLambda) { - histos.fill(HIST("GeneralQA/hSelectionV0s"), selPhysPrimAntiLambda + 2); // - histos.fill(HIST("h3dMassLambda"), centrality, pt, v0.mLambda()); - if (gapSide == 0) - histos.fill(HIST("h3dMassLambdaSGA"), centrality, pt, v0.mLambda()); - else if (gapSide == 1) - histos.fill(HIST("h3dMassLambdaSGC"), centrality, pt, v0.mLambda()); - else if (gapSide == 2) - histos.fill(HIST("h3dMassLambdaDG"), centrality, pt, v0.mLambda()); - else - histos.fill(HIST("h3dMassLambdaHadronic"), centrality, pt, v0.mLambda()); - histos.fill(HIST("hMassLambda"), v0.mLambda()); - if (doPlainTopoQA) { - histos.fill(HIST("Lambda/hPosDCAToPV"), v0.dcapostopv()); - histos.fill(HIST("Lambda/hNegDCAToPV"), v0.dcanegtopv()); - histos.fill(HIST("Lambda/hDCADaughters"), v0.dcaV0daughters()); - histos.fill(HIST("Lambda/hPointingAngle"), std::acos(v0.v0cosPA())); - histos.fill(HIST("Lambda/hV0Radius"), v0.v0radius()); - histos.fill(HIST("Lambda/h2dPositiveITSvsTPCpts"), posTrackExtra.tpcCrossedRows(), posTrackExtra.itsNCls()); - histos.fill(HIST("Lambda/h2dNegativeITSvsTPCpts"), negTrackExtra.tpcCrossedRows(), negTrackExtra.itsNCls()); - histos.fill(HIST("Lambda/h2dPositivePtVsPhi"), v0.positivept(), computePhiMod(v0.positivephi(), 1)); - histos.fill(HIST("Lambda/h2dNegativePtVsPhi"), v0.negativept(), computePhiMod(v0.negativephi(), -1)); - } - if (doDetectPropQA == 1) { - histos.fill(HIST("Lambda/h6dDetectPropVsCentrality"), centrality, posDetMap, posITSclusMap, negDetMap, negITSclusMap, pt); - histos.fill(HIST("Lambda/h4dPosDetectPropVsCentrality"), centrality, posTrackExtra.detectorMap(), posTrackExtra.itsClusterMap(), pt); - histos.fill(HIST("Lambda/h4dNegDetectPropVsCentrality"), centrality, negTrackExtra.detectorMap(), negTrackExtra.itsClusterMap(), pt); - } - if (doDetectPropQA == 2) { - histos.fill(HIST("Lambda/h7dDetectPropVsCentrality"), centrality, posDetMap, posITSclusMap, negDetMap, negITSclusMap, pt, v0.mLambda()); - histos.fill(HIST("Lambda/h5dPosDetectPropVsCentrality"), centrality, posTrackExtra.detectorMap(), posTrackExtra.itsClusterMap(), pt, v0.mLambda()); - histos.fill(HIST("Lambda/h5dNegDetectPropVsCentrality"), centrality, negTrackExtra.detectorMap(), negTrackExtra.itsClusterMap(), pt, v0.mLambda()); - } - if (doDetectPropQA == 3) { - histos.fill(HIST("Lambda/h3dITSchi2"), centrality, pt, std::max(posTrackExtra.itsChi2NCl(), negTrackExtra.itsChi2NCl())); - histos.fill(HIST("Lambda/h3dTPCchi2"), centrality, pt, std::max(posTrackExtra.tpcChi2NCl(), negTrackExtra.tpcChi2NCl())); - histos.fill(HIST("Lambda/h3dTPCFoundOverFindable"), centrality, pt, std::min(posTrackExtra.tpcFoundOverFindableCls(), negTrackExtra.tpcFoundOverFindableCls())); - histos.fill(HIST("Lambda/h3dTPCrowsOverFindable"), centrality, pt, std::min(posTrackExtra.tpcCrossedRowsOverFindableCls(), negTrackExtra.tpcCrossedRowsOverFindableCls())); - histos.fill(HIST("Lambda/h3dTPCsharedCls"), centrality, pt, std::max(posTrackExtra.tpcFractionSharedCls(), negTrackExtra.tpcFractionSharedCls())); - histos.fill(HIST("Lambda/h3dPositiveITSchi2"), centrality, pt, posTrackExtra.itsChi2NCl()); - histos.fill(HIST("Lambda/h3dNegativeITSchi2"), centrality, pt, negTrackExtra.itsChi2NCl()); - histos.fill(HIST("Lambda/h3dPositiveTPCchi2"), centrality, pt, posTrackExtra.tpcChi2NCl()); - histos.fill(HIST("Lambda/h3dNegativeTPCchi2"), centrality, pt, negTrackExtra.tpcChi2NCl()); - histos.fill(HIST("Lambda/h3dPositiveITSclusters"), centrality, pt, posTrackExtra.itsNCls()); - histos.fill(HIST("Lambda/h3dNegativeITSclusters"), centrality, pt, negTrackExtra.itsNCls()); - histos.fill(HIST("Lambda/h3dPositiveTPCcrossedRows"), centrality, pt, posTrackExtra.tpcCrossedRows()); - histos.fill(HIST("Lambda/h3dNegativeTPCcrossedRows"), centrality, pt, negTrackExtra.tpcCrossedRows()); - } - if (doTPCQA) { - histos.fill(HIST("Lambda/h3dPosNsigmaTPC"), centrality, pt, posTrackExtra.tpcNSigmaPr()); - histos.fill(HIST("Lambda/h3dNegNsigmaTPC"), centrality, pt, negTrackExtra.tpcNSigmaPi()); - histos.fill(HIST("Lambda/h3dPosTPCsignal"), centrality, pt, posTrackExtra.tpcSignal()); - histos.fill(HIST("Lambda/h3dNegTPCsignal"), centrality, pt, negTrackExtra.tpcSignal()); - histos.fill(HIST("Lambda/h3dPosNsigmaTPCvsTrackPtot"), centrality, v0.pfracpos() * v0.p(), posTrackExtra.tpcNSigmaPr()); - histos.fill(HIST("Lambda/h3dNegNsigmaTPCvsTrackPtot"), centrality, v0.pfracneg() * v0.p(), negTrackExtra.tpcNSigmaPi()); - histos.fill(HIST("Lambda/h3dPosTPCsignalVsTrackPtot"), centrality, v0.pfracpos() * v0.p(), posTrackExtra.tpcSignal()); - histos.fill(HIST("Lambda/h3dNegTPCsignalVsTrackPtot"), centrality, v0.pfracneg() * v0.p(), negTrackExtra.tpcSignal()); - histos.fill(HIST("Lambda/h3dPosNsigmaTPCvsTrackPt"), centrality, v0.positivept(), posTrackExtra.tpcNSigmaPr()); - histos.fill(HIST("Lambda/h3dNegNsigmaTPCvsTrackPt"), centrality, v0.negativept(), negTrackExtra.tpcNSigmaPi()); - histos.fill(HIST("Lambda/h3dPosTPCsignalVsTrackPt"), centrality, v0.positivept(), posTrackExtra.tpcSignal()); - histos.fill(HIST("Lambda/h3dNegTPCsignalVsTrackPt"), centrality, v0.negativept(), negTrackExtra.tpcSignal()); - } - if (doTOFQA) { - histos.fill(HIST("Lambda/h3dPosNsigmaTOF"), centrality, pt, v0.tofNSigmaLaPr()); - histos.fill(HIST("Lambda/h3dNegNsigmaTOF"), centrality, pt, v0.tofNSigmaLaPi()); - histos.fill(HIST("Lambda/h3dPosTOFdeltaT"), centrality, pt, v0.posTOFDeltaTLaPr()); - histos.fill(HIST("Lambda/h3dNegTOFdeltaT"), centrality, pt, v0.negTOFDeltaTLaPi()); - histos.fill(HIST("Lambda/h3dPosNsigmaTOFvsTrackPtot"), centrality, v0.pfracpos() * v0.p(), v0.tofNSigmaLaPr()); - histos.fill(HIST("Lambda/h3dNegNsigmaTOFvsTrackPtot"), centrality, v0.pfracneg() * v0.p(), v0.tofNSigmaLaPi()); - histos.fill(HIST("Lambda/h3dPosTOFdeltaTvsTrackPtot"), centrality, v0.pfracpos() * v0.p(), v0.posTOFDeltaTLaPr()); - histos.fill(HIST("Lambda/h3dNegTOFdeltaTvsTrackPtot"), centrality, v0.pfracneg() * v0.p(), v0.negTOFDeltaTLaPi()); - histos.fill(HIST("Lambda/h3dPosNsigmaTOFvsTrackPt"), centrality, v0.positivept(), v0.tofNSigmaLaPr()); - histos.fill(HIST("Lambda/h3dNegNsigmaTOFvsTrackPt"), centrality, v0.negativept(), v0.tofNSigmaLaPi()); - histos.fill(HIST("Lambda/h3dPosTOFdeltaTvsTrackPt"), centrality, v0.positivept(), v0.posTOFDeltaTLaPr()); - histos.fill(HIST("Lambda/h3dNegTOFdeltaTvsTrackPt"), centrality, v0.negativept(), v0.negTOFDeltaTLaPi()); - } - if (doEtaPhiQA) { - histos.fill(HIST("Lambda/h5dV0PhiVsEta"), centrality, pt, 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()); - } - nLambdas++; - } - - // // __________________________________________ - // // do systematics / qa plots - // if (doCompleteTopoQA) { - // if (analyseLambda) { - // if (verifyMask(selMap, maskTopoNoV0Radius | maskLambdaSpecific)) - // histos.fill(HIST("Lambda/h4dV0Radius"), centrality, pt, v0.mLambda(), v0.v0radius()); - // if (verifyMask(selMap, maskTopoNoDCAPosToPV | maskLambdaSpecific)) - // histos.fill(HIST("Lambda/h4dPosDCAToPV"), centrality, pt, v0.mLambda(), std::abs(v0.dcapostopv())); - // if (verifyMask(selMap, maskTopoNoDCANegToPV | maskLambdaSpecific)) - // histos.fill(HIST("Lambda/h4dNegDCAToPV"), centrality, pt, v0.mLambda(), std::abs(v0.dcanegtopv())); - // if (verifyMask(selMap, maskTopoNoCosPA | maskLambdaSpecific)) - // histos.fill(HIST("Lambda/h4dPointingAngle"), centrality, pt, v0.mLambda(), std::acos(v0.v0cosPA())); - // if (verifyMask(selMap, maskTopoNoDCAV0Dau | maskLambdaSpecific)) - // histos.fill(HIST("Lambda/h4dDCADaughters"), centrality, pt, v0.mLambda(), v0.dcaV0daughters()); - // } // end systematics / qa - // } - } - - // ______________________________________________________ - // Return slicing output - template - auto getCentralityRun3(TCollision const& collision) - { - if (centralityEstimator == kCentFT0C) - return collision.centFT0C(); - else if (centralityEstimator == kCentFT0M) - return collision.centFT0M(); - else if (centralityEstimator == kCentFT0CVariant1) - return collision.centFT0CVariant1(); - else if (centralityEstimator == kCentMFT) - return collision.centMFT(); - else if (centralityEstimator == kCentNGlobal) - return collision.centNGlobal(); - else if (centralityEstimator == kCentFV0A) - return collision.centFV0A(); - - return -1.f; - } - - - // ______________________________________________________ - // Reconstructed data processing - // Fill reconstructed event information - // Return centrality, occupancy, interaction rate, gap side and selGapside via reference-passing in arguments - template - void fillReconstructedEventProperties(TCollision const& collision, float& centrality, float& collisionOccupancy, double& interactionRate, int& gapSide, int& selGapSide) - { - if constexpr (requires { collision.centFT0C(); }) { // check if we are in Run 3 - centrality = getCentralityRun3(collision); - collisionOccupancy = eventSelections.useFT0CbasedOccupancy ? collision.ft0cOccupancyInTimeRange() : collision.trackOccupancyInTimeRange(); - // Fetch interaction rate only if required (in order to limit ccdb calls) - interactionRate = !irSource.value.empty() ? rateFetcher.fetch(ccdb.service, collision.timestamp(), collision.runNumber(), irSource) * 1.e-3 : -1; - - if (qaCentrality) { - auto hRawCentrality = histos.get(HIST("hRawCentrality")); - centrality = hRawCentrality->GetBinContent(hRawCentrality->FindBin(doPPAnalysis ? collision.multFT0A() + collision.multFT0C() : collision.multFT0C())); - } - - // gap side - gapSide = collision.gapSide(); - // -1 --> Hadronic - // 0 --> Single Gap - A side - // 1 --> Single Gap - C side - // 2 --> Double Gap - both A & C sides - selGapSide = sgSelector.trueGap(collision, upcCuts.fv0Cut, upcCuts.ft0Acut, upcCuts.ft0Ccut, upcCuts.zdcCut); - } else { // no, we are in Run 2 - centrality = eventSelections.useSPDTrackletsCent ? collision.centRun2SPDTracklets() : collision.centRun2V0M(); - } - - histos.fill(HIST("hGapSide"), gapSide); - histos.fill(HIST("hSelGapSide"), selGapSide); - histos.fill(HIST("hEventCentralityVsSelGapSide"), centrality, selGapSide <= 2 ? selGapSide : -1); - - histos.fill(HIST("hEventCentrality"), centrality); - - histos.fill(HIST("hCentralityVsNch"), centrality, collision.multNTracksPVeta1()); - if (doEventQA) { - if constexpr (requires { collision.centFT0C(); }) { // check if we are in Run 3 - histos.fill(HIST("hCentralityVsNGlobal"), centrality, collision.multNTracksGlobal()); - histos.fill(HIST("hEventCentVsMultFT0M"), collision.centFT0M(), collision.multFT0A() + collision.multFT0C()); - histos.fill(HIST("hEventCentVsMultFT0C"), collision.centFT0C(), collision.multFT0C()); - histos.fill(HIST("hEventCentVsMultNGlobal"), collision.centNGlobal(), collision.multNTracksGlobal()); - histos.fill(HIST("hEventCentVsMultFV0A"), collision.centFV0A(), collision.multFV0A()); - histos.fill(HIST("hEventMultFT0MvsMultNGlobal"), collision.multFT0A() + collision.multFT0C(), collision.multNTracksGlobal()); - histos.fill(HIST("hEventMultFT0CvsMultNGlobal"), collision.multFT0C(), collision.multNTracksGlobal()); - histos.fill(HIST("hEventMultFV0AvsMultNGlobal"), collision.multFV0A(), collision.multNTracksGlobal()); - histos.fill(HIST("hEventMultPVvsMultNGlobal"), collision.multNTracksPVeta1(), collision.multNTracksGlobal()); - histos.fill(HIST("hEventMultFT0CvsMultFV0A"), collision.multFT0C(), collision.multFV0A()); - } - } - - histos.fill(HIST("hCentralityVsPVz"), centrality, collision.posZ()); - histos.fill(HIST("hEventPVz"), collision.posZ()); - - histos.fill(HIST("hEventOccupancy"), collisionOccupancy); - histos.fill(HIST("hCentralityVsOccupancy"), centrality, collisionOccupancy); - - histos.fill(HIST("hInteractionRate"), interactionRate); - histos.fill(HIST("hCentralityVsInteractionRate"), centrality, interactionRate); - - histos.fill(HIST("hInteractionRateVsOccupancy"), interactionRate, collisionOccupancy); - return; - } - - template - bool isEventAccepted(TCollision collision, bool fillHists) - // check whether the collision passes our collision selections - { - float centrality = -1.0f; - if (fillHists) { - histos.fill(HIST("hEventSelection"), 0. /* all collisions */); - if (doEventQA) { - if constexpr (requires { collision.centFT0C(); }) { // check if we are in Run 3 - centrality = getCentralityRun3(collision); - } - histos.fill(HIST("hEventSelectionVsCentrality"), 0. /* all collisions */, centrality); - } - } - - if constexpr (requires { collision.centFT0C(); }) { // check if we are in Run 3 - if (eventSelections.requireSel8 && !collision.sel8()) { - return false; - } - if (fillHists) { - histos.fill(HIST("hEventSelection"), 1 /* sel8 collisions */); - if (doEventQA) { - histos.fill(HIST("hEventSelectionVsCentrality"), 1 /* sel8 collisions */, centrality); - } - } - - if (eventSelections.requireTriggerTVX && !collision.selection_bit(aod::evsel::kIsTriggerTVX)) { - return false; - } - if (fillHists) { - histos.fill(HIST("hEventSelection"), 2 /* FT0 vertex (acceptable FT0C-FT0A time difference) collisions */); - if (doEventQA) { - histos.fill(HIST("hEventSelectionVsCentrality"), 2 /* FT0 vertex (acceptable FT0C-FT0A time difference) collisions */, centrality); - } - } - - if (eventSelections.rejectITSROFBorder && !collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { - return false; - } - if (fillHists) { - histos.fill(HIST("hEventSelection"), 3 /* Not at ITS ROF border */); - if (doEventQA) { - histos.fill(HIST("hEventSelectionVsCentrality"), 3 /* Not at ITS ROF border */, centrality); - } - } - - if (eventSelections.rejectTFBorder && !collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { - return false; - } - if (fillHists) { - histos.fill(HIST("hEventSelection"), 4 /* Not at TF border */); - if (doEventQA) { - histos.fill(HIST("hEventSelectionVsCentrality"), 4 /* Not at TF border */, centrality); - } - } - - if (std::abs(collision.posZ()) > eventSelections.maxZVtxPosition) { - return false; - } - if (fillHists) { - histos.fill(HIST("hEventSelection"), 5 /* vertex-Z selected */); - if (doEventQA) { - histos.fill(HIST("hEventSelectionVsCentrality"), 5 /* vertex-Z selected */, centrality); - } - } - - if (eventSelections.requireIsVertexITSTPC && !collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) { - return false; - } - if (fillHists) { - histos.fill(HIST("hEventSelection"), 6 /* Contains at least one ITS-TPC track */); - if (doEventQA) { - histos.fill(HIST("hEventSelectionVsCentrality"), 6 /* Contains at least one ITS-TPC track */, centrality); - } - } - - if (eventSelections.requireIsGoodZvtxFT0VsPV && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { - return false; - } - if (fillHists) { - histos.fill(HIST("hEventSelection"), 7 /* PV position consistency check */); - if (doEventQA) { - histos.fill(HIST("hEventSelectionVsCentrality"), 7 /* PV position consistency check */, centrality); - } - } - - if (eventSelections.requireIsVertexTOFmatched && !collision.selection_bit(o2::aod::evsel::kIsVertexTOFmatched)) { - return false; - } - if (fillHists) { - histos.fill(HIST("hEventSelection"), 8 /* PV with at least one contributor matched with TOF */); - if (doEventQA) { - histos.fill(HIST("hEventSelectionVsCentrality"), 8 /* PV with at least one contributor matched with TOF */, centrality); - } - } - - if (eventSelections.requireIsVertexTRDmatched && !collision.selection_bit(o2::aod::evsel::kIsVertexTRDmatched)) { - return false; - } - if (fillHists) { - histos.fill(HIST("hEventSelection"), 9 /* PV with at least one contributor matched with TRD */); - if (doEventQA) { - histos.fill(HIST("hEventSelectionVsCentrality"), 9 /* PV with at least one contributor matched with TRD */, centrality); - } - } - - if (eventSelections.rejectSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { - return false; - } - if (fillHists) { - histos.fill(HIST("hEventSelection"), 10 /* Not at same bunch pile-up */); - if (doEventQA) { - histos.fill(HIST("hEventSelectionVsCentrality"), 10 /* Not at same bunch pile-up */, centrality); - } - } - - if (eventSelections.requireNoCollInTimeRangeStd && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { - return false; - } - if (fillHists) { - histos.fill(HIST("hEventSelection"), 11 /* No other collision within +/- 2 microseconds or mult above a certain threshold in -4 - -2 microseconds*/); - if (doEventQA) { - histos.fill(HIST("hEventSelectionVsCentrality"), 11 /* No other collision within +/- 2 microseconds or mult above a certain threshold in -4 - -2 microseconds*/, centrality); - } - } - - if (eventSelections.requireNoCollInTimeRangeStrict && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStrict)) { - return false; - } - if (fillHists) { - histos.fill(HIST("hEventSelection"), 12 /* No other collision within +/- 10 microseconds */); - if (doEventQA) { - histos.fill(HIST("hEventSelectionVsCentrality"), 12 /* No other collision within +/- 10 microseconds */, centrality); - } - } - - if (eventSelections.requireNoCollInTimeRangeNarrow && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeNarrow)) { - return false; - } - if (fillHists) { - histos.fill(HIST("hEventSelection"), 13 /* No other collision within +/- 2 microseconds */); - if (doEventQA) { - histos.fill(HIST("hEventSelectionVsCentrality"), 13 /* No other collision within +/- 2 microseconds */, centrality); - } - } - - if (eventSelections.requireNoCollInROFStd && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { - return false; - } - if (fillHists) { - histos.fill(HIST("hEventSelection"), 14 /* No other collision within the same ITS ROF with mult. above a certain threshold */); - if (doEventQA) { - histos.fill(HIST("hEventSelectionVsCentrality"), 14 /* No other collision within the same ITS ROF with mult. above a certain threshold */, centrality); - } - } - - if (eventSelections.requireNoCollInROFStrict && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStrict)) { - return false; - } - if (fillHists) { - histos.fill(HIST("hEventSelection"), 15 /* No other collision within the same ITS ROF */); - if (doEventQA) { - histos.fill(HIST("hEventSelectionVsCentrality"), 15 /* No other collision within the same ITS ROF */, centrality); - } - } - - if (doPPAnalysis) { // we are in pp - if (eventSelections.requireINEL0 && collision.multNTracksPVeta1() < 1) { - return false; - } - if (fillHists) { - histos.fill(HIST("hEventSelection"), 16 /* INEL > 0 */); - if (doEventQA) { - histos.fill(HIST("hEventSelectionVsCentrality"), 16 /* INEL > 0 */, centrality); - } - } - - if (eventSelections.requireINEL1 && collision.multNTracksPVeta1() < 2) { - return false; - } - if (fillHists) { - histos.fill(HIST("hEventSelection"), 17 /* INEL > 1 */); - if (doEventQA) { - histos.fill(HIST("hEventSelectionVsCentrality"), 17 /* INEL > 1 */, centrality); - } - } - - } else { // we are in Pb-Pb - float collisionOccupancy = eventSelections.useFT0CbasedOccupancy ? collision.ft0cOccupancyInTimeRange() : collision.trackOccupancyInTimeRange(); - if (eventSelections.minOccupancy >= 0 && collisionOccupancy < eventSelections.minOccupancy) { - return false; - } - if (fillHists) { - histos.fill(HIST("hEventSelection"), 16 /* Below min occupancy */); - if (doEventQA) { - histos.fill(HIST("hEventSelectionVsCentrality"), 16 /* Below min occupancy */, centrality); - } - } - - if (eventSelections.maxOccupancy >= 0 && collisionOccupancy > eventSelections.maxOccupancy) { - return false; - } - if (fillHists) { - histos.fill(HIST("hEventSelection"), 17 /* Above max occupancy */); - if (doEventQA) { - histos.fill(HIST("hEventSelectionVsCentrality"), 17 /* Above max occupancy */, centrality); - } - } - } - - // Fetch interaction rate only if required (in order to limit ccdb calls) - double interactionRate = (eventSelections.minIR >= 0 || eventSelections.maxIR >= 0) ? rateFetcher.fetch(ccdb.service, collision.timestamp(), collision.runNumber(), irSource) * 1.e-3 : -1; - if (eventSelections.minIR >= 0 && interactionRate < eventSelections.minIR) { - return false; - } - if (fillHists) { - histos.fill(HIST("hEventSelection"), 18 /* Below min IR */); - if (doEventQA) { - histos.fill(HIST("hEventSelectionVsCentrality"), 18 /* Below min IR */, centrality); - } - } - - if (eventSelections.maxIR >= 0 && interactionRate > eventSelections.maxIR) { - return false; - } - if (fillHists) { - histos.fill(HIST("hEventSelection"), 19 /* Above max IR */); - if (doEventQA) { - histos.fill(HIST("hEventSelectionVsCentrality"), 19 /* Above max IR */, centrality); - } - } - - if (!rctConfigurations.cfgRCTLabel.value.empty() && !rctFlagsChecker(collision)) { - return false; - } - if (fillHists) { - histos.fill(HIST("hEventSelection"), 20 /* Pass CBT condition */); - if (doEventQA) { - histos.fill(HIST("hEventSelectionVsCentrality"), 20 /* Pass CBT condition */, centrality); - } - } - - } else { // we are in Run 2 - if (eventSelections.requireSel8 && !collision.sel8()) { - return false; - } - if (fillHists) - histos.fill(HIST("hEventSelection"), 1 /* sel8 collisions */); - - if (eventSelections.requireSel7 && !collision.sel7()) { - return false; - } - if (fillHists) - histos.fill(HIST("hEventSelection"), 2 /* sel7 collisions */); - - if (eventSelections.requireINT7 && !collision.alias_bit(kINT7)) { - return false; - } - if (fillHists) - histos.fill(HIST("hEventSelection"), 3 /* INT7-triggered collisions */); - - if (eventSelections.requireTriggerTVX && !collision.selection_bit(o2::aod::evsel::kIsTriggerTVX)) { - return false; - } - if (fillHists) - histos.fill(HIST("hEventSelection"), 4 /* FT0 vertex (acceptable FT0C-FT0A time difference) at trigger level */); - - if (eventSelections.rejectIncompleteDAQ && !collision.selection_bit(o2::aod::evsel::kNoIncompleteDAQ)) { - return false; - } - if (fillHists) - histos.fill(HIST("hEventSelection"), 5 /* Complete events according to DAQ flags */); - - if (std::abs(collision.posZ()) > eventSelections.maxZVtxPosition) { - return false; - } - if (fillHists) - histos.fill(HIST("hEventSelection"), 6 /* vertex-Z selected */); - - if (eventSelections.requireConsistentSPDAndTrackVtx && !collision.selection_bit(o2::aod::evsel::kNoInconsistentVtx)) { - return false; - } - if (fillHists) - histos.fill(HIST("hEventSelection"), 7 /* No inconsistency in SPD and Track vertices */); - - if (eventSelections.rejectPileupFromSPD && !collision.selection_bit(o2::aod::evsel::kNoPileupFromSPD)) { - return false; - } - if (fillHists) - histos.fill(HIST("hEventSelection"), 8 /* No pileup according to SPD vertexer */); - - if (eventSelections.rejectV0PFPileup && !collision.selection_bit(o2::aod::evsel::kNoV0PFPileup)) { - return false; - } - if (fillHists) - histos.fill(HIST("hEventSelection"), 9 /* No out-of-bunch pileup according to V0 past-future info */); - - if (eventSelections.rejectPileupInMultBins && !collision.selection_bit(o2::aod::evsel::kNoPileupInMultBins)) { - return false; - } - if (fillHists) - histos.fill(HIST("hEventSelection"), 10 /* No pileup according to multiplicity-differential pileup checks */); - - if (eventSelections.rejectPileupMV && !collision.selection_bit(o2::aod::evsel::kNoPileupMV)) { - return false; - } - if (fillHists) - histos.fill(HIST("hEventSelection"), 11 /* No pileup according to multi-vertexer */); - - if (eventSelections.rejectTPCPileup && !collision.selection_bit(o2::aod::evsel::kNoPileupTPC)) { - return false; - } - if (fillHists) - histos.fill(HIST("hEventSelection"), 12 /* No pileup in TPC */); - - if (eventSelections.requireNoV0MOnVsOffPileup && !collision.selection_bit(o2::aod::evsel::kNoV0MOnVsOfPileup)) { - return false; - } - if (fillHists) - histos.fill(HIST("hEventSelection"), 13 /* No out-of-bunch pileup according to online-vs-offline VOM correlation */); - - if (eventSelections.requireNoSPDOnVsOffPileup && !collision.selection_bit(o2::aod::evsel::kNoSPDOnVsOfPileup)) { - return false; - } - if (fillHists) - histos.fill(HIST("hEventSelection"), 14 /* No out-of-bunch pileup according to online-vs-offline SPD correlation */); - - if (eventSelections.requireNoSPDClsVsTklBG && !collision.selection_bit(o2::aod::evsel::kNoSPDClsVsTklBG)) { - return false; - } - if (fillHists) - histos.fill(HIST("hEventSelection"), 15 /* No beam-gas according to cluster-vs-tracklet correlation */); - - if (doPPAnalysis) { // we are in pp - if (eventSelections.requireINEL0 && collision.multNTracksPVeta1() < 1) { - return false; - } - if (fillHists) - histos.fill(HIST("hEventSelection"), 16 /* INEL > 0 */); - - if (eventSelections.requireINEL1 && collision.multNTracksPVeta1() < 2) { - return false; - } - if (fillHists) - histos.fill(HIST("hEventSelection"), 17 /* INEL > 1 */); - } - } - - return true; - } - - // ______________________________________________________ - // Real data processing - no MC subscription - template - void analyzeRecoedV0sInRealData(TCollision const& collision, TV0s const& fullV0s) - { - // Fire up CCDB - if ((mlConfigurations.useLambdaScores && mlConfigurations.calculateLambdaScores) || - v0Selections.rejectTPCsectorBoundary) { - initCCDB(collision); - } - - if (!isEventAccepted(collision, true)) { - return; - } - - float centrality = -1; - float collisionOccupancy = -2; // -1 already taken for the case where occupancy cannot be evaluated - double interactionRate = -1; - // gap side - int gapSide = -1; - int selGapSide = -1; // -1 --> Hadronic ; 0 --> Single Gap - A side ; 1 --> Single Gap - C side ; 2 --> Double Gap - both A & C sides - // Fill recoed event properties - fillReconstructedEventProperties(collision, centrality, collisionOccupancy, interactionRate, gapSide, selGapSide); - - histos.fill(HIST("hInteractionRateVsOccupancy"), interactionRate, collisionOccupancy); - - // __________________________________________ - // perform main analysis - int nLambdas = 0; - for (auto const& v0 : fullV0s) { - if (std::abs(v0.negativeeta()) > v0Selections.daughterEtaCut || std::abs(v0.positiveeta()) > v0Selections.daughterEtaCut) - continue; // remove acceptance that's badly reproduced by MC / superfluous in future - - if (v0.v0Type() != v0Selections.v0TypeSelection && v0Selections.v0TypeSelection > -1) - continue; // skip V0s that are not standard - - // fill AP plot for all V0s - histos.fill(HIST("GeneralQA/h2dArmenterosAll"), v0.alpha(), v0.qtarm()); - - // uint64_t selMap = computeReconstructionBitmap(v0, collision, v0.yLambda(), v0.yK0Short(), v0.pt()); - uint64_t selMap = computeReconstructionBitmap(v0, collision, v0.yLambda(), v0.pt()); // Removed unneeded K0Short info - - // consider for histograms for all species - BITSET(selMap, selConsiderLambda); - BITSET(selMap, selPhysPrimLambda); - - // analyseCandidate(v0, v0.pt(), centrality, selMap, selGapSide, nK0Shorts, nLambdas, nAntiLambdas); - analyseCandidate(v0, v0.pt(), centrality, selMap, selGapSide, nLambdas); - } // end v0 loop - - // fill the histograms with the number of reconstructed Lambda per collision - if (analyseLambda) { - histos.fill(HIST("h2dNbrOfLambdaVsCentrality"), centrality, nLambdas); - } - } - - // Subscriping to the appropriate tables and running the code: - // ______________________________________________________ - // Real data processing in Run 3 - no MC subscription - void processRealDataRun3(soa::Join::iterator const& collision, V0Candidates const& fullV0s, DauTracks const&) - { - analyzeRecoedV0sInRealData(collision, fullV0s); - } - - // Kept only the process switch that is relevant for this particular work: - PROCESS_SWITCH(lambdaInvMassTest, processRealDataRun3, "process as if real data in Run 3", true); -}; - -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) -{ - return WorkflowSpec{adaptAnalysisTask(cfgc)}; -} \ No newline at end of file From ce06589c6b7a4523814bac1be9c3dc7e86a0a92d Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Tue, 3 Mar 2026 23:00:57 -0300 Subject: [PATCH 34/40] Removing unneeded dependencies in CMakeLists, organizing TableProducer's V0Topo histograms in a subfolder in ROOT --- .../TableProducer/Strangeness/CMakeLists.txt | 4 +-- .../Strangeness/lambdaJetPolarizationIons.cxx | 36 +++++++++---------- PWGLF/Tasks/Strangeness/CMakeLists.txt | 6 ++-- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/PWGLF/TableProducer/Strangeness/CMakeLists.txt b/PWGLF/TableProducer/Strangeness/CMakeLists.txt index 7c711caa5fd..9a6e18ac944 100644 --- a/PWGLF/TableProducer/Strangeness/CMakeLists.txt +++ b/PWGLF/TableProducer/Strangeness/CMakeLists.txt @@ -149,8 +149,8 @@ o2physics_add_dpl_workflow(lambdajetpolarizationbuilder o2physics_add_dpl_workflow(lambdajetpolarizationions SOURCES lambdaJetPolarizationIons.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGJECore FastJet::FastJet FastJet::Contrib O2Physics::EventFilteringUtils O2Physics::AnalysisCCDB -COMPONENT_NAME Analysis) + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGJECore FastJet::FastJet O2Physics::AnalysisCCDB + COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(stracents SOURCES stracents.cxx diff --git a/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx b/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx index 50109fa8759..d555c4fcaed 100644 --- a/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx +++ b/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx @@ -813,15 +813,15 @@ struct lambdajetpolarizationions { } // For all received candidates: - histos.add("hPosDCAToPV", "hPosDCAToPV", kTH1D, {axisConfigurations.axisDCAtoPV}); - histos.add("hNegDCAToPV", "hNegDCAToPV", kTH1D, {axisConfigurations.axisDCAtoPV}); - histos.add("hDCADaughters", "hDCADaughters", kTH1D, {axisConfigurations.axisDCAdau}); - histos.add("hPointingAngle", "hPointingAngle", kTH1D, {axisConfigurations.axisPointingAngle}); - histos.add("hV0Radius", "hV0Radius", kTH1D, {axisConfigurations.axisV0Radius}); - histos.add("h2dPositiveITSvsTPCpts", "h2dPositiveITSvsTPCpts", kTH2D, {axisConfigurations.axisTPCrows, axisConfigurations.axisITSclus}); - histos.add("h2dNegativeITSvsTPCpts", "h2dNegativeITSvsTPCpts", kTH2D, {axisConfigurations.axisTPCrows, axisConfigurations.axisITSclus}); - histos.add("h2dPositivePtVsPhi", "h2dPositivePtVsPhi", kTH2D, {axisConfigurations.axisPtCoarse, axisConfigurations.axisPhiMod}); - histos.add("h2dNegativePtVsPhi", "h2dNegativePtVsPhi", kTH2D, {axisConfigurations.axisPtCoarse, axisConfigurations.axisPhiMod}); + 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}); if (analyseLambda) { histos.add("Lambda/hPosDCAToPV", "hPosDCAToPV", kTH1D, {axisConfigurations.axisDCAtoPV}); histos.add("Lambda/hNegDCAToPV", "hNegDCAToPV", kTH1D, {axisConfigurations.axisDCAtoPV}); @@ -1748,15 +1748,15 @@ struct lambdajetpolarizationions { if (doCompleteTopoQA){ // Remaking these variables outside of the passesLambdaLambdaBarHypothesis. Loses performance, but that should be OK for QA - histos.fill(HIST("hPosDCAToPV"), v0.dcapostopv()); - histos.fill(HIST("hNegDCAToPV"), v0.dcanegtopv()); - histos.fill(HIST("hDCADaughters"), v0.dcaV0daughters()); - histos.fill(HIST("hPointingAngle"), std::acos(v0.v0cosPA())); - histos.fill(HIST("hV0Radius"), v0.v0radius()); - histos.fill(HIST("h2dPositiveITSvsTPCpts"), posTrackExtra.tpcNClsCrossedRows(), posTrackExtra.itsNCls()); - histos.fill(HIST("h2dNegativeITSvsTPCpts"), negTrackExtra.tpcNClsCrossedRows(), negTrackExtra.itsNCls()); - histos.fill(HIST("h2dPositivePtVsPhi"), v0.positivept(), computePhiMod(v0.positivephi(), 1)); - histos.fill(HIST("h2dNegativePtVsPhi"), v0.negativept(), computePhiMod(v0.negativephi(), -1)); + histos.fill(HIST("V0KinematicQA/hPosDCAToPV"), v0.dcapostopv()); + histos.fill(HIST("V0KinematicQA/hNegDCAToPV"), v0.dcanegtopv()); + histos.fill(HIST("V0KinematicQA/hDCADaughters"), v0.dcaV0daughters()); + histos.fill(HIST("V0KinematicQA/hPointingAngle"), std::acos(v0.v0cosPA())); + histos.fill(HIST("V0KinematicQA/hV0Radius"), v0.v0radius()); + histos.fill(HIST("V0KinematicQA/h2dPositiveITSvsTPCpts"), posTrackExtra.tpcNClsCrossedRows(), posTrackExtra.itsNCls()); + 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()); diff --git a/PWGLF/Tasks/Strangeness/CMakeLists.txt b/PWGLF/Tasks/Strangeness/CMakeLists.txt index 89d6615a8e8..d59685bc93d 100644 --- a/PWGLF/Tasks/Strangeness/CMakeLists.txt +++ b/PWGLF/Tasks/Strangeness/CMakeLists.txt @@ -167,9 +167,9 @@ o2physics_add_dpl_workflow(lambdajetpolarization COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(lambdajetpolarizationionsderived -SOURCES lambdaJetPolarizationIonsDerived.cxx -PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore -COMPONENT_NAME Analysis) + SOURCES lambdaJetPolarizationIonsDerived.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(lambdaspincorrderived SOURCES lambdaspincorrderived.cxx From 44c227b5184b99fda0f1cef0bdaa98621c8db641 Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Wed, 4 Mar 2026 11:01:30 -0300 Subject: [PATCH 35/40] Fixing wrong table fill order. Cleaning up table subscriptions and removing unused centrality estimators. --- PWGLF/DataModel/lambdaJetPolarizationIons.h | 3 -- .../Strangeness/lambdaJetPolarizationIons.cxx | 35 ++++--------------- 2 files changed, 7 insertions(+), 31 deletions(-) diff --git a/PWGLF/DataModel/lambdaJetPolarizationIons.h b/PWGLF/DataModel/lambdaJetPolarizationIons.h index 773b0c50183..daac6f12489 100644 --- a/PWGLF/DataModel/lambdaJetPolarizationIons.h +++ b/PWGLF/DataModel/lambdaJetPolarizationIons.h @@ -146,9 +146,6 @@ DECLARE_SOA_TABLE(RingCollisions, "AOD", "RINGCOLLISIONS", lambdajetpol::CollisionId, lambdajetpol::CentFT0M, lambdajetpol::CentFT0C, - lambdajetpol::CentFT0CVariant1, - lambdajetpol::CentMFT, - lambdajetpol::CentNGlobal, lambdajetpol::CentFV0A ); diff --git a/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx b/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx index d555c4fcaed..37cad7b3227 100644 --- a/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx +++ b/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx @@ -102,15 +102,15 @@ using namespace o2::aod::rctsel; ///// Aliases for joined tables /// Collisions: -using SelCollisions = soa::Join; // Added PVMults to get MultNTracksPVeta1 as centrality estimator -using SelCollisionsSimple = soa::Join; // Simpler, for jets +using SelCollisions = soa::Join; // Added PVMults to get MultNTracksPVeta1 as centrality estimator +using SelCollisionsSimple = soa::Join; // Simpler, for jets /// V0s and Daughter tracks: // using V0Candidates = soa::Join; // using V0CandidatesSimple = soa::Join; // No TOF /// To run in RAW data: -// using V0Candidates = aod::V0Datas; +// using V0Candidates = aod::V0Datas; // TODO: possible quicker subscription for analysis that do not require TOF. using V0CandidatesWithTOF = soa::Join; // Tables created by o2-analysis-lf-strangenesstofpid // using DauTracks = soa::Join; // Actually used subscriptions (smaller memory usage): @@ -129,9 +129,6 @@ using PseudoJetTracks = soa::Join 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:CentFT0CVariant1, 3:CentMFT, 4:CentNGlobal, 5:CentFV0A)"}; // Default is FT0M + 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) @@ -568,15 +565,9 @@ struct lambdajetpolarizationions { histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(23, "hasRingV0"); // Centrality: - histos.add("Centrality/hCentralityVsNGlobal", "hCentralityVsNGlobal", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisNch}); 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/hEventCentVsMultNGlobal", "hEventCentVsMultNGlobal", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisNch}); histos.add("Centrality/hEventCentVsMultFV0A", "hEventCentVsMultFV0A", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisMultFV0A}); - histos.add("Centrality/hEventMultFT0MvsMultNGlobal", "hEventMultFT0MvsMultNGlobal", kTH2D, {axisConfigurations.axisMultFT0M, axisConfigurations.axisNch}); - histos.add("Centrality/hEventMultFT0CvsMultNGlobal", "hEventMultFT0CvsMultNGlobal", kTH2D, {axisConfigurations.axisMultFT0C, axisConfigurations.axisNch}); - histos.add("Centrality/hEventMultFV0AvsMultNGlobal", "hEventMultFV0AvsMultNGlobal", kTH2D, {axisConfigurations.axisMultFV0A, axisConfigurations.axisNch}); - histos.add("Centrality/hEventMultPVvsMultNGlobal", "hEventMultPVvsMultNGlobal", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisNch}); histos.add("Centrality/hEventMultFT0CvsMultFV0A", "hEventMultFT0CvsMultFV0A", kTH2D, {axisConfigurations.axisMultFT0C, axisConfigurations.axisMultFV0A}); } @@ -929,9 +920,6 @@ struct lambdajetpolarizationions { { if (centralityEstimatorForQA == kCentFT0M) return collision.centFT0M(); else if (centralityEstimatorForQA == kCentFT0C) return collision.centFT0C(); - else if (centralityEstimatorForQA == kCentFT0CVariant1) return collision.centFT0CVariant1(); - else if (centralityEstimatorForQA == kCentMFT) return collision.centMFT(); - else if (centralityEstimatorForQA == kCentNGlobal) return collision.centNGlobal(); else if (centralityEstimatorForQA == kCentFV0A) return collision.centFV0A(); return -1.f; } @@ -1001,15 +989,9 @@ struct lambdajetpolarizationions { histos.fill(HIST("Centrality/hEventCentrality"), centrality); histos.fill(HIST("Centrality/hCentralityVsNch"), centrality, collision.multNTracksPVeta1()); if (doEventQA) { - histos.fill(HIST("Centrality/hCentralityVsNGlobal"), centrality, collision.multNTracksGlobal()); histos.fill(HIST("Centrality/hEventCentVsMultFT0M"), collision.centFT0M(), collision.multFT0A() + collision.multFT0C()); histos.fill(HIST("Centrality/hEventCentVsMultFT0C"), collision.centFT0C(), collision.multFT0C()); - histos.fill(HIST("Centrality/hEventCentVsMultNGlobal"), collision.centNGlobal(), collision.multNTracksGlobal()); histos.fill(HIST("Centrality/hEventCentVsMultFV0A"), collision.centFV0A(), collision.multFV0A()); - histos.fill(HIST("Centrality/hEventMultFT0MvsMultNGlobal"), collision.multFT0A() + collision.multFT0C(), collision.multNTracksGlobal()); - histos.fill(HIST("Centrality/hEventMultFT0CvsMultNGlobal"), collision.multFT0C(), collision.multNTracksGlobal()); - histos.fill(HIST("Centrality/hEventMultFV0AvsMultNGlobal"), collision.multFV0A(), collision.multNTracksGlobal()); - histos.fill(HIST("Centrality/hEventMultPVvsMultNGlobal"), collision.multNTracksPVeta1(), collision.multNTracksGlobal()); histos.fill(HIST("Centrality/hEventMultFT0CvsMultFV0A"), collision.multFT0C(), collision.multFV0A()); } return; @@ -1644,9 +1626,6 @@ struct lambdajetpolarizationions { tableCollisions(collIdx, collision.centFT0M(), collision.centFT0C(), - collision.centFT0CVariant1(), - collision.centMFT(), - collision.centNGlobal(), collision.centFV0A() ); // (TODO: add InteractionRate info and other useful cuts for later on in the analysis?) @@ -1705,9 +1684,9 @@ struct lambdajetpolarizationions { v0.mLambda(), v0.mAntiLambda(), v0.positivept(), v0.positiveeta(), v0.positivephi(), v0.negativept(), v0.negativeeta(), v0.negativephi(), - v0.v0cosPA(), v0.v0radius(), v0.dcaV0daughters(), v0.dcapostopv(), v0.dcanegtopv(), posTrackExtra.tpcNSigmaPr(), posTrackExtra.tpcNSigmaPi(), - negTrackExtra.tpcNSigmaPr(), negTrackExtra.tpcNSigmaPi() + negTrackExtra.tpcNSigmaPr(), negTrackExtra.tpcNSigmaPi(), + v0.v0cosPA(), v0.v0radius(), v0.dcaV0daughters(), v0.dcapostopv(), v0.dcanegtopv() ); if (doEventQA && !validV0AlreadyFound) fillEventSelectionQA(lastBinEvSel, centrality); // hasRingV0 passes validV0AlreadyFound = true; From 78bb47a1a5836fde67fb69085769956799266795 Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Wed, 4 Mar 2026 11:04:40 -0300 Subject: [PATCH 36/40] Removing old centrality estimators from derived data consumer --- .../Strangeness/lambdaJetPolarizationIonsDerived.cxx | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx index 79733ef2bd0..a21b6316dbc 100644 --- a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx @@ -70,9 +70,6 @@ constexpr double polPrefactorAntiLambda = 3.0/antiLambdaWeakDecayConstant; enum CentEstimator { kCentFT0C = 0, kCentFT0M, - kCentFT0CVariant1, - kCentMFT, - kCentNGlobal, kCentFV0A }; @@ -184,7 +181,7 @@ struct lambdajetpolarizationionsderived { 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:CentFT0CVariant1, 3:CentMFT, 4:CentNGlobal, 5:CentFV0A)"}; // Default is FT0M + Configurable centralityEstimator{"centralityEstimator", kCentFT0M, "Run 3 centrality estimator (0:CentFT0C, 1:CentFT0M, 2:CentFV0A)"}; // Default is FT0M // QAs that purposefully break the analysis // -- All of these tests should give us zero signal if the source is truly Lambda Polarization from vortices @@ -415,9 +412,6 @@ struct lambdajetpolarizationionsderived { { if (centralityEstimator == kCentFT0M) return collision.centFT0M(); else if (centralityEstimator == kCentFT0C) return collision.centFT0C(); - else if (centralityEstimator == kCentFT0CVariant1) return collision.centFT0CVariant1(); - else if (centralityEstimator == kCentMFT) return collision.centMFT(); - else if (centralityEstimator == kCentNGlobal) return collision.centNGlobal(); else if (centralityEstimator == kCentFV0A) return collision.centFV0A(); return -1.f; } From dbad51039affbd1a5939a789bea33035fba276cd Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Wed, 4 Mar 2026 14:44:20 -0300 Subject: [PATCH 37/40] Final fix before pull request -- Fixing repeated histogram name on pRingObservable2ndJetMass QA histogram --- 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 a21b6316dbc..bfa7874b12b 100644 --- a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx @@ -341,7 +341,7 @@ struct lambdajetpolarizationionsderived { // 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(), "pRingObservableLeadPMass;m_{p#pi};<#it{R}>", kTProfile, {axisConfigurations.axisLambdaMassSigExtract}); + histos.add((folder + "/pRingObservable2ndJetMass").c_str(), "pRingObservable2ndJetMass;m_{p#pi};<#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}); // TProfile2D: vs Mass (DeltaTheta) From 6867671425f9188e95150644d8ff5fbcdb9c27b1 Mon Sep 17 00:00:00 2001 From: cmuncinelli Date: Wed, 4 Mar 2026 14:59:03 -0300 Subject: [PATCH 38/40] Initializing variables to avoid warnings (following O2 Code Guidelines) --- 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 bfa7874b12b..5fe9ed3cc2e 100644 --- a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx @@ -669,7 +669,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) * - float PolStarX, PolStarY, PolStarZ; + 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(); From d321e59640bfbc77b61cff454b12eb9ca11fc6ce Mon Sep 17 00:00:00 2001 From: ALICE Action Bot Date: Wed, 4 Mar 2026 18:22:36 +0000 Subject: [PATCH 39/40] Please consider the following formatting changes --- PWGLF/DataModel/lambdaJetPolarizationIons.h | 30 +- .../Strangeness/lambdaJetPolarizationIons.cxx | 3479 +++++++++-------- .../lambdaJetPolarizationIonsDerived.cxx | 1371 +++---- 3 files changed, 2520 insertions(+), 2360 deletions(-) diff --git a/PWGLF/DataModel/lambdaJetPolarizationIons.h b/PWGLF/DataModel/lambdaJetPolarizationIons.h index daac6f12489..fef233231c0 100644 --- a/PWGLF/DataModel/lambdaJetPolarizationIons.h +++ b/PWGLF/DataModel/lambdaJetPolarizationIons.h @@ -78,23 +78,23 @@ DECLARE_SOA_COLUMN(DcaNegToPV, dcaNegToPV, float); // Dynamic columns for jets (Px,Py,Pz): DECLARE_SOA_DYNAMIC_COLUMN(JetPx, jetPx, //! Jet px - [](float jetPt, float jetPhi) -> float {return jetPt * std::cos(jetPhi);}); + [](float jetPt, float jetPhi) -> float { return jetPt * std::cos(jetPhi); }); DECLARE_SOA_DYNAMIC_COLUMN(JetPy, jetPy, //! Jet py - [](float jetPt, float jetPhi) -> float {return jetPt * std::sin(jetPhi);}); + [](float jetPt, float jetPhi) -> float { return jetPt * std::sin(jetPhi); }); DECLARE_SOA_DYNAMIC_COLUMN(JetPz, jetPz, //! Jet pz - [](float jetPt, float jetEta) -> float {return jetPt * std::sinh(jetEta);}); + [](float jetPt, float jetEta) -> float { return jetPt * std::sinh(jetEta); }); // Same for leading particles: DECLARE_SOA_DYNAMIC_COLUMN(LeadParticlePx, leadParticlePx, //! Leading particle px - [](float leadParticlePt, float leadParticlePhi) -> float {return leadParticlePt * std::cos(leadParticlePhi);}); + [](float leadParticlePt, float leadParticlePhi) -> float { return leadParticlePt * std::cos(leadParticlePhi); }); DECLARE_SOA_DYNAMIC_COLUMN(LeadParticlePy, leadParticlePy, //! Leading particle py - [](float leadParticlePt, float leadParticlePhi) -> float {return leadParticlePt * std::sin(leadParticlePhi);}); + [](float leadParticlePt, float leadParticlePhi) -> float { return leadParticlePt * std::sin(leadParticlePhi); }); DECLARE_SOA_DYNAMIC_COLUMN(LeadParticlePz, leadParticlePz, //! Leading particle pz - [](float leadParticlePt, float leadParticleEta) -> float {return leadParticlePt * std::sinh(leadParticleEta);}); + [](float leadParticlePt, float leadParticleEta) -> float { return leadParticlePt * std::sinh(leadParticleEta); }); } // namespace lambdajetpol DECLARE_SOA_TABLE(RingJets, "AOD", "RINGJETS", // Renamed to follow convention on "s" at the end of table name. - lambdajetpol::CollisionId, // Changed to an internal O2 index, slightly different from usual o2::soa::Index<> though + lambdajetpol::CollisionId, // Changed to an internal O2 index, slightly different from usual o2::soa::Index<> though lambdajetpol::JetPt, lambdajetpol::JetEta, lambdajetpol::JetPhi, @@ -102,8 +102,7 @@ DECLARE_SOA_TABLE(RingJets, "AOD", "RINGJETS", // Renamed to follow convention o // Dynamic columns lambdajetpol::JetPx, // Explicitly binding to static columns lambdajetpol::JetPy, - lambdajetpol::JetPz - ); + lambdajetpol::JetPz); DECLARE_SOA_TABLE(RingLeadP, "AOD", "RINGLEADP", // Leading particle table lambdajetpol::CollisionId, @@ -113,8 +112,7 @@ DECLARE_SOA_TABLE(RingLeadP, "AOD", "RINGLEADP", // Leading particle table // Dynamic columns lambdajetpol::LeadParticlePx, lambdajetpol::LeadParticlePy, - lambdajetpol::LeadParticlePz - ); + lambdajetpol::LeadParticlePz); DECLARE_SOA_TABLE(RingLaV0s, "AOD", "RINGLAV0S", lambdajetpol::CollisionId, @@ -139,16 +137,14 @@ DECLARE_SOA_TABLE(RingLaV0s, "AOD", "RINGLAV0S", lambdajetpol::V0Radius, lambdajetpol::DcaV0Daughters, lambdajetpol::DcaPosToPV, - lambdajetpol::DcaNegToPV - ); + lambdajetpol::DcaNegToPV); DECLARE_SOA_TABLE(RingCollisions, "AOD", "RINGCOLLISIONS", lambdajetpol::CollisionId, lambdajetpol::CentFT0M, lambdajetpol::CentFT0C, - lambdajetpol::CentFV0A - ); - + lambdajetpol::CentFV0A); + } // namespace o2::aod -#endif // PWGLF_DATAMODEL_LAMBDAJETPOL_H_ \ No newline at end of file +#endif // PWGLF_DATAMODEL_LAMBDAJETPOL_H_ diff --git a/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx b/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx index 37cad7b3227..5c1e2a99f63 100644 --- a/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx +++ b/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx @@ -35,9 +35,9 @@ #include // O2 CCDB / Conditions +#include "DataFormatsParameters/GRPMagField.h" #include #include -#include "DataFormatsParameters/GRPMagField.h" // O2 Reconstruction Data Formats #include @@ -104,7 +104,7 @@ using namespace o2::aod::rctsel; /// Collisions: using SelCollisions = soa::Join; // Added PVMults to get MultNTracksPVeta1 as centrality estimator -using SelCollisionsSimple = soa::Join; // Simpler, for jets +using SelCollisionsSimple = soa::Join; // Simpler, for jets /// V0s and Daughter tracks: // using V0Candidates = soa::Join; @@ -113,1740 +113,1891 @@ using SelCollisionsSimple = soa::Join; // Simpler, // using V0Candidates = aod::V0Datas; // TODO: possible quicker subscription for analysis that do not require TOF. using V0CandidatesWithTOF = soa::Join; // Tables created by o2-analysis-lf-strangenesstofpid // using DauTracks = soa::Join; - // Actually used subscriptions (smaller memory usage): +// Actually used subscriptions (smaller memory usage): using DauTracks = soa::Join; /// Jets: using PseudoJetTracks = soa::Join; // Simpler tracks access. (Not using TracksIU and TracksCovIU. Did not use their info for now) - // , aod::pidTOFFullPi, aod::pidTOFFullKa, aod::pidTOFFullPr>; // Not using TOF right now due to some possible mismatches + // , aod::pidTOFFullPi, aod::pidTOFFullKa, aod::pidTOFFullPr>; // Not using TOF right now due to some possible mismatches /// MC: // using SimCollisions = soa::Join; // using DauTracksMC = soa::Join; - - enum CentEstimator { - kCentFT0C = 0, - kCentFT0M, - kCentFV0A + kCentFT0C = 0, + kCentFT0M, + kCentFV0A }; enum JetAlgorithm { - kKt = 0, - kCambridgeAachen, - kAntiKt + kKt = 0, + kCambridgeAachen, + kAntiKt }; enum JetRecombScheme { - kEScheme = 0, - kPtScheme = 1, - kPt2Scheme = 2, - kWTAScheme = 7 + kEScheme = 0, + kPtScheme = 1, + kPt2Scheme = 2, + kWTAScheme = 7 }; enum JetType { - kChargedJet = 0, - kFullJet, - kPhotonJet, - kZJet + kChargedJet = 0, + kFullJet, + kPhotonJet, + kZJet }; enum BkgSubtraction { - kNoSubtraction = 0, - kAreaBased, - kConstituentBased + kNoSubtraction = 0, + kAreaBased, + kConstituentBased }; ////////////////////////////////////////////// struct lambdajetpolarizationions { - // struct : ProducesGroup { - // } products; - Produces tableV0s; - Produces tableJets; - Produces tableLeadParticles; - Produces tableCollisions; - - // Define histogram registries: - HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - - // 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 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 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 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"}; - ///////////////////////////////////////////// - - // ///////////////////////////////////////////// - // MC block -- not implemented! (TODO) - // Configurable doMCAssociation{"doMCAssociation", true, "if MC, do MC association"}; - // Configurable doTreatPiToMuon{"doTreatPiToMuon", false, "Take pi decay into muon into account in MC"}; - // Configurable doCollisionAssociationQA{"doCollisionAssociationQA", true, "check collision association"}; - // ///////////////////////////////////////////// - - // TODO: COMPLEMENTARY ANALYSES TO STUDY SPURIOUS POLARIZATION SOURCES! - // TODO: add an event plane selection procedure to get an angle between the global polarization axis and the jet axis to uncouple polarizations? - // TODO: (related to previous comment) if we already have event plane, also estimate v_2-caused polarization. Hydro papers indicate observable is unsensitive to this spurious polarization, but this is a perfect consistency check. - // TODO: add a longitudinal polarization block of code to estimate other sources of polarization (and possibly study their differential dependence on the angle wrlt the jets and their rings)? - // TODO: add a block of code that calculates polarization from Lambda fragmentation to estimate the contamination of this third source of polarization - - - // Configurable groups: - struct : ConfigurableGroup { - std::string prefix = "eventSelections"; // JSON group name - Configurable requireSel8{"requireSel8", true, "require sel8 event selection"}; - Configurable requireTriggerTVX{"requireTriggerTVX", true, "require FT0 vertex (acceptable FT0C-FT0A time difference) at trigger level"}; // 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 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 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)"}; - 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 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 - 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 - Configurable minIR{"minIR", -1, "minimum IR collisions"}; - Configurable maxIR{"maxIR", -1, "maximum IR collisions"}; - } eventSelections; - - struct : ConfigurableGroup { - std::string prefix = "v0Selections"; // JSON group name - Configurable v0TypeSelection{"v0TypeSelection", 1, "select on a certain V0 type (leave negative if no selection desired)"}; - - // 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 - 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 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 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 v0radiusMax{"v0radiusMax", 1E5, "maximum V0 radius (cm)"}; - Configurable lambdaLifetimeCut{"lambdaLifetimeCut", 30., "lifetime cut (c*tau) for Lambda (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. - - // Track quality - Configurable minTPCrows{"minTPCrows", 70, "minimum TPC crossed rows"}; - Configurable minITSclusters{"minITSclusters", 3, "minimum ITS clusters"}; // Default is off - Configurable minTPCrowsOverFindableClusters{"minTPCrowsOverFindableClusters", -1, "minimum nbr of TPC crossed rows over findable clusters"}; - Configurable minTPCfoundOverFindableClusters{"minTPCfoundOverFindableClusters", -1, "minimum nbr of found over findable TPC clusters"}; - Configurable maxFractionTPCSharedClusters{"maxFractionTPCSharedClusters", 1e+09, "maximum fraction of TPC shared clusters"}; - Configurable maxITSchi2PerNcls{"maxITSchi2PerNcls", 36.0f, "maximum ITS chi2 per clusters"}; // Default is 1e+09. New values from StraInJets recommendations - Configurable maxTPCchi2PerNcls{"maxTPCchi2PerNcls", 4.0f, "maximum TPC chi2 per clusters"}; // Default is 1e+09 - Configurable skipTPConly{"skipTPConly", false, "skip V0s comprised of at least one TPC only prong"}; - Configurable requirePosITSonly{"requirePosITSonly", false, "require that positive track is ITSonly (overrides TPC quality)"}; - Configurable requireNegITSonly{"requireNegITSonly", false, "require that negative track is ITSonly (overrides TPC quality)"}; - Configurable rejectPosITSafterburner{"rejectPosITSafterburner", false, "reject positive track formed out of afterburner ITS tracks"}; - Configurable rejectNegITSafterburner{"rejectNegITSafterburner", false, "reject negative track formed out of afterburner ITS tracks"}; - Configurable requirePosITSafterburnerOnly{"requirePosITSafterburnerOnly", false, "require positive track formed out of afterburner ITS tracks"}; - Configurable requireNegITSafterburnerOnly{"requireNegITSafterburnerOnly", false, "require negative track formed out of afterburner ITS tracks"}; - Configurable rejectTPCsectorBoundary{"rejectTPCsectorBoundary", false, "reject tracks close to the TPC sector boundaries"}; - Configurable phiLowCut{"phiLowCut", "0.06/x+pi/18.0-0.06", "Low azimuth cut parametrisation"}; - 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 tofPidNsigmaCutLaPr{"tofPidNsigmaCutLaPr", 1e+6, "tofPidNsigmaCutLaPr"}; - Configurable tofPidNsigmaCutLaPi{"tofPidNsigmaCutLaPi", 1e+6, "tofPidNsigmaCutLaPi"}; - - // PID (TOF) - Configurable maxDeltaTimeProton{"maxDeltaTimeProton", 1e+9, "check maximum allowed time"}; - Configurable maxDeltaTimePion{"maxDeltaTimePion", 1e+9, "check maximum allowed time"}; - } v0Selections; - - // Helpers for the "isTrackFarFromTPCBoundary" function: - TF1* fPhiCutLow = new TF1("fPhiCutLow", v0Selections.phiLowCut.value.data(), 0, 100); - TF1* fPhiCutHigh = new TF1("fPhiCutHigh", v0Selections.phiHighCut.value.data(), 0, 100); - - // 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 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; - RCTFlagsChecker rctFlagsChecker{rctConfigurations.cfgRCTLabel.value}; - - // ML SELECTIONS BLOCK -- NOT IMPLEMENTED! (TODO) - - // CCDB options - struct : ConfigurableGroup { - std::string prefix = "ccdbConfigurations"; // JSON group name - Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; - Configurable grpPath{"grpPath", "GLO/GRP/GRP", "Path of the grp file"}; - Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; - Configurable lutPath{"lutPath", "GLO/Param/MatLUT", "Path of the Lut parametrization"}; - Configurable geoPath{"geoPath", "GLO/Config/GeometryAligned", "Path of the geometry file"}; - Configurable mVtxPath{"mVtxPath", "GLO/Calib/MeanVertex", "Path of the mean vertex file"}; - - // manual magnetic field: - Configurable useCustomMagField{"useCustomMagField", false, "Use custom magnetic field value"}; - Configurable customMagField{"customMagField", 5.0f, "Manually set magnetic field"}; - } ccdbConfigurations; - - // Instantiating CCDB: - o2::ccdb::CcdbApi ccdbApi; - Service ccdb; - - // Other useful variables: - ctpRateFetcher rateFetcher; - int mRunNumber; - float magField; - std::map metadata; - o2::parameters::GRPMagField* grpmag = nullptr; - - // Histogram axes configuration: - 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 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 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)"}; - ConfigurableAxis axisDCAdau{"axisDCAdau", {20, 0.0f, 2.0f}, "DCA (cm)"}; - 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"}; - ConfigurableAxis axisTPCsignal{"axisTPCsignal", {200, 0.0f, 200.0f}, "TPC signal"}; - ConfigurableAxis axisNsigmaTOF{"axisNsigmaTOF", {200, -10.0f, 10.0f}, "N sigma TOF"}; - ConfigurableAxis axisTOFdeltaT{"axisTOFdeltaT", {200, -5000.0f, 5000.0f}, "TOF Delta T (ps)"}; - ConfigurableAxis axisPhi{"axisPhi", {50, 0.0f, constants::math::TwoPI}, "Azimuth angle (rad)"}; - 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 - 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"}; - - // 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 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"}; - ConfigurableAxis axisDeltaEta{"axisDeltaEta", {50, -1.5f, 1.5f}, "#Delta #phi"}; // Calculated as twice the subtraction "eta_max=0.9 - R_min=0.2", with a margin - ConfigurableAxis axisDeltaR{"axisDeltaR", {50, 0, 3.5f}, "#Delta R"}; // From 0 to about the maximum Delta R possible with R = 0.2 - ConfigurableAxis axisEnergy{"axisEnergy",{200, 0.f, 200.f},"E_{jet} (GeV) (#pi mass hypothesis)"}; // Jet energy is not that well defined here, due to track mass hypothesis being of pions! This is just to include px,py,pz in full! - } axisConfigurations; - - // Jet selection configuration: - // (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 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. - - 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 - // 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!) - - // (TODO: Check which of these configurables might be useful for the photon-tagged and regular analyses) - // // Configurables from JE PWG: - // Configurable jetEWSPtMin{"jetEWSPtMin", 0.0, "minimum event-wise subtracted jet pT"}; - // Configurable jetEWSPtMax{"jetEWSPtMax", 1000.0, "maximum event-wise subtracted jet pT"}; - // Configurable jetGhostArea{"jetGhostArea", 0.005, "jet ghost area"}; - // Configurable ghostRepeat{"ghostRepeat", 0, "set to 0 to gain speed if you dont need area calculation"}; - // Configurable DoTriggering{"DoTriggering", false, "used for the charged jet trigger to remove the eta constraint on the jet axis"}; - // Configurable jetAreaFractionMin{"jetAreaFractionMin", -99.0, "used to make a cut on the jet areas"}; - // // cluster level configurables - // Configurable clusterDefinitionS{"clusterDefinition", "kV3Default", "cluster definition to be selected, e.g. V3Default"}; - // Configurable clusterEtaMin{"clusterEtaMin", -0.71, "minimum cluster eta"}; // For ECMAL: |eta| < 0.7, phi = 1.40 - 3.26 - // Configurable clusterEtaMax{"clusterEtaMax", 0.71, "maximum cluster eta"}; // For ECMAL: |eta| < 0.7, phi = 1.40 - 3.26 - // Configurable clusterPhiMin{"clusterPhiMin", 1.39, "minimum cluster phi"}; - // Configurable clusterPhiMax{"clusterPhiMax", 3.27, "maximum cluster phi"}; - // Configurable clusterEnergyMin{"clusterEnergyMin", 0.5, "minimum cluster energy in EMCAL (GeV)"}; - // Configurable clusterTimeMin{"clusterTimeMin", -25., "minimum Cluster time (ns)"}; - // Configurable clusterTimeMax{"clusterTimeMax", 25., "maximum Cluster time (ns)"}; - // Configurable clusterRejectExotics{"clusterRejectExotics", true, "Reject exotic clusters"}; - // Configurable hadronicCorrectionType{"hadronicCorrectionType", 0, "0 = no correction, 1 = CorrectedOneTrack1, 2 = CorrectedOneTrack2, 3 = CorrectedAllTracks1, 4 = CorrectedAllTracks2"}; - // Configurable doEMCALEventSelection{"doEMCALEventSelection", true, "apply the selection to the event alias_bit for full and neutral jets"}; - // Configurable doEMCALEventSelectionChargedJets{"doEMCALEventSelectionChargedJets", false, "apply the selection to the event alias_bit for charged jets"}; - - Configurable minLeadParticlePt{"minLeadParticlePt", 2.0f, "Minimum Pt for a lead track to be considered a valid proxy for a jet"}; // For OO, about 2 or 3 should be enough (z~0.3 of jet), and for PbPb maybe 8 GeV - } jetConfigurations; - - // Creating a short map to make sure the proper FastJet enums are used (safeguard against possible updates in FastJet indices): - fastjet::JetAlgorithm mapFJAlgorithm(int algoIdx){ - switch (algoIdx) { - case 0: return fastjet::kt_algorithm; - case 1: return fastjet::cambridge_algorithm; - case 2: return fastjet::antikt_algorithm; - default: - throw std::invalid_argument("Unknown jet algorithm"); - } + // struct : ProducesGroup { + // } products; + Produces tableV0s; + Produces tableJets; + Produces tableLeadParticles; + Produces tableCollisions; + + // Define histogram registries: + HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + // 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 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 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 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"}; + ///////////////////////////////////////////// + + // ///////////////////////////////////////////// + // MC block -- not implemented! (TODO) + // Configurable doMCAssociation{"doMCAssociation", true, "if MC, do MC association"}; + // Configurable doTreatPiToMuon{"doTreatPiToMuon", false, "Take pi decay into muon into account in MC"}; + // Configurable doCollisionAssociationQA{"doCollisionAssociationQA", true, "check collision association"}; + // ///////////////////////////////////////////// + + // TODO: COMPLEMENTARY ANALYSES TO STUDY SPURIOUS POLARIZATION SOURCES! + // TODO: add an event plane selection procedure to get an angle between the global polarization axis and the jet axis to uncouple polarizations? + // TODO: (related to previous comment) if we already have event plane, also estimate v_2-caused polarization. Hydro papers indicate observable is unsensitive to this spurious polarization, but this is a perfect consistency check. + // TODO: add a longitudinal polarization block of code to estimate other sources of polarization (and possibly study their differential dependence on the angle wrlt the jets and their rings)? + // TODO: add a block of code that calculates polarization from Lambda fragmentation to estimate the contamination of this third source of polarization + + // Configurable groups: + struct : ConfigurableGroup { + std::string prefix = "eventSelections"; // JSON group name + Configurable requireSel8{"requireSel8", true, "require sel8 event selection"}; + Configurable requireTriggerTVX{"requireTriggerTVX", true, "require FT0 vertex (acceptable FT0C-FT0A time difference) at trigger level"}; // 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 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 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)"}; + 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 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 + 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 + Configurable minIR{"minIR", -1, "minimum IR collisions"}; + Configurable maxIR{"maxIR", -1, "maximum IR collisions"}; + } eventSelections; + + struct : ConfigurableGroup { + std::string prefix = "v0Selections"; // JSON group name + Configurable v0TypeSelection{"v0TypeSelection", 1, "select on a certain V0 type (leave negative if no selection desired)"}; + + // 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 + 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 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 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 v0radiusMax{"v0radiusMax", 1E5, "maximum V0 radius (cm)"}; + Configurable lambdaLifetimeCut{"lambdaLifetimeCut", 30., "lifetime cut (c*tau) for Lambda (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. + + // Track quality + Configurable minTPCrows{"minTPCrows", 70, "minimum TPC crossed rows"}; + Configurable minITSclusters{"minITSclusters", 3, "minimum ITS clusters"}; // Default is off + Configurable minTPCrowsOverFindableClusters{"minTPCrowsOverFindableClusters", -1, "minimum nbr of TPC crossed rows over findable clusters"}; + Configurable minTPCfoundOverFindableClusters{"minTPCfoundOverFindableClusters", -1, "minimum nbr of found over findable TPC clusters"}; + Configurable maxFractionTPCSharedClusters{"maxFractionTPCSharedClusters", 1e+09, "maximum fraction of TPC shared clusters"}; + Configurable maxITSchi2PerNcls{"maxITSchi2PerNcls", 36.0f, "maximum ITS chi2 per clusters"}; // Default is 1e+09. New values from StraInJets recommendations + Configurable maxTPCchi2PerNcls{"maxTPCchi2PerNcls", 4.0f, "maximum TPC chi2 per clusters"}; // Default is 1e+09 + Configurable skipTPConly{"skipTPConly", false, "skip V0s comprised of at least one TPC only prong"}; + Configurable requirePosITSonly{"requirePosITSonly", false, "require that positive track is ITSonly (overrides TPC quality)"}; + Configurable requireNegITSonly{"requireNegITSonly", false, "require that negative track is ITSonly (overrides TPC quality)"}; + Configurable rejectPosITSafterburner{"rejectPosITSafterburner", false, "reject positive track formed out of afterburner ITS tracks"}; + Configurable rejectNegITSafterburner{"rejectNegITSafterburner", false, "reject negative track formed out of afterburner ITS tracks"}; + Configurable requirePosITSafterburnerOnly{"requirePosITSafterburnerOnly", false, "require positive track formed out of afterburner ITS tracks"}; + Configurable requireNegITSafterburnerOnly{"requireNegITSafterburnerOnly", false, "require negative track formed out of afterburner ITS tracks"}; + Configurable rejectTPCsectorBoundary{"rejectTPCsectorBoundary", false, "reject tracks close to the TPC sector boundaries"}; + Configurable phiLowCut{"phiLowCut", "0.06/x+pi/18.0-0.06", "Low azimuth cut parametrisation"}; + 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 tofPidNsigmaCutLaPr{"tofPidNsigmaCutLaPr", 1e+6, "tofPidNsigmaCutLaPr"}; + Configurable tofPidNsigmaCutLaPi{"tofPidNsigmaCutLaPi", 1e+6, "tofPidNsigmaCutLaPi"}; + + // PID (TOF) + Configurable maxDeltaTimeProton{"maxDeltaTimeProton", 1e+9, "check maximum allowed time"}; + Configurable maxDeltaTimePion{"maxDeltaTimePion", 1e+9, "check maximum allowed time"}; + } v0Selections; + + // Helpers for the "isTrackFarFromTPCBoundary" function: + TF1* fPhiCutLow = new TF1("fPhiCutLow", v0Selections.phiLowCut.value.data(), 0, 100); + TF1* fPhiCutHigh = new TF1("fPhiCutHigh", v0Selections.phiHighCut.value.data(), 0, 100); + + // 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 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; + RCTFlagsChecker rctFlagsChecker{rctConfigurations.cfgRCTLabel.value}; + + // ML SELECTIONS BLOCK -- NOT IMPLEMENTED! (TODO) + + // CCDB options + struct : ConfigurableGroup { + std::string prefix = "ccdbConfigurations"; // JSON group name + Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable grpPath{"grpPath", "GLO/GRP/GRP", "Path of the grp file"}; + Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; + Configurable lutPath{"lutPath", "GLO/Param/MatLUT", "Path of the Lut parametrization"}; + Configurable geoPath{"geoPath", "GLO/Config/GeometryAligned", "Path of the geometry file"}; + Configurable mVtxPath{"mVtxPath", "GLO/Calib/MeanVertex", "Path of the mean vertex file"}; + + // manual magnetic field: + Configurable useCustomMagField{"useCustomMagField", false, "Use custom magnetic field value"}; + Configurable customMagField{"customMagField", 5.0f, "Manually set magnetic field"}; + } ccdbConfigurations; + + // Instantiating CCDB: + o2::ccdb::CcdbApi ccdbApi; + Service ccdb; + + // Other useful variables: + ctpRateFetcher rateFetcher; + int mRunNumber; + float magField; + std::map metadata; + o2::parameters::GRPMagField* grpmag = nullptr; + + // Histogram axes configuration: + 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 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 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)"}; + ConfigurableAxis axisDCAdau{"axisDCAdau", {20, 0.0f, 2.0f}, "DCA (cm)"}; + 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"}; + ConfigurableAxis axisTPCsignal{"axisTPCsignal", {200, 0.0f, 200.0f}, "TPC signal"}; + ConfigurableAxis axisNsigmaTOF{"axisNsigmaTOF", {200, -10.0f, 10.0f}, "N sigma TOF"}; + ConfigurableAxis axisTOFdeltaT{"axisTOFdeltaT", {200, -5000.0f, 5000.0f}, "TOF Delta T (ps)"}; + ConfigurableAxis axisPhi{"axisPhi", {50, 0.0f, constants::math::TwoPI}, "Azimuth angle (rad)"}; + 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 + 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"}; + + // 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 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"}; + ConfigurableAxis axisDeltaEta{"axisDeltaEta", {50, -1.5f, 1.5f}, "#Delta #phi"}; // Calculated as twice the subtraction "eta_max=0.9 - R_min=0.2", with a margin + ConfigurableAxis axisDeltaR{"axisDeltaR", {50, 0, 3.5f}, "#Delta R"}; // From 0 to about the maximum Delta R possible with R = 0.2 + ConfigurableAxis axisEnergy{"axisEnergy", {200, 0.f, 200.f}, "E_{jet} (GeV) (#pi mass hypothesis)"}; // Jet energy is not that well defined here, due to track mass hypothesis being of pions! This is just to include px,py,pz in full! + } axisConfigurations; + + // Jet selection configuration: + // (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 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. + + 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 + // 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!) + + // (TODO: Check which of these configurables might be useful for the photon-tagged and regular analyses) + // // Configurables from JE PWG: + // Configurable jetEWSPtMin{"jetEWSPtMin", 0.0, "minimum event-wise subtracted jet pT"}; + // Configurable jetEWSPtMax{"jetEWSPtMax", 1000.0, "maximum event-wise subtracted jet pT"}; + // Configurable jetGhostArea{"jetGhostArea", 0.005, "jet ghost area"}; + // Configurable ghostRepeat{"ghostRepeat", 0, "set to 0 to gain speed if you dont need area calculation"}; + // Configurable DoTriggering{"DoTriggering", false, "used for the charged jet trigger to remove the eta constraint on the jet axis"}; + // Configurable jetAreaFractionMin{"jetAreaFractionMin", -99.0, "used to make a cut on the jet areas"}; + // // cluster level configurables + // Configurable clusterDefinitionS{"clusterDefinition", "kV3Default", "cluster definition to be selected, e.g. V3Default"}; + // Configurable clusterEtaMin{"clusterEtaMin", -0.71, "minimum cluster eta"}; // For ECMAL: |eta| < 0.7, phi = 1.40 - 3.26 + // Configurable clusterEtaMax{"clusterEtaMax", 0.71, "maximum cluster eta"}; // For ECMAL: |eta| < 0.7, phi = 1.40 - 3.26 + // Configurable clusterPhiMin{"clusterPhiMin", 1.39, "minimum cluster phi"}; + // Configurable clusterPhiMax{"clusterPhiMax", 3.27, "maximum cluster phi"}; + // Configurable clusterEnergyMin{"clusterEnergyMin", 0.5, "minimum cluster energy in EMCAL (GeV)"}; + // Configurable clusterTimeMin{"clusterTimeMin", -25., "minimum Cluster time (ns)"}; + // Configurable clusterTimeMax{"clusterTimeMax", 25., "maximum Cluster time (ns)"}; + // Configurable clusterRejectExotics{"clusterRejectExotics", true, "Reject exotic clusters"}; + // Configurable hadronicCorrectionType{"hadronicCorrectionType", 0, "0 = no correction, 1 = CorrectedOneTrack1, 2 = CorrectedOneTrack2, 3 = CorrectedAllTracks1, 4 = CorrectedAllTracks2"}; + // Configurable doEMCALEventSelection{"doEMCALEventSelection", true, "apply the selection to the event alias_bit for full and neutral jets"}; + // Configurable doEMCALEventSelectionChargedJets{"doEMCALEventSelectionChargedJets", false, "apply the selection to the event alias_bit for charged jets"}; + + Configurable minLeadParticlePt{"minLeadParticlePt", 2.0f, "Minimum Pt for a lead track to be considered a valid proxy for a jet"}; // For OO, about 2 or 3 should be enough (z~0.3 of jet), and for PbPb maybe 8 GeV + } jetConfigurations; + + // Creating a short map to make sure the proper FastJet enums are used (safeguard against possible updates in FastJet indices): + fastjet::JetAlgorithm mapFJAlgorithm(int algoIdx) + { + switch (algoIdx) { + case 0: + return fastjet::kt_algorithm; + case 1: + return fastjet::cambridge_algorithm; + case 2: + return fastjet::antikt_algorithm; + default: + throw std::invalid_argument("Unknown jet algorithm"); } - fastjet::RecombinationScheme mapFJRecombScheme(int schemeIdx){ - switch (schemeIdx){ - case 0: return fastjet::E_scheme; - case 1: return fastjet::pt_scheme; - case 2: return fastjet::pt2_scheme; - case 7: return fastjet::WTA_pt_scheme; - default: - throw std::invalid_argument("Unknown recombination scheme"); - } + } + fastjet::RecombinationScheme mapFJRecombScheme(int schemeIdx) + { + switch (schemeIdx) { + case 0: + return fastjet::E_scheme; + case 1: + return fastjet::pt_scheme; + case 2: + return fastjet::pt2_scheme; + case 7: + return fastjet::WTA_pt_scheme; + default: + throw std::invalid_argument("Unknown recombination scheme"); + } + } + + // Track analysis parameters -- A specific group that is different from the v0Selections. In jet analyses we need to control our PseudoJet candidates! + // (TODO: include minimal selection criteria for electrons, muons and photons) + // Notice you do NOT need any PID for the PseudoJet candidates! Only need is to know the 4-momentum appropriately. Thus removed nsigma checks on PID + struct : ConfigurableGroup { + std::string prefix = "pseudoJetCandidateTrackSelections"; // JSON group name + Configurable minNCrossedRowsTPC{"minNCrossedRowsTPC", 70, "Minimum number of TPC crossed rows"}; + Configurable minITSnCls{"minITSnCls", -1, "Minimum number of ITS clusters"}; + Configurable maxChi2TPC{"maxChi2TPC", 5.0f, "Maximum chi2 per cluster TPC"}; // Loose cuts for pseudojet candidate selection + Configurable maxChi2ITS{"maxChi2ITS", 40.0f, "Maximum chi2 per cluster ITS"}; + Configurable etaCut{"etaCut", 0.9f, "Maximum eta absolute value"}; // (TODO: same test as the previous 0.8 eta cut) + + Configurable minCandidatePt{"minCandidatePt", 0.15f, "Minimum track pt for pseudojet candidate (GeV/c)"}; // Reduces number of pseudojet candidates from IR radiation + // (TODO: test these minimal ratios to suppress split tracks in high occupancy PbPb or OO) + // 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"}; + + // 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)"}; + + // Configurable maxDCAxy{"maxDCAxy", 2.4f,"Max DCAxy to primary vertex [cm]"}; + // Using same cuts as the StrangenessInJets analysis, with a pt dependence (which may bias high pt, so use with care): + Configurable dcaxyMaxTrackPar0{"dcaxyMaxTrackPar0", 0.0105f, "Asymptotic DCA resolution at high pt [cm]"}; + Configurable dcaxyMaxTrackPar1{"dcaxyMaxTrackPar1", 0.035f, "Low pt multiple-scattering term for DCA resolution [cm*(GeV/c)^Par2]"}; + Configurable dcaxyMaxTrackPar2{"dcaxyMaxTrackPar2", 1.1f, "Exponent of pt dependence of DCA resolution"}; + } pseudoJetCandidateTrackSelections; + + JetBkgSubUtils backgroundSub; + + void init(InitContext const&) + { + // setting CCDB service + ccdb->setURL(ccdbConfigurations.ccdbUrl); + ccdb->setCaching(true); + ccdb->setFatalWhenNull(false); + + // Initialise the RCTFlagsChecker + rctFlagsChecker.init(rctConfigurations.cfgRCTLabel.value, rctConfigurations.cfgCheckZDC, rctConfigurations.cfgTreatLimitedAcceptanceAsBad); + + // Event Counters + histos.add("hEventSelection", "hEventSelection", kTH1D, {{23, -0.5f, +20.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"); + if (doPPAnalysis) { + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(17, "INEL>0"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(18, "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(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"); + // (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.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"); + if (doPPAnalysis) { + histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(17, "INEL>0"); + histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(18, "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(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"); + + // 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}); } - // Track analysis parameters -- A specific group that is different from the v0Selections. In jet analyses we need to control our PseudoJet candidates! - // (TODO: include minimal selection criteria for electrons, muons and photons) - // Notice you do NOT need any PID for the PseudoJet candidates! Only need is to know the 4-momentum appropriately. Thus removed nsigma checks on PID - struct : ConfigurableGroup { - std::string prefix = "pseudoJetCandidateTrackSelections"; // JSON group name - Configurable minNCrossedRowsTPC{"minNCrossedRowsTPC", 70, "Minimum number of TPC crossed rows"}; - Configurable minITSnCls{"minITSnCls", -1, "Minimum number of ITS clusters"}; - Configurable maxChi2TPC{"maxChi2TPC", 5.0f, "Maximum chi2 per cluster TPC"}; // Loose cuts for pseudojet candidate selection - Configurable maxChi2ITS{"maxChi2ITS", 40.0f, "Maximum chi2 per cluster ITS"}; - Configurable etaCut{"etaCut", 0.9f, "Maximum eta absolute value"}; // (TODO: same test as the previous 0.8 eta cut) - - Configurable minCandidatePt{"minCandidatePt", 0.15f,"Minimum track pt for pseudojet candidate (GeV/c)"}; // Reduces number of pseudojet candidates from IR radiation - // (TODO: test these minimal ratios to suppress split tracks in high occupancy PbPb or OO) - // 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"}; - - // 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)"}; - - // Configurable maxDCAxy{"maxDCAxy", 2.4f,"Max DCAxy to primary vertex [cm]"}; - // Using same cuts as the StrangenessInJets analysis, with a pt dependence (which may bias high pt, so use with care): - Configurable dcaxyMaxTrackPar0{"dcaxyMaxTrackPar0", 0.0105f, "Asymptotic DCA resolution at high pt [cm]"}; - Configurable dcaxyMaxTrackPar1{"dcaxyMaxTrackPar1", 0.035f, "Low pt multiple-scattering term for DCA resolution [cm*(GeV/c)^Par2]"}; - Configurable dcaxyMaxTrackPar2{"dcaxyMaxTrackPar2", 1.1f, "Exponent of pt dependence of DCA resolution"}; - } pseudoJetCandidateTrackSelections; - - JetBkgSubUtils backgroundSub; - - void init(InitContext const&){ - // setting CCDB service - ccdb->setURL(ccdbConfigurations.ccdbUrl); - ccdb->setCaching(true); - ccdb->setFatalWhenNull(false); - - // Initialise the RCTFlagsChecker - rctFlagsChecker.init(rctConfigurations.cfgRCTLabel.value, rctConfigurations.cfgCheckZDC, rctConfigurations.cfgTreatLimitedAcceptanceAsBad); - - // Event Counters - histos.add("hEventSelection", "hEventSelection", kTH1D, {{23, -0.5f, +20.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"); - if (doPPAnalysis) { - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(17, "INEL>0"); - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(18, "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(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"); - // (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.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"); - if (doPPAnalysis) { - histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(17, "INEL>0"); - histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(18, "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(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"); - - // 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("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", kTH1D, {{100, -20.0f, +20.0f}}); - histos.add("hCentralityVsPVz", "hCentralityVsPVz", 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}); - - // for QA and test purposes - // auto hRawCentrality = histos.add("Centrality/hRawCentrality", "hRawCentrality", kTH1D, {axisConfigurations.axisRawCentrality}); - - // for (int ii = 1; ii < 101; ii++) { - // float value = 100.5f - static_cast(ii); - // hRawCentrality->SetBinContent(ii, value); - // } - - ////////////////////////////////////////////////////////////// - /// Lambda / AntiLambda V0 selection QA - ////////////////////////////////////////////////////////////// - struct CutLabel{std::string label;bool enabled;}; // A method of hiding labels of selections which were not used! - std::vector v0LambdaSelectionLabels = { - {"All V0 candidates",true}, - {"V0 radius (min)",true},{"V0 radius (max)",true},{"V0 cosPA",true},{"DCA_{V0 daughters}",true},{"|y_{#Lambda}|",v0Selections.rapidityCut>0.f}, - {"K^{0}_{S} mass rejection",v0Selections.compMassRejection>=0.f}, - {"ITS clusters (pos)",v0Selections.minITSclusters>0},{"ITS #chi^{2}/N_{cls} (pos)",v0Selections.maxITSchi2PerNcls<1e8}, - {"Reject ITS afterburner (pos)",v0Selections.rejectPosITSafterburner},{"Require ITS afterburner (pos)",v0Selections.requirePosITSafterburnerOnly}, - {"ITS clusters (neg)",v0Selections.minITSclusters>0},{"ITS #chi^{2}/N_{cls} (neg)",v0Selections.maxITSchi2PerNcls<1e8}, - {"Reject ITS afterburner (neg)",v0Selections.rejectNegITSafterburner},{"Require ITS afterburner (neg)",v0Selections.requireNegITSafterburnerOnly}, - {"TPC crossed rows (pos)",v0Selections.minTPCrows>0},{"TPC #chi^{2}/N_{cls} (pos)",v0Selections.maxTPCchi2PerNcls<1e8}, - {"TPC rows / findable (pos)",v0Selections.minTPCrowsOverFindableClusters>=0},{"TPC found / findable (pos)",v0Selections.minTPCfoundOverFindableClusters>=0}, - {"TPC shared clusters (pos)",v0Selections.maxFractionTPCSharedClusters<1e8},{"TPC sector boundary (pos)",v0Selections.rejectTPCsectorBoundary}, - {"TPC crossed rows (neg)",v0Selections.minTPCrows>0},{"TPC #chi^{2}/N_{cls} (neg)",v0Selections.maxTPCchi2PerNcls<1e8}, - {"TPC rows / findable (neg)",v0Selections.minTPCrowsOverFindableClusters>=0},{"TPC found / findable (neg)",v0Selections.minTPCfoundOverFindableClusters>=0}, - {"TPC shared clusters (neg)",v0Selections.maxFractionTPCSharedClusters<1e8},{"TPC sector boundary (neg)",v0Selections.rejectTPCsectorBoundary}, - {"Require ITS-only (pos)",v0Selections.requirePosITSonly},{"Require ITS-only (neg)",v0Selections.requireNegITSonly}, - {"Reject TPC-only (pos)",v0Selections.skipTPConly},{"Reject TPC-only (neg)",v0Selections.skipTPConly}, - }; // First, the labels that are hypothesis-agnostic - // Adding the Lambda or AntiLambda hypothesis labels as needed: - auto addHypothesis = [&](bool isLambda, bool analysisEnabled) { - if (!analysisEnabled) return; // i.e., don't add these labels if not analyzing said particle type - std::string p = isLambda ? "#Lambda: " : "#bar{#Lambda}: "; - v0LambdaSelectionLabels.insert(v0LambdaSelectionLabels.end(), { - {p + "DCA_{p} to PV", true}, - {p + "DCA_{#pi} to PV", true}, - {p + "TPC PID p", v0Selections.tpcPidNsigmaCut > 0}, - {p + "TPC PID #pi", v0Selections.tpcPidNsigmaCut > 0}, - {p + "TOF #Delta t p", v0Selections.maxDeltaTimeProton < 1e+9}, - {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} - }); - }; - 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 - addHypothesis(Lambda, analyseLambda); - addHypothesis(AntiLambda, analyseAntiLambda); - - auto hSelectionV0s = histos.add("GeneralQA/hSelectionV0s","V0 #rightarrow #Lambda / #bar{#Lambda} selection flow", kTH1D, - {{(int)v0LambdaSelectionLabels.size(),-0.5,(double)v0LambdaSelectionLabels.size()-0.5}}); - for(size_t i=0; iGetXaxis()->SetBinLabel(i+1,lbl.c_str()); // First non-underflow bin is bin 1 - } - //////////////////////////////////////////////// - // Jet track candidate selection flow (analogous to hSelectionV0s): - // Each label's "enabled" flag reflects whether the corresponding configurable - // makes that cut active, so disabled stages are shown in grey in the output. - std::vector jetTrackSelectionLabels = { - {"All track candidates", true}, - {"ITS clusters (min)", pseudoJetCandidateTrackSelections.minITSnCls >= 0}, - {"TPC crossed rows (min)", pseudoJetCandidateTrackSelections.minNCrossedRowsTPC > 0}, - {"TPC #chi^{2}/N_{cls} (max)", pseudoJetCandidateTrackSelections.maxChi2TPC < 1.e8f}, - {"ITS #chi^{2}/N_{cls} (max)", pseudoJetCandidateTrackSelections.maxChi2ITS < 1.e8f}, - {"p_{T} min", pseudoJetCandidateTrackSelections.minCandidatePt > 0.f}, - {"|#eta| cut", pseudoJetCandidateTrackSelections.etaCut < 1.5f}, - {"DCA_{z} to PV", pseudoJetCandidateTrackSelections.doDCAcuts.value}, - {"DCA_{xy} to PV (parametric)", pseudoJetCandidateTrackSelections.doDCAcuts.value}, - }; - auto hSelectionJetTracks = histos.add("GeneralQA/hSelectionJetTracks", "Charged pseudojet candidate selection flow", kTH1D, - {{(int)jetTrackSelectionLabels.size(), -0.5, (double)jetTrackSelectionLabels.size() - 0.5}}); - for (size_t i = 0; i < jetTrackSelectionLabels.size(); ++i) { - auto lbl = jetTrackSelectionLabels[i].label; - if (!jetTrackSelectionLabels[i].enabled) lbl = "#color[16]{(off) " + lbl + "}"; - hSelectionJetTracks->GetXaxis()->SetBinLabel(i + 1, lbl.c_str()); - } - //////////////////////////////////////////////// - - // 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}); - // 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}); - 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}); - } - 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}); - } - // (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}}); - 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}); - 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}); - } - 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}); - } - 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}); - } - } + // (TODO: add MC centrality vs PVz histos) - if (analyseLambda) { - histos.add("hMassLambda", "hMassLambda", kTH1D, {axisConfigurations.axisLambdaMass}); - histos.add("Lambda/hLambdasPerEvent", "hLambdasPerEvent", kTH1D, {{15,0,15}}); - } - if (analyseAntiLambda) { - histos.add("hMassAntiLambda", "hMassAntiLambda", kTH1D, {axisConfigurations.axisLambdaMass}); - histos.add("AntiLambda/hAntiLambdasPerEvent", "hAntiLambdasPerEvent", 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("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}); - // 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}); - // --- 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}); - // --- 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}); - } - 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}); - // --- 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}); - // --- 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}); - } - } + // for QA and test purposes + // auto hRawCentrality = histos.add("Centrality/hRawCentrality", "hRawCentrality", kTH1D, {axisConfigurations.axisRawCentrality}); - 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}); - } - - // 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}); - 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}); - } - 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}); - } - } + // for (int ii = 1; ii < 101; ii++) { + // float value = 100.5f - static_cast(ii); + // hRawCentrality->SetBinContent(ii, value); + // } - // 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/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 - if (doJetKinematicsQA){ - 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/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/hLeadingJetPt", "hLeadingJetPt", kTH1D, {axisConfigurations.axisJetPt}); - histos.add("JetKinematicsQA/hLeadingJetEta", "hLeadingJetEta", kTH1D, {axisConfigurations.axisEta}); - 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/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 - - // 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}); - - // 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}); - - //////////////////////////// - // 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}); - - // 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}); - - // Leading particle correlations: - 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}); - - // Main "Leading jet vs leading particle" correlations: - histos.add("JetVsLeadingParticleQA/h2dJetsPerEventvsLeadParticlePt","h2dJetsPerEventvsLeadParticlePt",kTH2D,{axisConfigurations.JetsPerEvent, 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}); - } + ////////////////////////////////////////////////////////////// + /// Lambda / AntiLambda V0 selection QA + ////////////////////////////////////////////////////////////// + struct CutLabel { + std::string label; + bool enabled; + }; // A method of hiding labels of selections which were not used! + std::vector v0LambdaSelectionLabels = { + {"All V0 candidates", true}, + {"V0 radius (min)", true}, + {"V0 radius (max)", true}, + {"V0 cosPA", true}, + {"DCA_{V0 daughters}", true}, + {"|y_{#Lambda}|", v0Selections.rapidityCut > 0.f}, + {"K^{0}_{S} mass rejection", v0Selections.compMassRejection >= 0.f}, + {"ITS clusters (pos)", v0Selections.minITSclusters > 0}, + {"ITS #chi^{2}/N_{cls} (pos)", v0Selections.maxITSchi2PerNcls < 1e8}, + {"Reject ITS afterburner (pos)", v0Selections.rejectPosITSafterburner}, + {"Require ITS afterburner (pos)", v0Selections.requirePosITSafterburnerOnly}, + {"ITS clusters (neg)", v0Selections.minITSclusters > 0}, + {"ITS #chi^{2}/N_{cls} (neg)", v0Selections.maxITSchi2PerNcls < 1e8}, + {"Reject ITS afterburner (neg)", v0Selections.rejectNegITSafterburner}, + {"Require ITS afterburner (neg)", v0Selections.requireNegITSafterburnerOnly}, + {"TPC crossed rows (pos)", v0Selections.minTPCrows > 0}, + {"TPC #chi^{2}/N_{cls} (pos)", v0Selections.maxTPCchi2PerNcls < 1e8}, + {"TPC rows / findable (pos)", v0Selections.minTPCrowsOverFindableClusters >= 0}, + {"TPC found / findable (pos)", v0Selections.minTPCfoundOverFindableClusters >= 0}, + {"TPC shared clusters (pos)", v0Selections.maxFractionTPCSharedClusters < 1e8}, + {"TPC sector boundary (pos)", v0Selections.rejectTPCsectorBoundary}, + {"TPC crossed rows (neg)", v0Selections.minTPCrows > 0}, + {"TPC #chi^{2}/N_{cls} (neg)", v0Selections.maxTPCchi2PerNcls < 1e8}, + {"TPC rows / findable (neg)", v0Selections.minTPCrowsOverFindableClusters >= 0}, + {"TPC found / findable (neg)", v0Selections.minTPCfoundOverFindableClusters >= 0}, + {"TPC shared clusters (neg)", v0Selections.maxFractionTPCSharedClusters < 1e8}, + {"TPC sector boundary (neg)", v0Selections.rejectTPCsectorBoundary}, + {"Require ITS-only (pos)", v0Selections.requirePosITSonly}, + {"Require ITS-only (neg)", v0Selections.requireNegITSonly}, + {"Reject TPC-only (pos)", v0Selections.skipTPConly}, + {"Reject TPC-only (neg)", v0Selections.skipTPConly}, + }; // First, the labels that are hypothesis-agnostic + // Adding the Lambda or AntiLambda hypothesis labels as needed: + auto addHypothesis = [&](bool isLambda, bool analysisEnabled) { + if (!analysisEnabled) + return; // i.e., don't add these labels if not analyzing said particle type + std::string p = isLambda ? "#Lambda: " : "#bar{#Lambda}: "; + v0LambdaSelectionLabels.insert(v0LambdaSelectionLabels.end(), {{p + "DCA_{p} to PV", true}, + {p + "DCA_{#pi} to PV", true}, + {p + "TPC PID p", v0Selections.tpcPidNsigmaCut > 0}, + {p + "TPC PID #pi", v0Selections.tpcPidNsigmaCut > 0}, + {p + "TOF #Delta t p", v0Selections.maxDeltaTimeProton < 1e+9}, + {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}}); + }; + 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 + addHypothesis(Lambda, analyseLambda); + addHypothesis(AntiLambda, analyseAntiLambda); + + auto hSelectionV0s = histos.add("GeneralQA/hSelectionV0s", "V0 #rightarrow #Lambda / #bar{#Lambda} selection flow", kTH1D, + {{(int)v0LambdaSelectionLabels.size(), -0.5, (double)v0LambdaSelectionLabels.size() - 0.5}}); + for (size_t i = 0; i < v0LambdaSelectionLabels.size(); ++i) { + auto lbl = v0LambdaSelectionLabels[i].label; + if (!v0LambdaSelectionLabels[i].enabled) + lbl = "#color[16]{(off) " + lbl + "}"; + hSelectionV0s->GetXaxis()->SetBinLabel(i + 1, lbl.c_str()); // First non-underflow bin is bin 1 + } + //////////////////////////////////////////////// + // Jet track candidate selection flow (analogous to hSelectionV0s): + // Each label's "enabled" flag reflects whether the corresponding configurable + // makes that cut active, so disabled stages are shown in grey in the output. + std::vector jetTrackSelectionLabels = { + {"All track candidates", true}, + {"ITS clusters (min)", pseudoJetCandidateTrackSelections.minITSnCls >= 0}, + {"TPC crossed rows (min)", pseudoJetCandidateTrackSelections.minNCrossedRowsTPC > 0}, + {"TPC #chi^{2}/N_{cls} (max)", pseudoJetCandidateTrackSelections.maxChi2TPC < 1.e8f}, + {"ITS #chi^{2}/N_{cls} (max)", pseudoJetCandidateTrackSelections.maxChi2ITS < 1.e8f}, + {"p_{T} min", pseudoJetCandidateTrackSelections.minCandidatePt > 0.f}, + {"|#eta| cut", pseudoJetCandidateTrackSelections.etaCut < 1.5f}, + {"DCA_{z} to PV", pseudoJetCandidateTrackSelections.doDCAcuts.value}, + {"DCA_{xy} to PV (parametric)", pseudoJetCandidateTrackSelections.doDCAcuts.value}, + }; + auto hSelectionJetTracks = histos.add("GeneralQA/hSelectionJetTracks", "Charged pseudojet candidate selection flow", kTH1D, + {{(int)jetTrackSelectionLabels.size(), -0.5, (double)jetTrackSelectionLabels.size() - 0.5}}); + for (size_t i = 0; i < jetTrackSelectionLabels.size(); ++i) { + auto lbl = jetTrackSelectionLabels[i].label; + if (!jetTrackSelectionLabels[i].enabled) + lbl = "#color[16]{(off) " + lbl + "}"; + hSelectionJetTracks->GetXaxis()->SetBinLabel(i + 1, lbl.c_str()); + } + //////////////////////////////////////////////// + + // 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}); + // 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}); + 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}); + } + 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}); + } + // (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}}); + 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}); + 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}); + } + 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}); + } + 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}); + } + } - // inspect histogram sizes, please - histos.print(); + if (analyseLambda) { + histos.add("hMassLambda", "hMassLambda", kTH1D, {axisConfigurations.axisLambdaMass}); + histos.add("Lambda/hLambdasPerEvent", "hLambdasPerEvent", kTH1D, {{15, 0, 15}}); + } + if (analyseAntiLambda) { + histos.add("hMassAntiLambda", "hMassAntiLambda", kTH1D, {axisConfigurations.axisLambdaMass}); + histos.add("AntiLambda/hAntiLambdasPerEvent", "hAntiLambdasPerEvent", kTH1D, {{15, 0, 15}}); + }; + if (analyseLambda && analyseAntiLambda) { + histos.add("hAmbiguousLambdaCandidates", "hAmbiguousLambdaCandidates", kTH1D, {{1, 0, 1}}); + histos.add("hAmbiguousPerEvent", "hAmbiguousPerEvent", kTH1D, {{15, 0, 15}}); } - template - auto getCentrality(TCollision const& collision) - { - if (centralityEstimatorForQA == kCentFT0M) return collision.centFT0M(); - else if (centralityEstimatorForQA == kCentFT0C) return collision.centFT0C(); - else if (centralityEstimatorForQA == kCentFV0A) return collision.centFV0A(); - return -1.f; + // 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}); + // --- 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}); + // --- 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}); + } + 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}); + // --- 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}); + // --- 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}); + } } - template - void initCCDB(TBC const& bc) { - if (mRunNumber == bc.runNumber()) { - return; - } + 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}); + } + + // 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}); + 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}); + } + 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}); + } + } - mRunNumber = bc.runNumber(); + // 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/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 + if (doJetKinematicsQA) { + 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/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/hLeadingJetPt", "hLeadingJetPt", kTH1D, {axisConfigurations.axisJetPt}); + histos.add("JetKinematicsQA/hLeadingJetEta", "hLeadingJetEta", kTH1D, {axisConfigurations.axisEta}); + 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/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 + + // 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}); + + // 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}); + + //////////////////////////// + // 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}); + + // 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}); + + // Leading particle correlations: + 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}); + + // Main "Leading jet vs leading particle" correlations: + histos.add("JetVsLeadingParticleQA/h2dJetsPerEventvsLeadParticlePt", "h2dJetsPerEventvsLeadParticlePt", kTH2D, {axisConfigurations.JetsPerEvent, 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}); + } - // Fetching magnetic field as requested - // In case override, don't proceed, please - no CCDB access required - if (ccdbConfigurations.useCustomMagField) { - magField = ccdbConfigurations.customMagField; - } - else { - grpmag = ccdb->getForRun(ccdbConfigurations.grpmagPath, mRunNumber); - if (!grpmag) { - LOG(fatal) << "Got nullptr from CCDB for path " << ccdbConfigurations.grpmagPath << " of object GRPMagField and " << ccdbConfigurations.grpPath << " of object GRPObject for run " << mRunNumber; - } - // Fetch magnetic field from ccdb for current bc - magField = std::lround(5.f * grpmag->getL3Current() / 30000.f); - LOG(info) << "Retrieved GRP for run " << mRunNumber << " with magnetic field of " << magField << " kZG"; - } + // inspect histogram sizes, please + histos.print(); + } + + template + auto getCentrality(TCollision const& collision) + { + if (centralityEstimatorForQA == kCentFT0M) + return collision.centFT0M(); + else if (centralityEstimatorForQA == kCentFT0C) + return collision.centFT0C(); + else if (centralityEstimatorForQA == kCentFV0A) + return collision.centFV0A(); + return -1.f; + } + + template + void initCCDB(TBC const& bc) + { + if (mRunNumber == bc.runNumber()) { + return; } - // Minimal helper to fill the hSelectionV0s histogram without having to deal with bins by myself - // (CAUTION! If you change selection order, change this too!) - struct V0SelectionFlowCounter{ // Using struct to keep internal bin counter over different functions - 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 + mRunNumber = bc.runNumber(); + + // Fetching magnetic field as requested + // In case override, don't proceed, please - no CCDB access required + if (ccdbConfigurations.useCustomMagField) { + magField = ccdbConfigurations.customMagField; + } else { + grpmag = ccdb->getForRun(ccdbConfigurations.grpmagPath, mRunNumber); + if (!grpmag) { + LOG(fatal) << "Got nullptr from CCDB for path " << ccdbConfigurations.grpmagPath << " of object GRPMagField and " << ccdbConfigurations.grpPath << " of object GRPObject for run " << mRunNumber; + } + // Fetch magnetic field from ccdb for current bc + magField = std::lround(5.f * grpmag->getL3Current() / 30000.f); + LOG(info) << "Retrieved GRP for run " << mRunNumber << " with magnetic field of " << magField << " kZG"; + } + } + + // Minimal helper to fill the hSelectionV0s histogram without having to deal with bins by myself + // (CAUTION! If you change selection order, change this too!) + struct V0SelectionFlowCounter { // Using struct to keep internal bin counter over different functions + 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; } + void fill() { histos->fill(HIST("GeneralQA/hSelectionV0s"), ++binValue); } // Hardcoded hSelectionV0s histogram, as it will not change. Increments before filling, by default + }; + V0SelectionFlowCounter V0SelCounter{0, &histos}; + + // Minimal helper to fill hSelectionJetTracks, mirroring V0SelectionFlowCounter. + // Reset once per track candidate, fill once per passed cut stage. + struct JetTrackSelectionFlowCounter { + int binValue = -1; // Same convention as V0: starts at -1, first fill goes to bin x=0 + HistogramRegistry* histos = nullptr; + void resetForNewTrack() { binValue = -1; } + void fill() { histos->fill(HIST("GeneralQA/hSelectionJetTracks"), ++binValue); } + }; + JetTrackSelectionFlowCounter JetTrackSelCounter{0, &histos}; + + // Short inlined helper to simplify QA + inline void fillEventSelectionQA(int bin, float centrality) + { + histos.fill(HIST("hEventSelection"), bin); + histos.fill(HIST("hEventSelectionVsCentrality"), bin, centrality); + } + + // Fill reconstructed event centrality information + // Based off fillReconstructedEventProperties, but optimized to avoid re-accessing information already present on isEventAccepted! + template + void fillCentralityProperties(TCollision const& collision, float centrality) + { + // if (qaCentrality) { + // auto hRawCentrality = histos.get(HIST("Centrality/hRawCentrality")); + // centrality = hRawCentrality->GetBinContent(hRawCentrality->FindBin(doPPAnalysis ? collision.multFT0A() + collision.multFT0C() : collision.multFT0C())); + // } + histos.fill(HIST("Centrality/hEventCentrality"), centrality); + histos.fill(HIST("Centrality/hCentralityVsNch"), centrality, collision.multNTracksPVeta1()); + if (doEventQA) { + histos.fill(HIST("Centrality/hEventCentVsMultFT0M"), collision.centFT0M(), collision.multFT0A() + collision.multFT0C()); + histos.fill(HIST("Centrality/hEventCentVsMultFT0C"), collision.centFT0C(), collision.multFT0C()); + histos.fill(HIST("Centrality/hEventCentVsMultFV0A"), collision.centFV0A(), collision.multFV0A()); + histos.fill(HIST("Centrality/hEventMultFT0CvsMultFV0A"), collision.multFT0C(), collision.multFV0A()); + } + return; + } + + ///////////////////////////////////////////// + // Computation helper functions: + 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 + { + if (magField < 0) // for negative polarity field + phi = o2::constants::math::TwoPI - phi; + if (sign < 0) // for negative charge + phi = o2::constants::math::TwoPI - phi; + if (phi < 0) + LOGF(warning, "phi < 0: %g", phi); + + phi += o2::constants::math::PI / 18.0; // to center gap in the middle + return fmod(phi, o2::constants::math::PI / 9.0); + } + + bool isTrackFarFromTPCBoundary(double trackPt, double trackPhi, int sign) + // check whether the track passes close to a TPC sector boundary + { + double phiModn = computePhiMod(trackPhi, sign); + if (phiModn > fPhiCutHigh->Eval(trackPt)) + return true; // keep track + if (phiModn < fPhiCutLow->Eval(trackPt)) + return true; // keep track + + return false; // reject track + } + + inline float cosThetaJets(const fastjet::PseudoJet& a, const fastjet::PseudoJet& b) + { + const double dot = a.px() * b.px() + a.py() * b.py() + a.pz() * b.pz(); + const double magA = std::sqrt(a.px() * a.px() + a.py() * a.py() + a.pz() * a.pz()); + const double magB = std::sqrt(b.px() * b.px() + b.py() * b.py() + b.pz() * b.pz()); + return dot / (magA * magB); + } + + ///////////////////////////////////////////// + // Helper functions for event and candidate selection: + template + bool isEventAccepted(TCollision const& collision, TBC const& bc, float centrality, 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()) + return false; + if (fillHists) + fillEventSelectionQA(++selectionIdx, centrality); + if (eventSelections.requireTriggerTVX && !collision.selection_bit(aod::evsel::kIsTriggerTVX)) + return false; + if (fillHists) + fillEventSelectionQA(++selectionIdx, centrality); + if (eventSelections.rejectITSROFBorder && !collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) + return false; + if (fillHists) + fillEventSelectionQA(++selectionIdx, centrality); + if (eventSelections.rejectTFBorder && !collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) + return false; + if (fillHists) + fillEventSelectionQA(++selectionIdx, centrality); + + const float collisionPVz = collision.posZ(); + if (std::abs(collisionPVz) > eventSelections.maxZVtxPosition) + return false; + if (fillHists) + fillEventSelectionQA(++selectionIdx, centrality); + + if (eventSelections.requireIsVertexITSTPC && !collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) + return false; + if (fillHists) + fillEventSelectionQA(++selectionIdx, centrality); + if (eventSelections.requireIsGoodZvtxFT0VsPV && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) + return false; + if (fillHists) + fillEventSelectionQA(++selectionIdx, centrality); + if (eventSelections.requireIsVertexTOFmatched && !collision.selection_bit(o2::aod::evsel::kIsVertexTOFmatched)) + return false; + if (fillHists) + fillEventSelectionQA(++selectionIdx, centrality); + if (eventSelections.requireIsVertexTRDmatched && !collision.selection_bit(o2::aod::evsel::kIsVertexTRDmatched)) + return false; + if (fillHists) + fillEventSelectionQA(++selectionIdx, centrality); + if (eventSelections.rejectSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) + return false; + if (fillHists) + fillEventSelectionQA(++selectionIdx, centrality); + if (eventSelections.requireNoCollInTimeRangeStd && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) + return false; + if (fillHists) + fillEventSelectionQA(++selectionIdx, centrality); + if (eventSelections.requireNoCollInTimeRangeStrict && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStrict)) + return false; + if (fillHists) + fillEventSelectionQA(++selectionIdx, centrality); + if (eventSelections.requireNoCollInTimeRangeNarrow && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeNarrow)) + return false; + if (fillHists) + fillEventSelectionQA(++selectionIdx, centrality); + if (eventSelections.requireNoCollInROFStd && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) + return false; + if (fillHists) + fillEventSelectionQA(++selectionIdx, centrality); + if (eventSelections.requireNoCollInROFStrict && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStrict)) + return false; + if (fillHists) + fillEventSelectionQA(++selectionIdx, centrality); + + if (doPPAnalysis) { // we are in pp + if constexpr (requires { collision.multNTracksPVeta1(); }) { + // Only considers compiling this block when the collision type actually + // has multNTracksPVeta1(). This is done to reduce collision-table + // subscriptions in the jet processing function. + // This is a compile-time check: since the function is templated, it + // is instantiated separately for Jets and V0s, and this block will be + // properly compiled for each use case and table subscription automatically! + if (eventSelections.requireINEL0 && collision.multNTracksPVeta1() < 1) + return false; + if (fillHists) + fillEventSelectionQA(++selectionIdx, centrality); + if (eventSelections.requireINEL1 && collision.multNTracksPVeta1() < 2) + return false; + if (fillHists) + fillEventSelectionQA(++selectionIdx, centrality); + } + } else { // Performing selections as if in Pb-Pb: + const float collisionOccupancy = eventSelections.useFT0CbasedOccupancy ? collision.ft0cOccupancyInTimeRange() : collision.trackOccupancyInTimeRange(); + if (eventSelections.minOccupancy >= 0 && collisionOccupancy < eventSelections.minOccupancy) + return false; + if (fillHists) + fillEventSelectionQA(++selectionIdx, centrality); + if (eventSelections.maxOccupancy >= 0 && collisionOccupancy > eventSelections.maxOccupancy) + return false; + 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; + if (eventSelections.minIR >= 0 && interactionRate < eventSelections.minIR) + return false; + if (fillHists) + fillEventSelectionQA(++selectionIdx, centrality); + if (eventSelections.maxIR >= 0 && interactionRate > eventSelections.maxIR) + return false; + if (fillHists) + fillEventSelectionQA(++selectionIdx, centrality); + if (!rctConfigurations.cfgRCTLabel.value.empty() && !rctFlagsChecker(collision)) + return false; + if (fillHists) + fillEventSelectionQA(++selectionIdx, centrality); + + // Filling histograms previously filled in fillReconstructedEventProperties here, to avoid re-accessing data: + if (fillHists) { + histos.fill(HIST("hEventOccupancy"), collisionOccupancy); + histos.fill(HIST("hCentralityVsOccupancy"), centrality, collisionOccupancy); + histos.fill(HIST("hInteractionRate"), interactionRate); + histos.fill(HIST("hCentralityVsInteractionRate"), centrality, interactionRate); + histos.fill(HIST("hInteractionRateVsOccupancy"), interactionRate, collisionOccupancy); + } + } - void resetForNewV0(){binValue = -1;} - void fill(){histos->fill(HIST("GeneralQA/hSelectionV0s"), ++binValue);} // Hardcoded hSelectionV0s histogram, as it will not change. Increments before filling, by default - }; - V0SelectionFlowCounter V0SelCounter{0, &histos}; - - // Minimal helper to fill hSelectionJetTracks, mirroring V0SelectionFlowCounter. - // Reset once per track candidate, fill once per passed cut stage. - struct JetTrackSelectionFlowCounter { - int binValue = -1; // Same convention as V0: starts at -1, first fill goes to bin x=0 - HistogramRegistry* histos = nullptr; - void resetForNewTrack() { binValue = -1; } - void fill() { histos->fill(HIST("GeneralQA/hSelectionJetTracks"), ++binValue); } - }; - JetTrackSelectionFlowCounter JetTrackSelCounter{0, &histos}; + if (fillHists) { + histos.fill(HIST("hCentralityVsPVz"), centrality, collisionPVz); + histos.fill(HIST("hEventPVz"), collisionPVz); + } + return true; + } + + template + bool isCandidateForChargedPseudojetAccepted(JetCandidate const& track) + { // (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.minITSnCls >= 0) { + if (track.itsNCls() < pseudoJetCandidateTrackSelections.minITSnCls) + return false; + } + JetTrackSelCounter.fill(); // bin: ITS clusters (min) + + if (track.tpcNClsCrossedRows() < pseudoJetCandidateTrackSelections.minNCrossedRowsTPC) + return false; + JetTrackSelCounter.fill(); + + if (track.tpcChi2NCl() > pseudoJetCandidateTrackSelections.maxChi2TPC) + return false; + JetTrackSelCounter.fill(); + if (track.itsChi2NCl() > pseudoJetCandidateTrackSelections.maxChi2ITS) + return false; + JetTrackSelCounter.fill(); + + // Kinematics: + const float pt = track.pt(); + if (pt < pseudoJetCandidateTrackSelections.minCandidatePt) + return false; + JetTrackSelCounter.fill(); + if (std::fabs(track.eta()) > pseudoJetCandidateTrackSelections.etaCut) + return false; + 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(); + // 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(); + } + return true; + } + + // Lambda selections: + template + bool passesGenericV0Cuts(TV0 const& v0) + { + // Base topological variables (high rejection, low cost checks) + if (v0.v0radius() < v0Selections.v0radius) + return false; + V0SelCounter.fill(); + if (v0.v0radius() > v0Selections.v0radiusMax) + return false; + V0SelCounter.fill(); + if (v0.v0cosPA() < v0Selections.v0cospa) + return false; + V0SelCounter.fill(); + if (v0.dcaV0daughters() > v0Selections.dcav0dau) + return false; + V0SelCounter.fill(); + + // pseudorapidity cuts: + 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) + 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(); + + // ITS quality cuts + bool posIsFromAfterburner = posTrackExtra.isITSAfterburner(); + bool negIsFromAfterburner = negTrackExtra.isITSAfterburner(); + + // 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(); + if (posTrackExtra.itsChi2NCl() >= v0Selections.maxITSchi2PerNcls) + return false; // check maximum ITS chi2 per clusters + V0SelCounter.fill(); + if (v0Selections.rejectPosITSafterburner && posIsFromAfterburner) + return false; // reject afterburner track or not + V0SelCounter.fill(); + if (v0Selections.requirePosITSafterburnerOnly && !posIsFromAfterburner) + return false; // keep afterburner track or not + V0SelCounter.fill(); + + if (negTrackExtra.itsNCls() < v0Selections.minITSclusters) + return false; // check minimum ITS clusters + V0SelCounter.fill(); + if (negTrackExtra.itsChi2NCl() >= v0Selections.maxITSchi2PerNcls) + return false; // check maximum ITS chi2 per clusters + V0SelCounter.fill(); + if (v0Selections.rejectNegITSafterburner && negIsFromAfterburner) + return false; // reject afterburner track or not + V0SelCounter.fill(); + if (v0Selections.requireNegITSafterburnerOnly && !negIsFromAfterburner) + return false; // keep afterburner track or not + V0SelCounter.fill(); + + // TPC quality cuts + if (posTrackExtra.tpcNClsCrossedRows() < v0Selections.minTPCrows) + return false; // check minimum TPC crossed rows + V0SelCounter.fill(); + if (posTrackExtra.tpcChi2NCl() >= v0Selections.maxTPCchi2PerNcls) + return false; // check maximum TPC chi2 per clusters + V0SelCounter.fill(); + if (posTrackExtra.tpcCrossedRowsOverFindableCls() < v0Selections.minTPCrowsOverFindableClusters) + return false; // check minimum fraction of TPC rows over findable + V0SelCounter.fill(); + if (posTrackExtra.tpcFoundOverFindableCls() < v0Selections.minTPCfoundOverFindableClusters) + return false; // check minimum fraction of found over findable TPC clusters + V0SelCounter.fill(); + if (posTrackExtra.tpcFractionSharedCls() >= v0Selections.maxFractionTPCSharedClusters) + return false; // check the maximum fraction of allowed shared TPC clusters + V0SelCounter.fill(); + if (v0Selections.rejectTPCsectorBoundary && !isTrackFarFromTPCBoundary(v0.positivept(), v0.positivephi(), +1)) + return false; // reject track far from TPC sector boundary or not + V0SelCounter.fill(); + + if (negTrackExtra.tpcNClsCrossedRows() < v0Selections.minTPCrows) + return false; // check minimum TPC crossed rows + V0SelCounter.fill(); + if (negTrackExtra.tpcChi2NCl() >= v0Selections.maxTPCchi2PerNcls) + return false; // check maximum TPC chi2 per clusters + V0SelCounter.fill(); + if (negTrackExtra.tpcCrossedRowsOverFindableCls() < v0Selections.minTPCrowsOverFindableClusters) + return false; // check minimum fraction of TPC rows over findable + V0SelCounter.fill(); + if (negTrackExtra.tpcFoundOverFindableCls() < v0Selections.minTPCfoundOverFindableClusters) + return false; // check minimum fraction of found over findable TPC clusters + V0SelCounter.fill(); + if (negTrackExtra.tpcFractionSharedCls() >= v0Selections.maxFractionTPCSharedClusters) + return false; // check the maximum fraction of allowed shared TPC clusters + V0SelCounter.fill(); + if (v0Selections.rejectTPCsectorBoundary && !isTrackFarFromTPCBoundary(v0.negativept(), v0.negativephi(), -1)) + return false; // reject track far from TPC sector boundary or not + V0SelCounter.fill(); + + // ITS only tag + if (v0Selections.requirePosITSonly && posTrackExtra.tpcNClsCrossedRows() > 1) + return false; + V0SelCounter.fill(); + if (v0Selections.requireNegITSonly && negTrackExtra.tpcNClsCrossedRows() > 1) + return false; + V0SelCounter.fill(); + + // TPC only tag + if (v0Selections.skipTPConly && posTrackExtra.detectorMap() == o2::aod::track::TPC) + return false; + V0SelCounter.fill(); + if (v0Selections.skipTPConly && negTrackExtra.detectorMap() == o2::aod::track::TPC) + return false; + V0SelCounter.fill(); + + return true; + } + + // Tests the hypothesis of the V0 being a Lambda or of it being an antiLambda. + template + bool passesLambdaLambdaBarHypothesis(TV0 const& v0, TCollision const& collision, bool Lambda_hypothesis) + { + // 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()); + 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. + if (dcaPionToPV < v0Selections.dcaPionToPV) + return false; + V0SelCounter.fill(); + + const auto posTrackExtra = v0.template posTrack_as(); + const auto negTrackExtra = v0.template negTrack_as(); + + // 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; + + ///// Expensive PID checks come last: + // TPC PID + if (std::fabs(protonTrack.tpcNSigmaPr()) > v0Selections.tpcPidNsigmaCut) + return false; + V0SelCounter.fill(); + if (std::fabs(pionTrack.tpcNSigmaPi()) > v0Selections.tpcPidNsigmaCut) + return false; + V0SelCounter.fill(); + + // TOF PID in DeltaT (if TOF is not available, then uses the track. If is available, uses it. In this sense, TOF is optional) + // const bool posHasTOF = posTrackExtra.hasTOF(); // For the older version, which worked only for Lambdas + const bool protonHasTOF = protonTrack.hasTOF(); // Should work even without PIDResponseTOF.h, as it is a TracksExtra property + const bool pionHasTOF = pionTrack.hasTOF(); + + // Proton-like track + if (protonHasTOF && std::abs(Lambda_hypothesis ? v0.posTOFDeltaTLaPr() : v0.negTOFDeltaTLaPr()) > v0Selections.maxDeltaTimeProton) + return false; + V0SelCounter.fill(); + // Pion-like track + if (pionHasTOF && std::abs(Lambda_hypothesis ? v0.negTOFDeltaTLaPi() : v0.posTOFDeltaTLaPi()) > v0Selections.maxDeltaTimePion) + return false; + V0SelCounter.fill(); + + // TOF PID in NSigma (TODO: add asymmetric NSigma windows for purity tuning?) + // Proton-like track + if (protonHasTOF && std::fabs(v0.tofNSigmaLaPr()) > v0Selections.tofPidNsigmaCutLaPr) + return false; // (No need to select which candidate is which with the Lambda_hypothesis. Automatically done already!) + V0SelCounter.fill(); + // Pion-like track + if (pionHasTOF && std::fabs(v0.tofNSigmaLaPi()) > v0Selections.tofPidNsigmaCutLaPi) + return false; + 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, instead of using the correct path length from the + // V0s PV-DCA and the such! In other words, it is a naive estimator of TOF PID, because it does not correct for the V0 + // mother's travel time and considers all tracks as if they came from the PV! + // 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. + + // proper lifetime + if (v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * o2::constants::physics::MassLambda0 > v0Selections.lambdaLifetimeCut) + return false; + 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 + // (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 + // if (std::abs(alpha) < v0Selections.armK0AlphaThreshold && qt < v0Selections.armK0QtThreshold) return kIsArmenterosK0; + // // std::abs(alpha) < 0.2 && qt < 0.1 + // if (std::abs(alpha) < v0Selections.armMinAlpha) return kArmenterosAmbiguous; + // // std::abs(alpha) < 0.01f + // // Lambda selection + // if (alpha > 0) return kIsArmenterosLambda; + // else return kIsArmenterosAntiLambda; + // } + + // TODO: another possible check that could be done (if not implemented already inside mLambda() getters) + // template + // int isCandidateMassLambda(TV0 const& v0) { + // float m1 = v0.mLambda(); // proton=positive + // float m2 = v0.mAntiLambda(); // proton=negative + // float d = std::abs(m1 - mLambdaTrue) - std::abs(m2 - mLambdaTrue); + // if (d < 0.f) return +1; // Lambda + // else return -1; // AntiLambda + // } + + void processJetsData(SelCollisionsSimple::iterator const& collision, PseudoJetTracks const& tracks, aod::BCsWithTimestamps const& bcs) + { // Uses BCsWithTimestamps to get timestamps for rejectTPCsectorBoundary + float centrality = -1.0f; // Just a placeholder + + // 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 validJetAlreadyFound = false; // Do not fill Event QA more than once + + auto bc = bcs.iteratorAt(collision.bcId()); // Got the iteratorAt() idea from O2Physics/PWGUD/Core/UDHelpers.h + if (!isEventAccepted(collision, bc, centrality, false)) + return; // Uses return instead of continue, as there is no explicit loop here + const uint64_t collIdx = collision.globalIndex(); + + // Loop over reconstructed tracks: + std::vector fjParticles; + 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" + + // Require that tracks pass selection criteria + if (!isCandidateForChargedPseudojetAccepted(track)) + continue; + + // Constructing pseudojet candidates vector: + // Using pion mass as hypothesis for track energy estimate (before PID, all particles treated as if with the same invariant mass) + // (TODO: study the possibility of using identified PseudoJet candidates for this estimate) + fastjet::PseudoJet candidate(track.px(), track.py(), track.pz(), track.energy(o2::constants::physics::MassPionCharged)); + fjParticles.emplace_back(candidate); + + // Calculating leading particle + float pt = candidate.pt(); + if (pt > leadingParticlePt) { + leadingParticlePt = pt; + leadingParticleIdx = fjParticles.size() - 1; + } + } + // Reject empty events + if (fjParticles.size() < 1) + return; - // Short inlined helper to simplify QA - inline void fillEventSelectionQA(int bin, float centrality){ - histos.fill(HIST("hEventSelection"), bin); - histos.fill(HIST("hEventSelectionVsCentrality"), bin, centrality); + auto const& leadingParticle = fjParticles[leadingParticleIdx]; + if (leadingParticle.pt() > jetConfigurations.minLeadParticlePt) { // If not, leading particle is probably a bad proxy + tableLeadParticles(collIdx, leadingParticle.pt(), leadingParticle.eta(), leadingParticle.phi()); } - // Fill reconstructed event centrality information - // Based off fillReconstructedEventProperties, but optimized to avoid re-accessing information already present on isEventAccepted! - template - void fillCentralityProperties(TCollision const& collision, float centrality) - { - // if (qaCentrality) { - // auto hRawCentrality = histos.get(HIST("Centrality/hRawCentrality")); - // centrality = hRawCentrality->GetBinContent(hRawCentrality->FindBin(doPPAnalysis ? collision.multFT0A() + collision.multFT0C() : collision.multFT0C())); - // } - histos.fill(HIST("Centrality/hEventCentrality"), centrality); - histos.fill(HIST("Centrality/hCentralityVsNch"), centrality, collision.multNTracksPVeta1()); - if (doEventQA) { - histos.fill(HIST("Centrality/hEventCentVsMultFT0M"), collision.centFT0M(), collision.multFT0A() + collision.multFT0C()); - histos.fill(HIST("Centrality/hEventCentVsMultFT0C"), collision.centFT0C(), collision.multFT0C()); - histos.fill(HIST("Centrality/hEventCentVsMultFV0A"), collision.centFV0A(), collision.multFV0A()); - histos.fill(HIST("Centrality/hEventMultFT0CvsMultFV0A"), collision.multFT0C(), collision.multFV0A()); + // Start jet clusterization: + // Cluster particles using the anti-kt algorithm + fastjet::JetDefinition jetDef(mapFJAlgorithm(jetConfigurations.jetAlgorithm), jetConfigurations.radiusJet, mapFJRecombScheme(jetConfigurations.jetRecombScheme)); + // std::vector jets_pt, jets_eta, jets_phi; // Not worth it to store 4-vectors: the tracks assume pion mass hypothesis, so energy and rapidity are not right. + if (jetConfigurations.bkgSubtraction == kAreaBased) { + 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()) + return; + // 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; + // bool hasLeadingJet = false; // Not needed: if the event has any jet, that is the leading jet. Check is superseded by the selectedJets information + float leadingJetPt = -1.f; + 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)) + continue; + + auto jetForSub = jet; + // Subtracts same background estimated for highest pt jet, but every jet might have a slightly different area + // (TODO: check possible problems with OO and physics impacts of this particular cone method and choice of single background estimator based on leading jet) + // (TODO: improve for Pb-Pb, specially central!) + fastjet::PseudoJet jetMinusBkg = backgroundSub.doRhoAreaSub(jetForSub, rhoPerp, rhoMPerp); + // Jet pt must be larger than threshold: + if (jetMinusBkg.pt() < jetConfigurations.minJetPt) + continue; + selectedJets++; + + // Store jet: + tableJets(collIdx, + jetMinusBkg.pt(), + jetMinusBkg.eta(), // Using eta instead of rapidity + jetMinusBkg.phi(), + jetMinusBkg.constituents().size()); + + // Finding the leading jet after subtraction (leading jet is NOT known a priori!): + if (jetMinusBkg.pt() > leadingJetPt) { + leadingJetPt = jetMinusBkg.pt(); + leadingJetSub = jetMinusBkg; } + } + histos.fill(HIST("hJetsPerEvent"), selectedJets); + if (selectedJets == 0) return; - } + histos.fill(HIST("hEventsWithJet"), 0.5); + // Another version of this counter, which is already integrated in the Event Selection flow: + if (doEventQA && !validJetAlreadyFound) + fillEventSelectionQA(lastBinEvSel - 1, centrality); // hasRingJet passes + validJetAlreadyFound = true; + + if (doJetKinematicsQA) { + histos.fill(HIST("JetKinematicsQA/hLeadingJetPt"), leadingJetSub.pt()); + 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; + + float cosTheta = cosThetaJets(leadingJetSub, jetMinusBkg); + float deltaPhi = RecoDecay::constrainAngle(leadingJetSub.phi() - jetMinusBkg.phi(), -o2::constants::math::PI); + float deltaEta = leadingJetSub.eta() - jetMinusBkg.eta(); + float deltaR = std::sqrt(deltaPhi * deltaPhi + deltaEta * deltaEta); + + histos.fill(HIST("JetKinematicsQA/hCosThetaToLeadingJet"), cosTheta); + histos.fill(HIST("JetKinematicsQA/hDeltaPhiToLeadingJet"), deltaPhi); + histos.fill(HIST("JetKinematicsQA/hDeltaEtaToLeadingJet"), deltaEta); + histos.fill(HIST("JetKinematicsQA/hDeltaRToLeadingJet"), deltaR); + + // 2D correlations: + histos.fill(HIST("JetKinematicsQA/h2dJetsPerEventvsLeadJetPt"), selectedJets, leadingJetSub.pt()); + histos.fill(HIST("JetKinematicsQA/h2dJetsPerEventvsJetPt"), selectedJets, jetMinusBkg.pt()); + histos.fill(HIST("JetKinematicsQA/h2dCosThetaToLeadvsDeltaPhiToLead"), cosTheta, deltaPhi); + histos.fill(HIST("JetKinematicsQA/h2dCosThetaToLeadvsDeltaEtaToLead"), cosTheta, deltaEta); + histos.fill(HIST("JetKinematicsQA/h2dCosThetaToLeadvsDeltaRToLead"), cosTheta, deltaR); + histos.fill(HIST("JetKinematicsQA/h2dDeltaPhiToLeadvsDeltaEtaToLead"), deltaPhi, deltaEta); + + histos.fill(HIST("JetKinematicsQA/h2dJetPtvsDeltaPhiToLead"), jetMinusBkg.pt(), deltaPhi); // Can't really get the energy of the jet, just the pt to make this comparison + histos.fill(HIST("JetKinematicsQA/h2dJetEnergyvsDeltaPhiToLead"), jetMinusBkg.E(), deltaPhi); // Just a different scale + histos.fill(HIST("JetKinematicsQA/h2dJetEnergyvsCosThetaToLead"), jetMinusBkg.E(), cosTheta); + + histos.fill(HIST("JetKinematicsQA/h2dJetsPerEventvsDeltaPhiToLead"), selectedJets, deltaPhi); + histos.fill(HIST("JetKinematicsQA/h2dJetsPerEventvsDeltaEtaToLead"), selectedJets, deltaEta); + histos.fill(HIST("JetKinematicsQA/h2dJetsPerEventvsCosThetaToLead"), selectedJets, cosTheta); + } + // Leading particle comparisons: + histos.fill(HIST("JetVsLeadingParticleQA/hLeadingParticlePt"), leadingParticle.pt()); + histos.fill(HIST("JetVsLeadingParticleQA/hLeadingParticleEta"), leadingParticle.eta()); + histos.fill(HIST("JetVsLeadingParticleQA/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); - ///////////////////////////////////////////// - // Computation helper functions: - 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 - { - if (magField < 0) // for negative polarity field - phi = o2::constants::math::TwoPI - phi; - if (sign < 0) // for negative charge - phi = o2::constants::math::TwoPI - phi; - if (phi < 0) - LOGF(warning, "phi < 0: %g", phi); + histos.fill(HIST("JetVsLeadingParticleQA/h2dDeltaPhiParticleToLeadvsDeltaEtaParticleToLead"), deltaPhiParticleToJet, deltaEtaParticleToJet); - phi += o2::constants::math::PI / 18.0; // to center gap in the middle - return fmod(phi, o2::constants::math::PI / 9.0); - } + histos.fill(HIST("JetVsLeadingParticleQA/h2dJetsPerEventvsDeltaPhiParticleToLead"), selectedJets, deltaPhiParticleToJet); + histos.fill(HIST("JetVsLeadingParticleQA/h2dJetsPerEventvsDeltaEtaParticleToLead"), selectedJets, deltaEtaParticleToJet); + histos.fill(HIST("JetVsLeadingParticleQA/h2dJetsPerEventvsCosThetaParticleToLead"), selectedJets, cosThetaParticleToJet); - bool isTrackFarFromTPCBoundary(double trackPt, double trackPhi, int sign) - // check whether the track passes close to a TPC sector boundary - { - double phiModn = computePhiMod(trackPhi, sign); - if (phiModn > fPhiCutHigh->Eval(trackPt)) return true; // keep track - if (phiModn < fPhiCutLow->Eval(trackPt)) return true; // keep track + histos.fill(HIST("JetVsLeadingParticleQA/h2dJetsPerEventvsLeadParticlePt"), selectedJets, leadingParticle.pt()); + histos.fill(HIST("JetVsLeadingParticleQA/h2dLeadJetPtvsLeadParticlePt"), leadingJetSub.pt(), leadingParticle.pt()); - return false; // reject track - } + histos.fill(HIST("JetVsLeadingParticleQA/h2dLeadJetPtvsCosThetaParticleToLead"), leadingJetSub.pt(), cosThetaParticleToJet); + histos.fill(HIST("JetVsLeadingParticleQA/h2dLeadParticlePtvsCosThetaParticleToLead"), leadingParticle.pt(), cosThetaParticleToJet); - inline float cosThetaJets(const fastjet::PseudoJet& a, const fastjet::PseudoJet& b){ - const double dot = a.px() * b.px() + a.py() * b.py() + a.pz() * b.pz(); - const double magA = std::sqrt(a.px()*a.px() + a.py()*a.py() + a.pz()*a.pz()); - const double magB = std::sqrt(b.px()*b.px() + b.py()*b.py() + b.pz()*b.pz()); - return dot / (magA * magB); - } + 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); + } + } else { // Otherwise, simple jet clustering (TODO: this is the fall back for kConstituentBased while not implemented) + fastjet::ClusterSequence clustSeq(fjParticles, jetDef); + // Jet pt must be larger than threshold: + std::vector jets = fastjet::sorted_by_pt(clustSeq.inclusive_jets(jetConfigurations.minJetPt)); - ///////////////////////////////////////////// - // Helper functions for event and candidate selection: - template - bool isEventAccepted(TCollision const& collision, TBC const& bc, float centrality, 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()) return false; - if (fillHists) fillEventSelectionQA(++selectionIdx, centrality); - if (eventSelections.requireTriggerTVX && !collision.selection_bit(aod::evsel::kIsTriggerTVX)) return false; - if (fillHists) fillEventSelectionQA(++selectionIdx, centrality); - if (eventSelections.rejectITSROFBorder && !collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) return false; - if (fillHists) fillEventSelectionQA(++selectionIdx, centrality); - if (eventSelections.rejectTFBorder && !collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) return false; - if (fillHists) fillEventSelectionQA(++selectionIdx, centrality); - - const float collisionPVz = collision.posZ(); - if (std::abs(collisionPVz) > eventSelections.maxZVtxPosition) return false; - if (fillHists) fillEventSelectionQA(++selectionIdx, centrality); - - if (eventSelections.requireIsVertexITSTPC && !collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) return false; - if (fillHists) fillEventSelectionQA(++selectionIdx, centrality); - if (eventSelections.requireIsGoodZvtxFT0VsPV && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) return false; - if (fillHists) fillEventSelectionQA(++selectionIdx, centrality); - if (eventSelections.requireIsVertexTOFmatched && !collision.selection_bit(o2::aod::evsel::kIsVertexTOFmatched)) return false; - if (fillHists) fillEventSelectionQA(++selectionIdx, centrality); - if (eventSelections.requireIsVertexTRDmatched && !collision.selection_bit(o2::aod::evsel::kIsVertexTRDmatched)) return false; - if (fillHists) fillEventSelectionQA(++selectionIdx, centrality); - if (eventSelections.rejectSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) return false; - if (fillHists) fillEventSelectionQA(++selectionIdx, centrality); - if (eventSelections.requireNoCollInTimeRangeStd && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) return false; - if (fillHists) fillEventSelectionQA(++selectionIdx, centrality); - if (eventSelections.requireNoCollInTimeRangeStrict && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStrict)) return false; - if (fillHists) fillEventSelectionQA(++selectionIdx, centrality); - if (eventSelections.requireNoCollInTimeRangeNarrow && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeNarrow)) return false; - if (fillHists) fillEventSelectionQA(++selectionIdx, centrality); - if (eventSelections.requireNoCollInROFStd && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) return false; - if (fillHists) fillEventSelectionQA(++selectionIdx, centrality); - if (eventSelections.requireNoCollInROFStrict && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStrict)) return false; - if (fillHists) fillEventSelectionQA(++selectionIdx, centrality); - - if (doPPAnalysis) { // we are in pp - if constexpr (requires { collision.multNTracksPVeta1(); }){ - // Only considers compiling this block when the collision type actually - // has multNTracksPVeta1(). This is done to reduce collision-table - // subscriptions in the jet processing function. - // This is a compile-time check: since the function is templated, it - // is instantiated separately for Jets and V0s, and this block will be - // properly compiled for each use case and table subscription automatically! - if (eventSelections.requireINEL0 && collision.multNTracksPVeta1() < 1) return false; - if (fillHists) fillEventSelectionQA(++selectionIdx, centrality); - if (eventSelections.requireINEL1 && collision.multNTracksPVeta1() < 2) return false; - if (fillHists) fillEventSelectionQA(++selectionIdx, centrality); - } - } - else { // Performing selections as if in Pb-Pb: - const float collisionOccupancy = eventSelections.useFT0CbasedOccupancy ? collision.ft0cOccupancyInTimeRange() : collision.trackOccupancyInTimeRange(); - if (eventSelections.minOccupancy >= 0 && collisionOccupancy < eventSelections.minOccupancy) return false; - if (fillHists) fillEventSelectionQA(++selectionIdx, centrality); - if (eventSelections.maxOccupancy >= 0 && collisionOccupancy > eventSelections.maxOccupancy) return false; - 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; - if (eventSelections.minIR >= 0 && interactionRate < eventSelections.minIR) return false; - if (fillHists) fillEventSelectionQA(++selectionIdx, centrality); - if (eventSelections.maxIR >= 0 && interactionRate > eventSelections.maxIR) return false; - if (fillHists) fillEventSelectionQA(++selectionIdx, centrality); - if (!rctConfigurations.cfgRCTLabel.value.empty() && !rctFlagsChecker(collision)) return false; - if (fillHists) fillEventSelectionQA(++selectionIdx, centrality); - - // Filling histograms previously filled in fillReconstructedEventProperties here, to avoid re-accessing data: - if (fillHists){ - histos.fill(HIST("hEventOccupancy"), collisionOccupancy); - histos.fill(HIST("hCentralityVsOccupancy"), centrality, collisionOccupancy); - histos.fill(HIST("hInteractionRate"), interactionRate); - histos.fill(HIST("hCentralityVsInteractionRate"), centrality, interactionRate); - histos.fill(HIST("hInteractionRateVsOccupancy"), interactionRate, collisionOccupancy); - } - } + const int jetsInEvent = jets.size(); + histos.fill(HIST("hJetsPerEvent"), jetsInEvent); // Fills even in empty events, as this is a useful number to know! - if (fillHists){ - histos.fill(HIST("hCentralityVsPVz"), centrality, collisionPVz); - histos.fill(HIST("hEventPVz"), collisionPVz); + if (jetsInEvent == 0) + return; + histos.fill(HIST("hEventsWithJet"), 0.5); + // Another version of this counter, which is already integrated in the Event Selection flow: + if (doEventQA && !validJetAlreadyFound) + fillEventSelectionQA(lastBinEvSel - 1, centrality); // hasRingJet passes + validJetAlreadyFound = true; + + 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)) + continue; + + tableJets(collIdx, + jet.pt(), + jet_eta, // Using eta instead of rapidity + jet.phi(), + jet.constituents().size()); + + if (doJetKinematicsQA) { + histos.fill(HIST("JetKinematicsQA/hJetPt"), jet.pt()); + histos.fill(HIST("JetKinematicsQA/hJetEta"), jet_eta); + histos.fill(HIST("JetKinematicsQA/hJetPhi"), 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 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! + histos.fill(HIST("JetKinematicsQA/hDeltaPhiToLeadingJet"), deltaPhi); + histos.fill(HIST("JetKinematicsQA/hDeltaEtaToLeadingJet"), deltaEta); + histos.fill(HIST("JetKinematicsQA/hDeltaRToLeadingJet"), deltaR); + + // 2D correlations: + histos.fill(HIST("JetKinematicsQA/h2dJetsPerEventvsLeadJetPt"), jetsInEvent, leadingJet.pt()); + histos.fill(HIST("JetKinematicsQA/h2dJetsPerEventvsJetPt"), jetsInEvent, jet.pt()); + histos.fill(HIST("JetKinematicsQA/h2dCosThetaToLeadvsDeltaPhiToLead"), cosTheta, deltaPhi); + histos.fill(HIST("JetKinematicsQA/h2dCosThetaToLeadvsDeltaEtaToLead"), cosTheta, deltaEta); + histos.fill(HIST("JetKinematicsQA/h2dCosThetaToLeadvsDeltaRToLead"), cosTheta, deltaR); + histos.fill(HIST("JetKinematicsQA/h2dDeltaPhiToLeadvsDeltaEtaToLead"), deltaPhi, deltaEta); + + histos.fill(HIST("JetKinematicsQA/h2dJetPtvsDeltaPhiToLead"), jet.pt(), deltaPhi); // Can't really get the energy of the jet, just the pt to make this comparison + histos.fill(HIST("JetKinematicsQA/h2dJetEnergyvsDeltaPhiToLead"), jet.E(), deltaPhi); // Just a different scale + histos.fill(HIST("JetKinematicsQA/h2dJetEnergyvsCosThetaToLead"), jet.E(), cosTheta); + + histos.fill(HIST("JetKinematicsQA/h2dJetsPerEventvsDeltaPhiToLead"), jetsInEvent, deltaPhi); + histos.fill(HIST("JetKinematicsQA/h2dJetsPerEventvsDeltaEtaToLead"), jetsInEvent, deltaEta); + histos.fill(HIST("JetKinematicsQA/h2dJetsPerEventvsCosThetaToLead"), jetsInEvent, cosTheta); } - return true; - } + } + if (doJetKinematicsQA) { + histos.fill(HIST("JetKinematicsQA/hLeadingJetPt"), leadingJet.pt()); + histos.fill(HIST("JetKinematicsQA/hLeadingJetEta"), leadingJet.eta()); + histos.fill(HIST("JetKinematicsQA/hLeadingJetPhi"), leadingJet.phi()); - template - bool isCandidateForChargedPseudojetAccepted(JetCandidate const& track){ // (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.minITSnCls >= 0){ - if (track.itsNCls() < pseudoJetCandidateTrackSelections.minITSnCls) return false; - } - JetTrackSelCounter.fill(); // bin: ITS clusters (min) - - if (track.tpcNClsCrossedRows() < pseudoJetCandidateTrackSelections.minNCrossedRowsTPC) return false; - JetTrackSelCounter.fill(); - - if (track.tpcChi2NCl() > pseudoJetCandidateTrackSelections.maxChi2TPC) return false; - JetTrackSelCounter.fill(); - if (track.itsChi2NCl() > pseudoJetCandidateTrackSelections.maxChi2ITS) return false; - JetTrackSelCounter.fill(); - - // Kinematics: - const float pt = track.pt(); - if (pt < pseudoJetCandidateTrackSelections.minCandidatePt) return false; - JetTrackSelCounter.fill(); - if (std::fabs(track.eta()) > pseudoJetCandidateTrackSelections.etaCut) return false; - 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(); - // 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(); - } - return true; - } + // Leading particle comparisons: + histos.fill(HIST("JetVsLeadingParticleQA/hLeadingParticlePt"), leadingParticle.pt()); + histos.fill(HIST("JetVsLeadingParticleQA/hLeadingParticleEta"), leadingParticle.eta()); + histos.fill(HIST("JetVsLeadingParticleQA/hLeadingParticlePhi"), leadingParticle.phi()); - // Lambda selections: - template - bool passesGenericV0Cuts(TV0 const& v0){ - // Base topological variables (high rejection, low cost checks) - if (v0.v0radius() < v0Selections.v0radius) return false; - V0SelCounter.fill(); - if (v0.v0radius() > v0Selections.v0radiusMax) return false; - V0SelCounter.fill(); - if (v0.v0cosPA() < v0Selections.v0cospa) return false; - V0SelCounter.fill(); - if (v0.dcaV0daughters() > v0Selections.dcav0dau) return false; - V0SelCounter.fill(); - - // pseudorapidity cuts: - 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) 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(); - - // ITS quality cuts - bool posIsFromAfterburner = posTrackExtra.isITSAfterburner(); - bool negIsFromAfterburner = negTrackExtra.isITSAfterburner(); - - // 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(); - if (posTrackExtra.itsChi2NCl() >= v0Selections.maxITSchi2PerNcls) return false; // check maximum ITS chi2 per clusters - V0SelCounter.fill(); - if (v0Selections.rejectPosITSafterburner && posIsFromAfterburner) return false; // reject afterburner track or not - V0SelCounter.fill(); - if (v0Selections.requirePosITSafterburnerOnly && !posIsFromAfterburner) return false; // keep afterburner track or not - V0SelCounter.fill(); - - if (negTrackExtra.itsNCls() < v0Selections.minITSclusters) return false; // check minimum ITS clusters - V0SelCounter.fill(); - if (negTrackExtra.itsChi2NCl() >= v0Selections.maxITSchi2PerNcls) return false; // check maximum ITS chi2 per clusters - V0SelCounter.fill(); - if (v0Selections.rejectNegITSafterburner && negIsFromAfterburner) return false; // reject afterburner track or not - V0SelCounter.fill(); - if (v0Selections.requireNegITSafterburnerOnly && !negIsFromAfterburner) return false; // keep afterburner track or not - V0SelCounter.fill(); - - // TPC quality cuts - if (posTrackExtra.tpcNClsCrossedRows() < v0Selections.minTPCrows) return false; // check minimum TPC crossed rows - V0SelCounter.fill(); - if (posTrackExtra.tpcChi2NCl() >= v0Selections.maxTPCchi2PerNcls) return false; // check maximum TPC chi2 per clusters - V0SelCounter.fill(); - if (posTrackExtra.tpcCrossedRowsOverFindableCls() < v0Selections.minTPCrowsOverFindableClusters) return false; // check minimum fraction of TPC rows over findable - V0SelCounter.fill(); - if (posTrackExtra.tpcFoundOverFindableCls() < v0Selections.minTPCfoundOverFindableClusters) return false; // check minimum fraction of found over findable TPC clusters - V0SelCounter.fill(); - if (posTrackExtra.tpcFractionSharedCls() >= v0Selections.maxFractionTPCSharedClusters) return false; // check the maximum fraction of allowed shared TPC clusters - V0SelCounter.fill(); - if (v0Selections.rejectTPCsectorBoundary && !isTrackFarFromTPCBoundary(v0.positivept(), v0.positivephi(), +1)) return false; // reject track far from TPC sector boundary or not - V0SelCounter.fill(); - - if (negTrackExtra.tpcNClsCrossedRows() < v0Selections.minTPCrows) return false; // check minimum TPC crossed rows - V0SelCounter.fill(); - if (negTrackExtra.tpcChi2NCl() >= v0Selections.maxTPCchi2PerNcls) return false; // check maximum TPC chi2 per clusters - V0SelCounter.fill(); - if (negTrackExtra.tpcCrossedRowsOverFindableCls() < v0Selections.minTPCrowsOverFindableClusters) return false; // check minimum fraction of TPC rows over findable - V0SelCounter.fill(); - if (negTrackExtra.tpcFoundOverFindableCls() < v0Selections.minTPCfoundOverFindableClusters) return false; // check minimum fraction of found over findable TPC clusters - V0SelCounter.fill(); - if (negTrackExtra.tpcFractionSharedCls() >= v0Selections.maxFractionTPCSharedClusters) return false; // check the maximum fraction of allowed shared TPC clusters - V0SelCounter.fill(); - if (v0Selections.rejectTPCsectorBoundary && !isTrackFarFromTPCBoundary(v0.negativept(), v0.negativephi(), -1)) return false; // reject track far from TPC sector boundary or not - V0SelCounter.fill(); - - // ITS only tag - if (v0Selections.requirePosITSonly && posTrackExtra.tpcNClsCrossedRows() > 1) return false; - V0SelCounter.fill(); - if (v0Selections.requireNegITSonly && negTrackExtra.tpcNClsCrossedRows() > 1) return false; - V0SelCounter.fill(); - - // TPC only tag - if (v0Selections.skipTPConly && posTrackExtra.detectorMap() == o2::aod::track::TPC) return false; - V0SelCounter.fill(); - if (v0Selections.skipTPConly && negTrackExtra.detectorMap() == o2::aod::track::TPC) return false; - V0SelCounter.fill(); - - return true; - } - - // Tests the hypothesis of the V0 being a Lambda or of it being an antiLambda. - template - bool passesLambdaLambdaBarHypothesis(TV0 const& v0, TCollision const& collision, bool Lambda_hypothesis){ - // 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()); - 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. - if (dcaPionToPV < v0Selections.dcaPionToPV) return false; - V0SelCounter.fill(); - - const auto posTrackExtra = v0.template posTrack_as(); - const auto negTrackExtra = v0.template negTrack_as(); - - // 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; - - ///// Expensive PID checks come last: - // TPC PID - if (std::fabs(protonTrack.tpcNSigmaPr()) > v0Selections.tpcPidNsigmaCut) return false; - V0SelCounter.fill(); - if (std::fabs(pionTrack.tpcNSigmaPi()) > v0Selections.tpcPidNsigmaCut) return false; - V0SelCounter.fill(); - - // TOF PID in DeltaT (if TOF is not available, then uses the track. If is available, uses it. In this sense, TOF is optional) - // const bool posHasTOF = posTrackExtra.hasTOF(); // For the older version, which worked only for Lambdas - const bool protonHasTOF = protonTrack.hasTOF(); // Should work even without PIDResponseTOF.h, as it is a TracksExtra property - const bool pionHasTOF = pionTrack.hasTOF(); - - // Proton-like track - if (protonHasTOF && std::abs(Lambda_hypothesis ? v0.posTOFDeltaTLaPr() : v0.negTOFDeltaTLaPr()) - > v0Selections.maxDeltaTimeProton) return false; - V0SelCounter.fill(); - // Pion-like track - if (pionHasTOF && std::abs(Lambda_hypothesis ? v0.negTOFDeltaTLaPi() : v0.posTOFDeltaTLaPi()) - > v0Selections.maxDeltaTimePion) return false; - V0SelCounter.fill(); - - // TOF PID in NSigma (TODO: add asymmetric NSigma windows for purity tuning?) - // Proton-like track - if (protonHasTOF && std::fabs(v0.tofNSigmaLaPr()) > v0Selections.tofPidNsigmaCutLaPr) return false; // (No need to select which candidate is which with the Lambda_hypothesis. Automatically done already!) - V0SelCounter.fill(); - // Pion-like track - if (pionHasTOF && std::fabs(v0.tofNSigmaLaPi()) > v0Selections.tofPidNsigmaCutLaPi) return false; - 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, instead of using the correct path length from the - // V0s PV-DCA and the such! In other words, it is a naive estimator of TOF PID, because it does not correct for the V0 - // mother's travel time and considers all tracks as if they came from the PV! - // 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. - - // proper lifetime - if (v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * o2::constants::physics::MassLambda0 > v0Selections.lambdaLifetimeCut) return false; - V0SelCounter.fill(); - - return true; - } + 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 - // 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 - // (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 - // if (std::abs(alpha) < v0Selections.armK0AlphaThreshold && qt < v0Selections.armK0QtThreshold) return kIsArmenterosK0; - // // std::abs(alpha) < 0.2 && qt < 0.1 - // if (std::abs(alpha) < v0Selections.armMinAlpha) return kArmenterosAmbiguous; - // // std::abs(alpha) < 0.01f - // // Lambda selection - // if (alpha > 0) return kIsArmenterosLambda; - // else return kIsArmenterosAntiLambda; - // } + histos.fill(HIST("JetVsLeadingParticleQA/hCosThetaLeadParticleToJet"), cosThetaParticleToJet); + histos.fill(HIST("JetVsLeadingParticleQA/hDeltaPhiLeadParticleToJet"), deltaPhiParticleToJet); + histos.fill(HIST("JetVsLeadingParticleQA/hDeltaEtaToLeadParticleToJet"), deltaEtaParticleToJet); - // TODO: another possible check that could be done (if not implemented already inside mLambda() getters) - // template - // int isCandidateMassLambda(TV0 const& v0) { - // float m1 = v0.mLambda(); // proton=positive - // float m2 = v0.mAntiLambda(); // proton=negative - // float d = std::abs(m1 - mLambdaTrue) - std::abs(m2 - mLambdaTrue); - // if (d < 0.f) return +1; // Lambda - // else return -1; // AntiLambda - // } + histos.fill(HIST("JetVsLeadingParticleQA/h2dDeltaPhiParticleToLeadvsDeltaEtaParticleToLead"), deltaPhiParticleToJet, deltaEtaParticleToJet); - void processJetsData(SelCollisionsSimple::iterator const& collision, PseudoJetTracks const& tracks, aod::BCsWithTimestamps const& bcs){ // Uses BCsWithTimestamps to get timestamps for rejectTPCsectorBoundary - float centrality = -1.0f; // Just a placeholder - - // 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 validJetAlreadyFound = false; // Do not fill Event QA more than once - - auto bc = bcs.iteratorAt(collision.bcId()); // Got the iteratorAt() idea from O2Physics/PWGUD/Core/UDHelpers.h - if (!isEventAccepted(collision, bc, centrality, false)) return; // Uses return instead of continue, as there is no explicit loop here - const uint64_t collIdx = collision.globalIndex(); - - // Loop over reconstructed tracks: - std::vector fjParticles; - 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" - - // Require that tracks pass selection criteria - if (!isCandidateForChargedPseudojetAccepted(track)) continue; - - // Constructing pseudojet candidates vector: - // Using pion mass as hypothesis for track energy estimate (before PID, all particles treated as if with the same invariant mass) - // (TODO: study the possibility of using identified PseudoJet candidates for this estimate) - fastjet::PseudoJet candidate(track.px(), track.py(), track.pz(), track.energy(o2::constants::physics::MassPionCharged)); - fjParticles.emplace_back(candidate); - - // Calculating leading particle - float pt = candidate.pt(); - if (pt > leadingParticlePt){ - leadingParticlePt = pt; - leadingParticleIdx = fjParticles.size() - 1; - } - } - // Reject empty events - if (fjParticles.size() < 1) return; + histos.fill(HIST("JetVsLeadingParticleQA/h2dJetsPerEventvsDeltaPhiParticleToLead"), jetsInEvent, deltaPhiParticleToJet); + histos.fill(HIST("JetVsLeadingParticleQA/h2dJetsPerEventvsDeltaEtaParticleToLead"), jetsInEvent, deltaEtaParticleToJet); + histos.fill(HIST("JetVsLeadingParticleQA/h2dJetsPerEventvsCosThetaParticleToLead"), jetsInEvent, cosThetaParticleToJet); - auto const& leadingParticle = fjParticles[leadingParticleIdx]; - if (leadingParticle.pt() > jetConfigurations.minLeadParticlePt){ // If not, leading particle is probably a bad proxy - tableLeadParticles(collIdx, leadingParticle.pt(), leadingParticle.eta(), leadingParticle.phi()); - } + histos.fill(HIST("JetVsLeadingParticleQA/h2dJetsPerEventvsLeadParticlePt"), jetsInEvent, leadingParticle.pt()); + histos.fill(HIST("JetVsLeadingParticleQA/h2dLeadJetPtvsLeadParticlePt"), leadingJet.pt(), leadingParticle.pt()); - // Start jet clusterization: - // Cluster particles using the anti-kt algorithm - fastjet::JetDefinition jetDef(mapFJAlgorithm(jetConfigurations.jetAlgorithm), jetConfigurations.radiusJet, mapFJRecombScheme(jetConfigurations.jetRecombScheme)); - // std::vector jets_pt, jets_eta, jets_phi; // Not worth it to store 4-vectors: the tracks assume pion mass hypothesis, so energy and rapidity are not right. - if (jetConfigurations.bkgSubtraction == kAreaBased){ - 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()) return; - // 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; - // bool hasLeadingJet = false; // Not needed: if the event has any jet, that is the leading jet. Check is superseded by the selectedJets information - float leadingJetPt = -1.f; - 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)) continue; - - auto jetForSub = jet; - // Subtracts same background estimated for highest pt jet, but every jet might have a slightly different area - // (TODO: check possible problems with OO and physics impacts of this particular cone method and choice of single background estimator based on leading jet) - // (TODO: improve for Pb-Pb, specially central!) - fastjet::PseudoJet jetMinusBkg = backgroundSub.doRhoAreaSub(jetForSub, rhoPerp, rhoMPerp); - // Jet pt must be larger than threshold: - if (jetMinusBkg.pt() < jetConfigurations.minJetPt) continue; - selectedJets++; - - // Store jet: - tableJets(collIdx, - jetMinusBkg.pt(), - jetMinusBkg.eta(), // Using eta instead of rapidity - jetMinusBkg.phi(), - jetMinusBkg.constituents().size() - ); - - // Finding the leading jet after subtraction (leading jet is NOT known a priori!): - if (jetMinusBkg.pt() > leadingJetPt) { - leadingJetPt = jetMinusBkg.pt(); - leadingJetSub = jetMinusBkg; - } - } - histos.fill(HIST("hJetsPerEvent"), selectedJets); - if (selectedJets == 0) return; - histos.fill(HIST("hEventsWithJet"), 0.5); - // Another version of this counter, which is already integrated in the Event Selection flow: - if (doEventQA && !validJetAlreadyFound) fillEventSelectionQA(lastBinEvSel-1, centrality); // hasRingJet passes - validJetAlreadyFound = true; - - if (doJetKinematicsQA){ - histos.fill(HIST("JetKinematicsQA/hLeadingJetPt"), leadingJetSub.pt()); - 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; - - float cosTheta = cosThetaJets(leadingJetSub, jetMinusBkg); - float deltaPhi = RecoDecay::constrainAngle(leadingJetSub.phi() - jetMinusBkg.phi(), -o2::constants::math::PI); - float deltaEta = leadingJetSub.eta() - jetMinusBkg.eta(); - float deltaR = std::sqrt(deltaPhi*deltaPhi + deltaEta*deltaEta); - - histos.fill(HIST("JetKinematicsQA/hCosThetaToLeadingJet"), cosTheta); - histos.fill(HIST("JetKinematicsQA/hDeltaPhiToLeadingJet"), deltaPhi); - histos.fill(HIST("JetKinematicsQA/hDeltaEtaToLeadingJet"), deltaEta); - histos.fill(HIST("JetKinematicsQA/hDeltaRToLeadingJet"), deltaR); - - // 2D correlations: - histos.fill(HIST("JetKinematicsQA/h2dJetsPerEventvsLeadJetPt"), selectedJets, leadingJetSub.pt()); - histos.fill(HIST("JetKinematicsQA/h2dJetsPerEventvsJetPt"), selectedJets, jetMinusBkg.pt()); - histos.fill(HIST("JetKinematicsQA/h2dCosThetaToLeadvsDeltaPhiToLead"), cosTheta, deltaPhi); - histos.fill(HIST("JetKinematicsQA/h2dCosThetaToLeadvsDeltaEtaToLead"), cosTheta, deltaEta); - histos.fill(HIST("JetKinematicsQA/h2dCosThetaToLeadvsDeltaRToLead"), cosTheta, deltaR); - histos.fill(HIST("JetKinematicsQA/h2dDeltaPhiToLeadvsDeltaEtaToLead"), deltaPhi, deltaEta); - - histos.fill(HIST("JetKinematicsQA/h2dJetPtvsDeltaPhiToLead"), jetMinusBkg.pt(), deltaPhi); // Can't really get the energy of the jet, just the pt to make this comparison - histos.fill(HIST("JetKinematicsQA/h2dJetEnergyvsDeltaPhiToLead"), jetMinusBkg.E(), deltaPhi); // Just a different scale - histos.fill(HIST("JetKinematicsQA/h2dJetEnergyvsCosThetaToLead"), jetMinusBkg.E(), cosTheta); - - histos.fill(HIST("JetKinematicsQA/h2dJetsPerEventvsDeltaPhiToLead"), selectedJets, deltaPhi); - histos.fill(HIST("JetKinematicsQA/h2dJetsPerEventvsDeltaEtaToLead"), selectedJets, deltaEta); - histos.fill(HIST("JetKinematicsQA/h2dJetsPerEventvsCosThetaToLead"), selectedJets, cosTheta); - } - // Leading particle comparisons: - histos.fill(HIST("JetVsLeadingParticleQA/hLeadingParticlePt"), leadingParticle.pt()); - histos.fill(HIST("JetVsLeadingParticleQA/hLeadingParticleEta"), leadingParticle.eta()); - histos.fill(HIST("JetVsLeadingParticleQA/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("JetVsLeadingParticleQA/h2dDeltaPhiParticleToLeadvsDeltaEtaParticleToLead"), deltaPhiParticleToJet, deltaEtaParticleToJet); - - histos.fill(HIST("JetVsLeadingParticleQA/h2dJetsPerEventvsDeltaPhiParticleToLead"), selectedJets, deltaPhiParticleToJet); - histos.fill(HIST("JetVsLeadingParticleQA/h2dJetsPerEventvsDeltaEtaParticleToLead"), selectedJets, deltaEtaParticleToJet); - histos.fill(HIST("JetVsLeadingParticleQA/h2dJetsPerEventvsCosThetaParticleToLead"), selectedJets, cosThetaParticleToJet); - - histos.fill(HIST("JetVsLeadingParticleQA/h2dJetsPerEventvsLeadParticlePt"), selectedJets, leadingParticle.pt()); - histos.fill(HIST("JetVsLeadingParticleQA/h2dLeadJetPtvsLeadParticlePt"), leadingJetSub.pt(), leadingParticle.pt()); - - histos.fill(HIST("JetVsLeadingParticleQA/h2dLeadJetPtvsCosThetaParticleToLead"), leadingJetSub.pt(), cosThetaParticleToJet); - histos.fill(HIST("JetVsLeadingParticleQA/h2dLeadParticlePtvsCosThetaParticleToLead"), leadingParticle.pt(), cosThetaParticleToJet); - - 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); - } - } - else{ // Otherwise, simple jet clustering (TODO: this is the fall back for kConstituentBased while not implemented) - fastjet::ClusterSequence clustSeq(fjParticles, jetDef); - // Jet pt must be larger than threshold: - std::vector jets = fastjet::sorted_by_pt(clustSeq.inclusive_jets(jetConfigurations.minJetPt)); - - const int jetsInEvent = jets.size(); - histos.fill(HIST("hJetsPerEvent"), jetsInEvent); // Fills even in empty events, as this is a useful number to know! - - if (jetsInEvent == 0) return; - histos.fill(HIST("hEventsWithJet"), 0.5); - // Another version of this counter, which is already integrated in the Event Selection flow: - if (doEventQA && !validJetAlreadyFound) fillEventSelectionQA(lastBinEvSel-1, centrality); // hasRingJet passes - validJetAlreadyFound = true; - - 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)) continue; - - tableJets(collIdx, - jet.pt(), - jet_eta, // Using eta instead of rapidity - jet.phi(), - jet.constituents().size() - ); - - if (doJetKinematicsQA){ - histos.fill(HIST("JetKinematicsQA/hJetPt"), jet.pt()); - histos.fill(HIST("JetKinematicsQA/hJetEta"), jet_eta); - histos.fill(HIST("JetKinematicsQA/hJetPhi"), 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 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! - histos.fill(HIST("JetKinematicsQA/hDeltaPhiToLeadingJet"), deltaPhi); - histos.fill(HIST("JetKinematicsQA/hDeltaEtaToLeadingJet"), deltaEta); - histos.fill(HIST("JetKinematicsQA/hDeltaRToLeadingJet"), deltaR); - - // 2D correlations: - histos.fill(HIST("JetKinematicsQA/h2dJetsPerEventvsLeadJetPt"), jetsInEvent, leadingJet.pt()); - histos.fill(HIST("JetKinematicsQA/h2dJetsPerEventvsJetPt"), jetsInEvent, jet.pt()); - histos.fill(HIST("JetKinematicsQA/h2dCosThetaToLeadvsDeltaPhiToLead"), cosTheta, deltaPhi); - histos.fill(HIST("JetKinematicsQA/h2dCosThetaToLeadvsDeltaEtaToLead"), cosTheta, deltaEta); - histos.fill(HIST("JetKinematicsQA/h2dCosThetaToLeadvsDeltaRToLead"), cosTheta, deltaR); - histos.fill(HIST("JetKinematicsQA/h2dDeltaPhiToLeadvsDeltaEtaToLead"), deltaPhi, deltaEta); - - histos.fill(HIST("JetKinematicsQA/h2dJetPtvsDeltaPhiToLead"), jet.pt(), deltaPhi); // Can't really get the energy of the jet, just the pt to make this comparison - histos.fill(HIST("JetKinematicsQA/h2dJetEnergyvsDeltaPhiToLead"), jet.E(), deltaPhi); // Just a different scale - histos.fill(HIST("JetKinematicsQA/h2dJetEnergyvsCosThetaToLead"), jet.E(), cosTheta); - - histos.fill(HIST("JetKinematicsQA/h2dJetsPerEventvsDeltaPhiToLead"), jetsInEvent, deltaPhi); - histos.fill(HIST("JetKinematicsQA/h2dJetsPerEventvsDeltaEtaToLead"), jetsInEvent, deltaEta); - histos.fill(HIST("JetKinematicsQA/h2dJetsPerEventvsCosThetaToLead"), jetsInEvent, cosTheta); - } - } - if (doJetKinematicsQA){ - histos.fill(HIST("JetKinematicsQA/hLeadingJetPt"), leadingJet.pt()); - histos.fill(HIST("JetKinematicsQA/hLeadingJetEta"), leadingJet.eta()); - 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()); - - 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("JetVsLeadingParticleQA/h2dDeltaPhiParticleToLeadvsDeltaEtaParticleToLead"), deltaPhiParticleToJet, deltaEtaParticleToJet); - - histos.fill(HIST("JetVsLeadingParticleQA/h2dJetsPerEventvsDeltaPhiParticleToLead"), jetsInEvent, deltaPhiParticleToJet); - histos.fill(HIST("JetVsLeadingParticleQA/h2dJetsPerEventvsDeltaEtaParticleToLead"), jetsInEvent, deltaEtaParticleToJet); - histos.fill(HIST("JetVsLeadingParticleQA/h2dJetsPerEventvsCosThetaParticleToLead"), jetsInEvent, cosThetaParticleToJet); - - histos.fill(HIST("JetVsLeadingParticleQA/h2dJetsPerEventvsLeadParticlePt"), jetsInEvent, leadingParticle.pt()); - histos.fill(HIST("JetVsLeadingParticleQA/h2dLeadJetPtvsLeadParticlePt"), leadingJet.pt(), leadingParticle.pt()); - - histos.fill(HIST("JetVsLeadingParticleQA/h2dLeadJetPtvsCosThetaParticleToLead"), leadingJet.pt(), cosThetaParticleToJet); - histos.fill(HIST("JetVsLeadingParticleQA/h2dLeadParticlePtvsCosThetaParticleToLead"), leadingParticle.pt(), cosThetaParticleToJet); - - 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); - } - } - } + histos.fill(HIST("JetVsLeadingParticleQA/h2dLeadJetPtvsCosThetaParticleToLead"), leadingJet.pt(), cosThetaParticleToJet); + histos.fill(HIST("JetVsLeadingParticleQA/h2dLeadParticlePtvsCosThetaParticleToLead"), leadingParticle.pt(), cosThetaParticleToJet); - // Had to include DauTracks in subscription, even though I don't loop in it, for the indices - // to resolve, avoiding " Exception while running: Index pointing to Tracks is not bound!" - // Added the compiler option [[maybe_unused]] to avoid triggering any warnings because of this - void processV0sData(SelCollisions::iterator const& collision, V0CandidatesWithTOF const& fullV0s, aod::BCsWithTimestamps const& bcs, [[maybe_unused]] DauTracks const& V0DauTracks){ - 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); - - auto bc = bcs.iteratorAt(collision.bcId()); - if (!isEventAccepted(collision, bc, centrality, doEventQA)) return; // Uses return instead of continue, as there is no explicit loop here - - if (doEventQA) fillCentralityProperties(collision, centrality); - const uint64_t collIdx = collision.globalIndex(); - if (v0Selections.rejectTPCsectorBoundary) initCCDB(bc); // Substituted call from collision to bc for raw data - - // Fill event table: - tableCollisions(collIdx, - collision.centFT0M(), - collision.centFT0C(), - collision.centFV0A() - ); // (TODO: add InteractionRate info and other useful cuts for later on in the analysis?) - - uint NLambdas = 0; // Counting particles per event - uint NAntiLambdas = 0; - uint NAmbiguous = 0; - for (auto const& v0 : fullV0s){ - V0SelCounter.resetForNewV0(); - 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)) continue; - - if (doArmenterosQA) histos.fill(HIST("GeneralQA/h2dArmenterosKinematicSelected"), v0.alpha(), v0.qtarm()); - - // Else, just continue the loop: - bool isLambda = false; - bool isAntiLambda = false; - if (analyseLambda) isLambda = passesLambdaLambdaBarHypothesis(v0, collision, true); - if (analyseAntiLambda) isAntiLambda = passesLambdaLambdaBarHypothesis(v0, collision, false); - - if (!isLambda && !isAntiLambda) continue; // Candidate is not considered to be a Lambda - - if (isLambda) NLambdas++; - if (isAntiLambda) NAntiLambdas++; - - if (doArmenterosQA) histos.fill(HIST("GeneralQA/h2dArmenterosFullSelected"), v0.alpha(), v0.qtarm()); // cross-check - if (isLambda && !isAntiLambda) histos.fill(HIST("GeneralQA/h2dArmenterosFullSelectedLambda"), v0.alpha(), v0.qtarm()); - if (!isLambda && isAntiLambda) histos.fill(HIST("GeneralQA/h2dArmenterosFullSelectedAntiLambda"), v0.alpha(), v0.qtarm()); - - // int lambdaIdx = -1; // No need to pass armenteros - if (isLambda && isAntiLambda) { - 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 - - // Armenteros cut is not worth it! From QA histograms, only about 0.05% of ambiguous candidates are in the regions probable to be Lamda/AntiLambdas! - // The statistics gain is not worth it. - // // Third and final check to distinguish between Lambda and AntiLambda ambiguous v0s: - // // (This check is only performed to recycle AMBIGUOUS candidates! Not a hard cut on all candidates!) - // lambdaIdx = isCandidateArmenterosLambda(v0.alpha(), v0.qtarm()); - } - // 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) - // v0.mLambda() - - // 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(collIdx, - v0pt, v0.eta(), v0.phi(), // Using eta instead of rapidity - isLambda, isAntiLambda, - v0.mLambda(), v0.mAntiLambda(), - v0.positivept(), v0.positiveeta(), v0.positivephi(), - v0.negativept(), v0.negativeeta(), v0.negativephi(), - posTrackExtra.tpcNSigmaPr(), posTrackExtra.tpcNSigmaPi(), - negTrackExtra.tpcNSigmaPr(), negTrackExtra.tpcNSigmaPi(), - v0.v0cosPA(), v0.v0radius(), v0.dcaV0daughters(), v0.dcapostopv(), v0.dcanegtopv() - ); - if (doEventQA && !validV0AlreadyFound) fillEventSelectionQA(lastBinEvSel, centrality); // hasRingV0 passes - validV0AlreadyFound = true; - - if (doV0KinematicQA){ - // Cache kinematics once - const float v0y = v0.yLambda(); - const float v0phi = v0.phi(); - const float mLambda = v0.mLambda(); - const float mAntiLambda = v0.mAntiLambda(); - if (analyseLambda && isLambda){ - // --- Basic kinematics --- - histos.fill(HIST("V0KinematicQA/Lambda/hPt"), v0pt); - histos.fill(HIST("V0KinematicQA/Lambda/hY"), v0y); - histos.fill(HIST("V0KinematicQA/Lambda/hPhi"), v0phi); - // --- Mass correlations --- - histos.fill(HIST("V0KinematicQA/Lambda/hMassVsPt"), v0pt, mLambda); - histos.fill(HIST("V0KinematicQA/Lambda/hMassVsY"), v0y, mLambda); - histos.fill(HIST("V0KinematicQA/Lambda/hMassVsPhi"), v0phi, mLambda); - // --- Kinematic correlations --- - histos.fill(HIST("V0KinematicQA/Lambda/hYVsPt"), v0pt, v0y); - histos.fill(HIST("V0KinematicQA/Lambda/hPhiVsPt"), v0pt, v0phi); - } - if (analyseAntiLambda && isAntiLambda){ - // --- Basic kinematics --- - histos.fill(HIST("V0KinematicQA/AntiLambda/hPt"), v0pt); - histos.fill(HIST("V0KinematicQA/AntiLambda/hY"), v0y); - histos.fill(HIST("V0KinematicQA/AntiLambda/hPhi"), v0phi); - // --- Mass correlations --- - histos.fill(HIST("V0KinematicQA/AntiLambda/hMassVsPt"), v0pt, mAntiLambda); - histos.fill(HIST("V0KinematicQA/AntiLambda/hMassVsY"), v0y, mAntiLambda); - histos.fill(HIST("V0KinematicQA/AntiLambda/hMassVsPhi"), v0phi, mAntiLambda); - // --- Kinematic correlations --- - histos.fill(HIST("V0KinematicQA/AntiLambda/hYVsPt"), v0pt, v0y); - histos.fill(HIST("V0KinematicQA/AntiLambda/hPhiVsPt"), v0pt, v0phi); - } - } - - if (doCompleteTopoQA){ - // 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()); - histos.fill(HIST("V0KinematicQA/hPointingAngle"), std::acos(v0.v0cosPA())); - histos.fill(HIST("V0KinematicQA/hV0Radius"), v0.v0radius()); - histos.fill(HIST("V0KinematicQA/h2dPositiveITSvsTPCpts"), posTrackExtra.tpcNClsCrossedRows(), posTrackExtra.itsNCls()); - 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()); - histos.fill(HIST("Lambda/hPosDCAToPV"), v0.dcapostopv()); - histos.fill(HIST("Lambda/hNegDCAToPV"), v0.dcanegtopv()); - histos.fill(HIST("Lambda/hDCADaughters"), v0.dcaV0daughters()); - histos.fill(HIST("Lambda/hPointingAngle"), std::acos(v0.v0cosPA())); - histos.fill(HIST("Lambda/hV0Radius"), v0.v0radius()); - histos.fill(HIST("Lambda/h2dPositiveITSvsTPCpts"), posTrackExtra.tpcNClsCrossedRows(), posTrackExtra.itsNCls()); - histos.fill(HIST("Lambda/h2dNegativeITSvsTPCpts"), negTrackExtra.tpcNClsCrossedRows(), negTrackExtra.itsNCls()); - histos.fill(HIST("Lambda/h2dPositivePtVsPhi"), v0.positivept(), computePhiMod(v0.positivephi(), 1)); - histos.fill(HIST("Lambda/h2dNegativePtVsPhi"), v0.negativept(), computePhiMod(v0.negativephi(), -1)); - if (doTPCQA) { - histos.fill(HIST("Lambda/h3dPosNsigmaTPC"), centrality, v0pt, posTrackExtra.tpcNSigmaPr()); - histos.fill(HIST("Lambda/h3dNegNsigmaTPC"), centrality, v0pt, negTrackExtra.tpcNSigmaPi()); - histos.fill(HIST("Lambda/h3dPosTPCsignal"), centrality, v0pt, posTrackExtra.tpcSignal()); - histos.fill(HIST("Lambda/h3dNegTPCsignal"), centrality, v0pt, negTrackExtra.tpcSignal()); - histos.fill(HIST("Lambda/h3dPosNsigmaTPCvsTrackPtot"), centrality, v0.pfracpos() * v0.p(), posTrackExtra.tpcNSigmaPr()); - histos.fill(HIST("Lambda/h3dNegNsigmaTPCvsTrackPtot"), centrality, v0.pfracneg() * v0.p(), negTrackExtra.tpcNSigmaPi()); - histos.fill(HIST("Lambda/h3dPosTPCsignalVsTrackPtot"), centrality, v0.pfracpos() * v0.p(), posTrackExtra.tpcSignal()); - histos.fill(HIST("Lambda/h3dNegTPCsignalVsTrackPtot"), centrality, v0.pfracneg() * v0.p(), negTrackExtra.tpcSignal()); - histos.fill(HIST("Lambda/h3dPosNsigmaTPCvsTrackPt"), centrality, v0.positivept(), posTrackExtra.tpcNSigmaPr()); - histos.fill(HIST("Lambda/h3dNegNsigmaTPCvsTrackPt"), centrality, v0.negativept(), negTrackExtra.tpcNSigmaPi()); - histos.fill(HIST("Lambda/h3dPosTPCsignalVsTrackPt"), centrality, v0.positivept(), posTrackExtra.tpcSignal()); - histos.fill(HIST("Lambda/h3dNegTPCsignalVsTrackPt"), centrality, v0.negativept(), negTrackExtra.tpcSignal()); - } - if (doTOFQA) { - histos.fill(HIST("Lambda/h3dPosNsigmaTOF"), centrality, v0pt, v0.tofNSigmaLaPr()); - histos.fill(HIST("Lambda/h3dNegNsigmaTOF"), centrality, v0pt, v0.tofNSigmaLaPi()); - histos.fill(HIST("Lambda/h3dPosTOFdeltaT"), centrality, v0pt, v0.posTOFDeltaTLaPr()); - histos.fill(HIST("Lambda/h3dNegTOFdeltaT"), centrality, v0pt, v0.negTOFDeltaTLaPi()); - histos.fill(HIST("Lambda/h3dPosNsigmaTOFvsTrackPtot"), centrality, v0.pfracpos() * v0.p(), v0.tofNSigmaLaPr()); - histos.fill(HIST("Lambda/h3dNegNsigmaTOFvsTrackPtot"), centrality, v0.pfracneg() * v0.p(), v0.tofNSigmaLaPi()); - histos.fill(HIST("Lambda/h3dPosTOFdeltaTvsTrackPtot"), centrality, v0.pfracpos() * v0.p(), v0.posTOFDeltaTLaPr()); - histos.fill(HIST("Lambda/h3dNegTOFdeltaTvsTrackPtot"), centrality, v0.pfracneg() * v0.p(), v0.negTOFDeltaTLaPi()); - histos.fill(HIST("Lambda/h3dPosNsigmaTOFvsTrackPt"), centrality, v0.positivept(), v0.tofNSigmaLaPr()); - histos.fill(HIST("Lambda/h3dNegNsigmaTOFvsTrackPt"), centrality, v0.negativept(), v0.tofNSigmaLaPi()); - histos.fill(HIST("Lambda/h3dPosTOFdeltaTvsTrackPt"), centrality, v0.positivept(), v0.posTOFDeltaTLaPr()); - 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()); - histos.fill(HIST("AntiLambda/h3dMassAntiLambda"), centrality, v0pt, v0.mAntiLambda()); - histos.fill(HIST("AntiLambda/hPosDCAToPV"), v0.dcapostopv()); - histos.fill(HIST("AntiLambda/hNegDCAToPV"), v0.dcanegtopv()); - histos.fill(HIST("AntiLambda/hDCADaughters"), v0.dcaV0daughters()); - histos.fill(HIST("AntiLambda/hPointingAngle"), std::acos(v0.v0cosPA())); - histos.fill(HIST("AntiLambda/hV0Radius"), v0.v0radius()); - histos.fill(HIST("AntiLambda/h2dPositiveITSvsTPCpts"), posTrackExtra.tpcNClsCrossedRows(), posTrackExtra.itsNCls()); - histos.fill(HIST("AntiLambda/h2dNegativeITSvsTPCpts"), negTrackExtra.tpcNClsCrossedRows(), negTrackExtra.itsNCls()); - histos.fill(HIST("AntiLambda/h2dPositivePtVsPhi"), v0.positivept(), computePhiMod(v0.positivephi(), 1)); - histos.fill(HIST("AntiLambda/h2dNegativePtVsPhi"), v0.negativept(), computePhiMod(v0.negativephi(), -1)); - if (doTPCQA) { - histos.fill(HIST("AntiLambda/h3dPosNsigmaTPC"), centrality, v0pt, posTrackExtra.tpcNSigmaPi()); - histos.fill(HIST("AntiLambda/h3dNegNsigmaTPC"), centrality, v0pt, negTrackExtra.tpcNSigmaPr()); - histos.fill(HIST("AntiLambda/h3dPosTPCsignal"), centrality, v0pt, posTrackExtra.tpcSignal()); - histos.fill(HIST("AntiLambda/h3dNegTPCsignal"), centrality, v0pt, negTrackExtra.tpcSignal()); - histos.fill(HIST("AntiLambda/h3dPosNsigmaTPCvsTrackPtot"), centrality, v0.pfracpos() * v0.p(), posTrackExtra.tpcNSigmaPi()); - histos.fill(HIST("AntiLambda/h3dNegNsigmaTPCvsTrackPtot"), centrality, v0.pfracneg() * v0.p(), negTrackExtra.tpcNSigmaPr()); - histos.fill(HIST("AntiLambda/h3dPosTPCsignalVsTrackPtot"), centrality, v0.pfracpos() * v0.p(), posTrackExtra.tpcSignal()); - histos.fill(HIST("AntiLambda/h3dNegTPCsignalVsTrackPtot"), centrality, v0.pfracneg() * v0.p(), negTrackExtra.tpcSignal()); - histos.fill(HIST("AntiLambda/h3dPosNsigmaTPCvsTrackPt"), centrality, v0.positivept(), posTrackExtra.tpcNSigmaPi()); - histos.fill(HIST("AntiLambda/h3dNegNsigmaTPCvsTrackPt"), centrality, v0.negativept(), negTrackExtra.tpcNSigmaPr()); - histos.fill(HIST("AntiLambda/h3dPosTPCsignalVsTrackPt"), centrality, v0.positivept(), posTrackExtra.tpcSignal()); - histos.fill(HIST("AntiLambda/h3dNegTPCsignalVsTrackPt"), centrality, v0.negativept(), negTrackExtra.tpcSignal()); - } - if (doTOFQA) { - histos.fill(HIST("AntiLambda/h3dPosNsigmaTOF"), centrality, v0pt, v0.tofNSigmaALaPi()); - histos.fill(HIST("AntiLambda/h3dNegNsigmaTOF"), centrality, v0pt, v0.tofNSigmaALaPr()); - histos.fill(HIST("AntiLambda/h3dPosTOFdeltaT"), centrality, v0pt, v0.posTOFDeltaTLaPi()); - histos.fill(HIST("AntiLambda/h3dNegTOFdeltaT"), centrality, v0pt, v0.negTOFDeltaTLaPr()); - histos.fill(HIST("AntiLambda/h3dPosNsigmaTOFvsTrackPtot"), centrality, v0.pfracpos() * v0.p(), v0.tofNSigmaALaPi()); - histos.fill(HIST("AntiLambda/h3dNegNsigmaTOFvsTrackPtot"), centrality, v0.pfracneg() * v0.p(), v0.tofNSigmaALaPr()); - histos.fill(HIST("AntiLambda/h3dPosTOFdeltaTvsTrackPtot"), centrality, v0.pfracpos() * v0.p(), v0.posTOFDeltaTLaPi()); - histos.fill(HIST("AntiLambda/h3dNegTOFdeltaTvsTrackPtot"), centrality, v0.pfracneg() * v0.p(), v0.negTOFDeltaTLaPr()); - histos.fill(HIST("AntiLambda/h3dPosNsigmaTOFvsTrackPt"), centrality, v0.positivept(), v0.tofNSigmaALaPi()); - histos.fill(HIST("AntiLambda/h3dNegNsigmaTOFvsTrackPt"), centrality, v0.negativept(), v0.tofNSigmaALaPr()); - histos.fill(HIST("AntiLambda/h3dPosTOFdeltaTvsTrackPt"), centrality, v0.positivept(), v0.posTOFDeltaTLaPi()); - 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 - - // 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); + 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); + } } - - PROCESS_SWITCH(lambdajetpolarizationions, processJetsData, "Process jets and produce derived data in Run 3 Data", true); - PROCESS_SWITCH(lambdajetpolarizationions, processV0sData, "Process V0s and produce derived data in Run 3 Data", true); - // PROCESS_SWITCH(lambdajetpolarizationions, processJetsMC, "Process jets and produced derived data in Run 3 MC", true); - // PROCESS_SWITCH(lambdajetpolarizationions, processV0sMC, "Process V0s and produce derived data in Run 3 MC", true); + } + + // Had to include DauTracks in subscription, even though I don't loop in it, for the indices + // to resolve, avoiding " Exception while running: Index pointing to Tracks is not bound!" + // Added the compiler option [[maybe_unused]] to avoid triggering any warnings because of this + void processV0sData(SelCollisions::iterator const& collision, V0CandidatesWithTOF const& fullV0s, aod::BCsWithTimestamps const& bcs, [[maybe_unused]] DauTracks const& V0DauTracks) + { + 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); + + auto bc = bcs.iteratorAt(collision.bcId()); + if (!isEventAccepted(collision, bc, centrality, doEventQA)) + return; // Uses return instead of continue, as there is no explicit loop here + + if (doEventQA) + fillCentralityProperties(collision, centrality); + const uint64_t collIdx = collision.globalIndex(); + if (v0Selections.rejectTPCsectorBoundary) + initCCDB(bc); // Substituted call from collision to bc for raw data + + // Fill event table: + tableCollisions(collIdx, + collision.centFT0M(), + collision.centFT0C(), + collision.centFV0A()); // (TODO: add InteractionRate info and other useful cuts for later on in the analysis?) + + uint NLambdas = 0; // Counting particles per event + uint NAntiLambdas = 0; + uint NAmbiguous = 0; + for (auto const& v0 : fullV0s) { + V0SelCounter.resetForNewV0(); + 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)) + continue; + + if (doArmenterosQA) + histos.fill(HIST("GeneralQA/h2dArmenterosKinematicSelected"), v0.alpha(), v0.qtarm()); + + // Else, just continue the loop: + bool isLambda = false; + bool isAntiLambda = false; + if (analyseLambda) + isLambda = passesLambdaLambdaBarHypothesis(v0, collision, true); + if (analyseAntiLambda) + isAntiLambda = passesLambdaLambdaBarHypothesis(v0, collision, false); + + if (!isLambda && !isAntiLambda) + continue; // Candidate is not considered to be a Lambda + + if (isLambda) + NLambdas++; + if (isAntiLambda) + NAntiLambdas++; + + if (doArmenterosQA) + histos.fill(HIST("GeneralQA/h2dArmenterosFullSelected"), v0.alpha(), v0.qtarm()); // cross-check + if (isLambda && !isAntiLambda) + histos.fill(HIST("GeneralQA/h2dArmenterosFullSelectedLambda"), v0.alpha(), v0.qtarm()); + if (!isLambda && isAntiLambda) + histos.fill(HIST("GeneralQA/h2dArmenterosFullSelectedAntiLambda"), v0.alpha(), v0.qtarm()); + + // int lambdaIdx = -1; // No need to pass armenteros + if (isLambda && isAntiLambda) { + 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 + + // Armenteros cut is not worth it! From QA histograms, only about 0.05% of ambiguous candidates are in the regions probable to be Lamda/AntiLambdas! + // The statistics gain is not worth it. + // // Third and final check to distinguish between Lambda and AntiLambda ambiguous v0s: + // // (This check is only performed to recycle AMBIGUOUS candidates! Not a hard cut on all candidates!) + // lambdaIdx = isCandidateArmenterosLambda(v0.alpha(), v0.qtarm()); + } + // 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) + // v0.mLambda() + + // 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(collIdx, + v0pt, v0.eta(), v0.phi(), // Using eta instead of rapidity + isLambda, isAntiLambda, + v0.mLambda(), v0.mAntiLambda(), + v0.positivept(), v0.positiveeta(), v0.positivephi(), + v0.negativept(), v0.negativeeta(), v0.negativephi(), + posTrackExtra.tpcNSigmaPr(), posTrackExtra.tpcNSigmaPi(), + negTrackExtra.tpcNSigmaPr(), negTrackExtra.tpcNSigmaPi(), + v0.v0cosPA(), v0.v0radius(), v0.dcaV0daughters(), v0.dcapostopv(), v0.dcanegtopv()); + if (doEventQA && !validV0AlreadyFound) + fillEventSelectionQA(lastBinEvSel, centrality); // hasRingV0 passes + validV0AlreadyFound = true; + + if (doV0KinematicQA) { + // Cache kinematics once + const float v0y = v0.yLambda(); + const float v0phi = v0.phi(); + const float mLambda = v0.mLambda(); + const float mAntiLambda = v0.mAntiLambda(); + if (analyseLambda && isLambda) { + // --- Basic kinematics --- + histos.fill(HIST("V0KinematicQA/Lambda/hPt"), v0pt); + histos.fill(HIST("V0KinematicQA/Lambda/hY"), v0y); + histos.fill(HIST("V0KinematicQA/Lambda/hPhi"), v0phi); + // --- Mass correlations --- + histos.fill(HIST("V0KinematicQA/Lambda/hMassVsPt"), v0pt, mLambda); + histos.fill(HIST("V0KinematicQA/Lambda/hMassVsY"), v0y, mLambda); + histos.fill(HIST("V0KinematicQA/Lambda/hMassVsPhi"), v0phi, mLambda); + // --- Kinematic correlations --- + histos.fill(HIST("V0KinematicQA/Lambda/hYVsPt"), v0pt, v0y); + histos.fill(HIST("V0KinematicQA/Lambda/hPhiVsPt"), v0pt, v0phi); + } + if (analyseAntiLambda && isAntiLambda) { + // --- Basic kinematics --- + histos.fill(HIST("V0KinematicQA/AntiLambda/hPt"), v0pt); + histos.fill(HIST("V0KinematicQA/AntiLambda/hY"), v0y); + histos.fill(HIST("V0KinematicQA/AntiLambda/hPhi"), v0phi); + // --- Mass correlations --- + histos.fill(HIST("V0KinematicQA/AntiLambda/hMassVsPt"), v0pt, mAntiLambda); + histos.fill(HIST("V0KinematicQA/AntiLambda/hMassVsY"), v0y, mAntiLambda); + histos.fill(HIST("V0KinematicQA/AntiLambda/hMassVsPhi"), v0phi, mAntiLambda); + // --- Kinematic correlations --- + histos.fill(HIST("V0KinematicQA/AntiLambda/hYVsPt"), v0pt, v0y); + histos.fill(HIST("V0KinematicQA/AntiLambda/hPhiVsPt"), v0pt, v0phi); + } + } + + if (doCompleteTopoQA) { + // 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()); + histos.fill(HIST("V0KinematicQA/hPointingAngle"), std::acos(v0.v0cosPA())); + histos.fill(HIST("V0KinematicQA/hV0Radius"), v0.v0radius()); + histos.fill(HIST("V0KinematicQA/h2dPositiveITSvsTPCpts"), posTrackExtra.tpcNClsCrossedRows(), posTrackExtra.itsNCls()); + 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()); + histos.fill(HIST("Lambda/hPosDCAToPV"), v0.dcapostopv()); + histos.fill(HIST("Lambda/hNegDCAToPV"), v0.dcanegtopv()); + histos.fill(HIST("Lambda/hDCADaughters"), v0.dcaV0daughters()); + histos.fill(HIST("Lambda/hPointingAngle"), std::acos(v0.v0cosPA())); + histos.fill(HIST("Lambda/hV0Radius"), v0.v0radius()); + histos.fill(HIST("Lambda/h2dPositiveITSvsTPCpts"), posTrackExtra.tpcNClsCrossedRows(), posTrackExtra.itsNCls()); + histos.fill(HIST("Lambda/h2dNegativeITSvsTPCpts"), negTrackExtra.tpcNClsCrossedRows(), negTrackExtra.itsNCls()); + histos.fill(HIST("Lambda/h2dPositivePtVsPhi"), v0.positivept(), computePhiMod(v0.positivephi(), 1)); + histos.fill(HIST("Lambda/h2dNegativePtVsPhi"), v0.negativept(), computePhiMod(v0.negativephi(), -1)); + if (doTPCQA) { + histos.fill(HIST("Lambda/h3dPosNsigmaTPC"), centrality, v0pt, posTrackExtra.tpcNSigmaPr()); + histos.fill(HIST("Lambda/h3dNegNsigmaTPC"), centrality, v0pt, negTrackExtra.tpcNSigmaPi()); + histos.fill(HIST("Lambda/h3dPosTPCsignal"), centrality, v0pt, posTrackExtra.tpcSignal()); + histos.fill(HIST("Lambda/h3dNegTPCsignal"), centrality, v0pt, negTrackExtra.tpcSignal()); + histos.fill(HIST("Lambda/h3dPosNsigmaTPCvsTrackPtot"), centrality, v0.pfracpos() * v0.p(), posTrackExtra.tpcNSigmaPr()); + histos.fill(HIST("Lambda/h3dNegNsigmaTPCvsTrackPtot"), centrality, v0.pfracneg() * v0.p(), negTrackExtra.tpcNSigmaPi()); + histos.fill(HIST("Lambda/h3dPosTPCsignalVsTrackPtot"), centrality, v0.pfracpos() * v0.p(), posTrackExtra.tpcSignal()); + histos.fill(HIST("Lambda/h3dNegTPCsignalVsTrackPtot"), centrality, v0.pfracneg() * v0.p(), negTrackExtra.tpcSignal()); + histos.fill(HIST("Lambda/h3dPosNsigmaTPCvsTrackPt"), centrality, v0.positivept(), posTrackExtra.tpcNSigmaPr()); + histos.fill(HIST("Lambda/h3dNegNsigmaTPCvsTrackPt"), centrality, v0.negativept(), negTrackExtra.tpcNSigmaPi()); + histos.fill(HIST("Lambda/h3dPosTPCsignalVsTrackPt"), centrality, v0.positivept(), posTrackExtra.tpcSignal()); + histos.fill(HIST("Lambda/h3dNegTPCsignalVsTrackPt"), centrality, v0.negativept(), negTrackExtra.tpcSignal()); + } + if (doTOFQA) { + histos.fill(HIST("Lambda/h3dPosNsigmaTOF"), centrality, v0pt, v0.tofNSigmaLaPr()); + histos.fill(HIST("Lambda/h3dNegNsigmaTOF"), centrality, v0pt, v0.tofNSigmaLaPi()); + histos.fill(HIST("Lambda/h3dPosTOFdeltaT"), centrality, v0pt, v0.posTOFDeltaTLaPr()); + histos.fill(HIST("Lambda/h3dNegTOFdeltaT"), centrality, v0pt, v0.negTOFDeltaTLaPi()); + histos.fill(HIST("Lambda/h3dPosNsigmaTOFvsTrackPtot"), centrality, v0.pfracpos() * v0.p(), v0.tofNSigmaLaPr()); + histos.fill(HIST("Lambda/h3dNegNsigmaTOFvsTrackPtot"), centrality, v0.pfracneg() * v0.p(), v0.tofNSigmaLaPi()); + histos.fill(HIST("Lambda/h3dPosTOFdeltaTvsTrackPtot"), centrality, v0.pfracpos() * v0.p(), v0.posTOFDeltaTLaPr()); + histos.fill(HIST("Lambda/h3dNegTOFdeltaTvsTrackPtot"), centrality, v0.pfracneg() * v0.p(), v0.negTOFDeltaTLaPi()); + histos.fill(HIST("Lambda/h3dPosNsigmaTOFvsTrackPt"), centrality, v0.positivept(), v0.tofNSigmaLaPr()); + histos.fill(HIST("Lambda/h3dNegNsigmaTOFvsTrackPt"), centrality, v0.negativept(), v0.tofNSigmaLaPi()); + histos.fill(HIST("Lambda/h3dPosTOFdeltaTvsTrackPt"), centrality, v0.positivept(), v0.posTOFDeltaTLaPr()); + 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()); + histos.fill(HIST("AntiLambda/h3dMassAntiLambda"), centrality, v0pt, v0.mAntiLambda()); + histos.fill(HIST("AntiLambda/hPosDCAToPV"), v0.dcapostopv()); + histos.fill(HIST("AntiLambda/hNegDCAToPV"), v0.dcanegtopv()); + histos.fill(HIST("AntiLambda/hDCADaughters"), v0.dcaV0daughters()); + histos.fill(HIST("AntiLambda/hPointingAngle"), std::acos(v0.v0cosPA())); + histos.fill(HIST("AntiLambda/hV0Radius"), v0.v0radius()); + histos.fill(HIST("AntiLambda/h2dPositiveITSvsTPCpts"), posTrackExtra.tpcNClsCrossedRows(), posTrackExtra.itsNCls()); + histos.fill(HIST("AntiLambda/h2dNegativeITSvsTPCpts"), negTrackExtra.tpcNClsCrossedRows(), negTrackExtra.itsNCls()); + histos.fill(HIST("AntiLambda/h2dPositivePtVsPhi"), v0.positivept(), computePhiMod(v0.positivephi(), 1)); + histos.fill(HIST("AntiLambda/h2dNegativePtVsPhi"), v0.negativept(), computePhiMod(v0.negativephi(), -1)); + if (doTPCQA) { + histos.fill(HIST("AntiLambda/h3dPosNsigmaTPC"), centrality, v0pt, posTrackExtra.tpcNSigmaPi()); + histos.fill(HIST("AntiLambda/h3dNegNsigmaTPC"), centrality, v0pt, negTrackExtra.tpcNSigmaPr()); + histos.fill(HIST("AntiLambda/h3dPosTPCsignal"), centrality, v0pt, posTrackExtra.tpcSignal()); + histos.fill(HIST("AntiLambda/h3dNegTPCsignal"), centrality, v0pt, negTrackExtra.tpcSignal()); + histos.fill(HIST("AntiLambda/h3dPosNsigmaTPCvsTrackPtot"), centrality, v0.pfracpos() * v0.p(), posTrackExtra.tpcNSigmaPi()); + histos.fill(HIST("AntiLambda/h3dNegNsigmaTPCvsTrackPtot"), centrality, v0.pfracneg() * v0.p(), negTrackExtra.tpcNSigmaPr()); + histos.fill(HIST("AntiLambda/h3dPosTPCsignalVsTrackPtot"), centrality, v0.pfracpos() * v0.p(), posTrackExtra.tpcSignal()); + histos.fill(HIST("AntiLambda/h3dNegTPCsignalVsTrackPtot"), centrality, v0.pfracneg() * v0.p(), negTrackExtra.tpcSignal()); + histos.fill(HIST("AntiLambda/h3dPosNsigmaTPCvsTrackPt"), centrality, v0.positivept(), posTrackExtra.tpcNSigmaPi()); + histos.fill(HIST("AntiLambda/h3dNegNsigmaTPCvsTrackPt"), centrality, v0.negativept(), negTrackExtra.tpcNSigmaPr()); + histos.fill(HIST("AntiLambda/h3dPosTPCsignalVsTrackPt"), centrality, v0.positivept(), posTrackExtra.tpcSignal()); + histos.fill(HIST("AntiLambda/h3dNegTPCsignalVsTrackPt"), centrality, v0.negativept(), negTrackExtra.tpcSignal()); + } + if (doTOFQA) { + histos.fill(HIST("AntiLambda/h3dPosNsigmaTOF"), centrality, v0pt, v0.tofNSigmaALaPi()); + histos.fill(HIST("AntiLambda/h3dNegNsigmaTOF"), centrality, v0pt, v0.tofNSigmaALaPr()); + histos.fill(HIST("AntiLambda/h3dPosTOFdeltaT"), centrality, v0pt, v0.posTOFDeltaTLaPi()); + histos.fill(HIST("AntiLambda/h3dNegTOFdeltaT"), centrality, v0pt, v0.negTOFDeltaTLaPr()); + histos.fill(HIST("AntiLambda/h3dPosNsigmaTOFvsTrackPtot"), centrality, v0.pfracpos() * v0.p(), v0.tofNSigmaALaPi()); + histos.fill(HIST("AntiLambda/h3dNegNsigmaTOFvsTrackPtot"), centrality, v0.pfracneg() * v0.p(), v0.tofNSigmaALaPr()); + histos.fill(HIST("AntiLambda/h3dPosTOFdeltaTvsTrackPtot"), centrality, v0.pfracpos() * v0.p(), v0.posTOFDeltaTLaPi()); + histos.fill(HIST("AntiLambda/h3dNegTOFdeltaTvsTrackPtot"), centrality, v0.pfracneg() * v0.p(), v0.negTOFDeltaTLaPr()); + histos.fill(HIST("AntiLambda/h3dPosNsigmaTOFvsTrackPt"), centrality, v0.positivept(), v0.tofNSigmaALaPi()); + histos.fill(HIST("AntiLambda/h3dNegNsigmaTOFvsTrackPt"), centrality, v0.negativept(), v0.tofNSigmaALaPr()); + histos.fill(HIST("AntiLambda/h3dPosTOFdeltaTvsTrackPt"), centrality, v0.positivept(), v0.posTOFDeltaTLaPi()); + 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 + + // 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); + } + + PROCESS_SWITCH(lambdajetpolarizationions, processJetsData, "Process jets and produce derived data in Run 3 Data", true); + PROCESS_SWITCH(lambdajetpolarizationions, processV0sData, "Process V0s and produce derived data in Run 3 Data", true); + // PROCESS_SWITCH(lambdajetpolarizationions, processJetsMC, "Process jets and produced derived data in Run 3 MC", true); + // PROCESS_SWITCH(lambdajetpolarizationions, processV0sMC, "Process V0s and produce derived data in Run 3 MC", true); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{ adaptAnalysisTask(cfgc)}; -} \ No newline at end of file +} + \ No newline at end of file diff --git a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx index 5fe9ed3cc2e..3bff75a5a54 100644 --- a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx @@ -17,13 +17,13 @@ // Jet Polarization Ions task -- Derived data // ================ // -// This code loops over custom derived data tables defined on +// This code loops over custom derived data tables defined on // lambdaJetPolarizationIons.h (JetsRing, LambdaLikeV0sRing). // From this derived data, calculates polarization on an EbE // basis (see TProfiles). // Signal extraction is done out of the framework, based on // the AnalysisResults of this code. -// +// // // Comments, questions, complaints, suggestions? // Please write to: @@ -56,701 +56,714 @@ using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; -using ROOT::Math::XYZVector; using ROOT::Math::PtEtaPhiMVector; +using ROOT::Math::XYZVector; // using namespace o2::aod::lambdajetpol; // Used it explicitly along the code for clarity - // Declaring constants: +// 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 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, - kCentFT0M, - kCentFV0A + kCentFT0C = 0, + kCentFT0M, + kCentFV0A }; // Helper macro to avoid writing the histogram fills 4 times for about 20 histograms: -#define RING_OBSERVABLE_FILL_LIST(X, FOLDER) \ - /* Counters */ \ - X(FOLDER "/QA/hDeltaPhi", deltaPhiJet) \ - X(FOLDER "/QA/hDeltaTheta", deltaThetaJet) \ - X(FOLDER "/QA/hIntegrated", 0.) \ - /* Lambda pT variation -- Youpeng's proposal */ \ - X(FOLDER "/QA/hLambdaPt", v0pt) \ - /* Counters */ \ - X(FOLDER "/QA/h2dDeltaPhiVsLambdaPt", deltaPhiJet, v0pt) \ - X(FOLDER "/QA/h2dDeltaThetaVsLambdaPt", deltaThetaJet, v0pt) \ - /* Additional plots for instant gratification - 1D Profiles */ \ - X(FOLDER "/pRingObservableDeltaPhi", deltaPhiJet, ringObservable) \ - X(FOLDER "/pRingObservableDeltaTheta", deltaThetaJet, ringObservable) \ - X(FOLDER "/pRingObservableIntegrated", 0., ringObservable) \ - X(FOLDER "/pRingObservableLambdaPt", v0pt, ringObservable) \ - /* 2D Profiles */ \ - X(FOLDER "/p2dRingObservableDeltaPhiVsLambdaPt", deltaPhiJet, v0pt, ringObservable) \ - X(FOLDER "/p2dRingObservableDeltaThetaVsLambdaPt", deltaThetaJet, v0pt, ringObservable) \ - X(FOLDER "/p2dRingObservableDeltaPhiVsLeadJetPt", deltaPhiJet, leadingJetPt, ringObservable) \ - X(FOLDER "/p2dRingObservableDeltaThetaVsLeadJetPt", deltaThetaJet, leadingJetPt, ringObservable) \ - /* 1D Mass */ \ - X(FOLDER "/QA/hMass", v0LambdaLikeMass) \ - X(FOLDER "/QA/hRingObservableNumMass", v0LambdaLikeMass, ringObservable) \ - X(FOLDER "/hMassSigExtract", v0LambdaLikeMass) \ - /* Counters */ \ - X(FOLDER "/QA/h2dDeltaPhiVsMass", deltaPhiJet, v0LambdaLikeMass) \ - X(FOLDER "/QA/h2dDeltaThetaVsMass", deltaThetaJet, v0LambdaLikeMass) \ - X(FOLDER "/QA/h3dDeltaPhiVsMassVsLambdaPt", deltaPhiJet, v0LambdaLikeMass, v0pt) \ - X(FOLDER "/QA/h3dDeltaThetaVsMassVsLambdaPt", deltaThetaJet, v0LambdaLikeMass, v0pt) \ - X(FOLDER "/QA/h3dDeltaPhiVsMassVsLeadJetPt", deltaPhiJet, v0LambdaLikeMass, leadingJetPt) \ - X(FOLDER "/QA/h3dDeltaThetaVsMassVsLeadJetPt", deltaThetaJet, v0LambdaLikeMass, leadingJetPt) \ - X(FOLDER "/QA/h3dDeltaPhiVsMassVsCent", deltaPhiJet, v0LambdaLikeMass, centrality) \ - X(FOLDER "/QA/h3dDeltaThetaVsMassVsCent", deltaThetaJet, v0LambdaLikeMass, centrality) \ - /* TProfile of Ring vs Mass */ \ - X(FOLDER "/pRingObservableMass", v0LambdaLikeMass, ringObservable) \ - /* TProfile of Ring vs Mass -- Leading Particle and 2nd-to-leading jet - QA */ \ - X(FOLDER "/pRingObservableLeadPMass", v0LambdaLikeMass, ringObservableLeadP) \ - X(FOLDER "/pRingObservable2ndJetMass", v0LambdaLikeMass, ringObservable2ndJet) \ - /* 2D Profiles: Angle vs Mass */ \ - X(FOLDER "/p2dRingObservableDeltaPhiVsMass", deltaPhiJet, v0LambdaLikeMass, ringObservable) \ - X(FOLDER "/p2dRingObservableDeltaThetaVsMass", deltaThetaJet, v0LambdaLikeMass, ringObservable) \ - /* 3D Profiles: Angle vs Mass vs Lambda pT */ \ - X(FOLDER "/p3dRingObservableDeltaPhiVsMassVsLambdaPt", deltaPhiJet, v0LambdaLikeMass, v0pt, ringObservable) \ - X(FOLDER "/p3dRingObservableDeltaThetaVsMassVsLambdaPt", deltaThetaJet, v0LambdaLikeMass, v0pt, ringObservable) \ - /* 3D Profiles: Angle vs Mass vs Lead Jet pT */ \ - X(FOLDER "/p3dRingObservableDeltaPhiVsMassVsLeadJetPt", deltaPhiJet, v0LambdaLikeMass, leadingJetPt, ringObservable) \ - X(FOLDER "/p3dRingObservableDeltaThetaVsMassVsLeadJetPt",deltaThetaJet, v0LambdaLikeMass, leadingJetPt, ringObservable) \ - /* 2D Profile: Mass vs Centrality */ \ - X(FOLDER "/p2dRingObservableMassVsCent", v0LambdaLikeMass, centrality, ringObservable) \ - /* 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) - // (TODO: add counters for regular TH2Ds about centrality) +#define RING_OBSERVABLE_FILL_LIST(X, FOLDER) \ + /* Counters */ \ + X(FOLDER "/QA/hDeltaPhi", deltaPhiJet) \ + X(FOLDER "/QA/hDeltaTheta", deltaThetaJet) \ + X(FOLDER "/QA/hIntegrated", 0.) \ + /* Lambda pT variation -- Youpeng's proposal */ \ + X(FOLDER "/QA/hLambdaPt", v0pt) \ + /* Counters */ \ + X(FOLDER "/QA/h2dDeltaPhiVsLambdaPt", deltaPhiJet, v0pt) \ + X(FOLDER "/QA/h2dDeltaThetaVsLambdaPt", deltaThetaJet, v0pt) \ + /* Additional plots for instant gratification - 1D Profiles */ \ + X(FOLDER "/pRingObservableDeltaPhi", deltaPhiJet, ringObservable) \ + X(FOLDER "/pRingObservableDeltaTheta", deltaThetaJet, ringObservable) \ + X(FOLDER "/pRingObservableIntegrated", 0., ringObservable) \ + X(FOLDER "/pRingObservableLambdaPt", v0pt, ringObservable) \ + /* 2D Profiles */ \ + X(FOLDER "/p2dRingObservableDeltaPhiVsLambdaPt", deltaPhiJet, v0pt, ringObservable) \ + X(FOLDER "/p2dRingObservableDeltaThetaVsLambdaPt", deltaThetaJet, v0pt, ringObservable) \ + X(FOLDER "/p2dRingObservableDeltaPhiVsLeadJetPt", deltaPhiJet, leadingJetPt, ringObservable) \ + X(FOLDER "/p2dRingObservableDeltaThetaVsLeadJetPt", deltaThetaJet, leadingJetPt, ringObservable) \ + /* 1D Mass */ \ + X(FOLDER "/QA/hMass", v0LambdaLikeMass) \ + X(FOLDER "/QA/hRingObservableNumMass", v0LambdaLikeMass, ringObservable) \ + X(FOLDER "/hMassSigExtract", v0LambdaLikeMass) \ + /* Counters */ \ + X(FOLDER "/QA/h2dDeltaPhiVsMass", deltaPhiJet, v0LambdaLikeMass) \ + X(FOLDER "/QA/h2dDeltaThetaVsMass", deltaThetaJet, v0LambdaLikeMass) \ + X(FOLDER "/QA/h3dDeltaPhiVsMassVsLambdaPt", deltaPhiJet, v0LambdaLikeMass, v0pt) \ + X(FOLDER "/QA/h3dDeltaThetaVsMassVsLambdaPt", deltaThetaJet, v0LambdaLikeMass, v0pt) \ + X(FOLDER "/QA/h3dDeltaPhiVsMassVsLeadJetPt", deltaPhiJet, v0LambdaLikeMass, leadingJetPt) \ + X(FOLDER "/QA/h3dDeltaThetaVsMassVsLeadJetPt", deltaThetaJet, v0LambdaLikeMass, leadingJetPt) \ + X(FOLDER "/QA/h3dDeltaPhiVsMassVsCent", deltaPhiJet, v0LambdaLikeMass, centrality) \ + X(FOLDER "/QA/h3dDeltaThetaVsMassVsCent", deltaThetaJet, v0LambdaLikeMass, centrality) \ + /* TProfile of Ring vs Mass */ \ + X(FOLDER "/pRingObservableMass", v0LambdaLikeMass, ringObservable) \ + /* TProfile of Ring vs Mass -- Leading Particle and 2nd-to-leading jet - QA */ \ + X(FOLDER "/pRingObservableLeadPMass", v0LambdaLikeMass, ringObservableLeadP) \ + X(FOLDER "/pRingObservable2ndJetMass", v0LambdaLikeMass, ringObservable2ndJet) \ + /* 2D Profiles: Angle vs Mass */ \ + X(FOLDER "/p2dRingObservableDeltaPhiVsMass", deltaPhiJet, v0LambdaLikeMass, ringObservable) \ + X(FOLDER "/p2dRingObservableDeltaThetaVsMass", deltaThetaJet, v0LambdaLikeMass, ringObservable) \ + /* 3D Profiles: Angle vs Mass vs Lambda pT */ \ + X(FOLDER "/p3dRingObservableDeltaPhiVsMassVsLambdaPt", deltaPhiJet, v0LambdaLikeMass, v0pt, ringObservable) \ + X(FOLDER "/p3dRingObservableDeltaThetaVsMassVsLambdaPt", deltaThetaJet, v0LambdaLikeMass, v0pt, ringObservable) \ + /* 3D Profiles: Angle vs Mass vs Lead Jet pT */ \ + X(FOLDER "/p3dRingObservableDeltaPhiVsMassVsLeadJetPt", deltaPhiJet, v0LambdaLikeMass, leadingJetPt, ringObservable) \ + X(FOLDER "/p3dRingObservableDeltaThetaVsMassVsLeadJetPt", deltaThetaJet, v0LambdaLikeMass, leadingJetPt, ringObservable) \ + /* 2D Profile: Mass vs Centrality */ \ + X(FOLDER "/p2dRingObservableMassVsCent", v0LambdaLikeMass, centrality, ringObservable) \ + /* 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) +// (TODO: add counters for regular TH2Ds about centrality) // For leading particle -#define RING_OBSERVABLE_LEADP_FILL_LIST(X, FOLDER) \ - X(FOLDER "/pRingObservableLeadPDeltaPhi", deltaPhiLeadP, ringObservableLeadP) \ - X(FOLDER "/pRingObservableLeadPDeltaTheta", deltaThetaLeadP, ringObservableLeadP) \ - X(FOLDER "/pRingObservableLeadPIntegrated", 0., ringObservableLeadP) \ - X(FOLDER "/pRingObservableLeadPLambdaPt", v0pt, ringObservableLeadP) +#define RING_OBSERVABLE_LEADP_FILL_LIST(X, FOLDER) \ + X(FOLDER "/pRingObservableLeadPDeltaPhi", deltaPhiLeadP, ringObservableLeadP) \ + X(FOLDER "/pRingObservableLeadPDeltaTheta", deltaThetaLeadP, ringObservableLeadP) \ + X(FOLDER "/pRingObservableLeadPIntegrated", 0., ringObservableLeadP) \ + X(FOLDER "/pRingObservableLeadPLambdaPt", v0pt, ringObservableLeadP) // For subleading jet: -#define RING_OBSERVABLE_2NDJET_FILL_LIST(X, FOLDER) \ - X(FOLDER "/pRingObservable2ndJetDeltaPhi", deltaPhi2ndJet, ringObservable2ndJet) \ - X(FOLDER "/pRingObservable2ndJetDeltaTheta", deltaTheta2ndJet, ringObservable2ndJet) \ - X(FOLDER "/pRingObservable2ndJetIntegrated", 0., ringObservable2ndJet) \ - X(FOLDER "/pRingObservable2ndJetLambdaPt", v0pt, ringObservable2ndJet) - - -#define POLARIZATION_PROFILE_FILL_LIST(X, FOLDER) \ - /* =============================== */ \ - /* 1D TProfiles vs v0phi */ \ - /* =============================== */ \ - 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) \ - /* =============================== */ \ - /* 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) - -// Apply the macros (notice I had to include the semicolon (";") after the function, so you don't need to +#define RING_OBSERVABLE_2NDJET_FILL_LIST(X, FOLDER) \ + X(FOLDER "/pRingObservable2ndJetDeltaPhi", deltaPhi2ndJet, ringObservable2ndJet) \ + X(FOLDER "/pRingObservable2ndJetDeltaTheta", deltaTheta2ndJet, ringObservable2ndJet) \ + X(FOLDER "/pRingObservable2ndJetIntegrated", 0., ringObservable2ndJet) \ + X(FOLDER "/pRingObservable2ndJetLambdaPt", v0pt, ringObservable2ndJet) + +#define POLARIZATION_PROFILE_FILL_LIST(X, FOLDER) \ + /* =============================== */ \ + /* 1D TProfiles vs v0phi */ \ + /* =============================== */ \ + 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) \ + /* =============================== */ \ + /* 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) + +// 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 // would not know to end each statement with a semicolon): #define APPLY_HISTO_FILL(NAME, ...) histos.fill(HIST(NAME), __VA_ARGS__); - struct lambdajetpolarizationionsderived { - // Define histogram registries: - HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - - // Master analysis switches - Configurable analyseLambda{"analyseLambda", true, "process Lambda-like candidates"}; - Configurable analyseAntiLambda{"analyseAntiLambda", false, "process AntiLambda-like candidates"}; - 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 - - // 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 jetRForSmuding{"jetRForSmuding", 0.4, "QA quantity: the chosen R scale for the jet direction smudge"}; - - ///////////////////////// - // Configurable blocks: - // Histogram axes configuration: - 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 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 axisDeltaTheta{"axisDeltaTheta", {40, 0, constants::math::PI}, "#Delta #theta_{jet}"}; - ConfigurableAxis axisDeltaPhi{"axisDeltaPhi", {40, -constants::math::PI, constants::math::PI}, "#Delta #phi_{jet}"}; - - // 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. - // A coarser axis (sigma is still well estimated, with about 8 bins in the peak region) - 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 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)"}; - - // (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"}; - } axisConfigurations; - - - // Helper functions: - // Fast wrapping into [-PI, PI) (restricted to this interval for function speed) - inline double wrapToPiFast(double phi){ - constexpr double TwoPi = o2::constants::math::TwoPI; - constexpr double Pi = o2::constants::math::PI; - if (phi >= Pi) phi -= TwoPi; - else if (phi < -Pi) phi += TwoPi; - return phi; - } - - - void init(InitContext const&){ - // Ring observable histograms: - // Helper to register one full histogram family (kinematic cut variation of ring observable) - auto addRingObservableFamily = [&](const std::string& folder){ - // =============================== - // 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/hIntegrated").c_str(), "hIntegrated", kTH1D, {{1, -0.5, 0.5}}); - // =============================== - // 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}); - // =============================== - // 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 - // =============================== - // 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}); - // =============================== - // 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}); - - // 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 - // -- If any efficiency corrections arise, you can fill with the kTH1D as (deltaPhiJet, ringObservable, weight) - // instead of the simple (deltaPhiJet, ringObservable) --> Notice TProfile knows how to accept 3 entries - // for a TH1D-like object! - // -- CAUTION! The TProfile does not utilize unbiased variance estimators with N-1 instead of N in the denominator, - // so you might get biased errors when counts are too low in higher-dimensional profiles (i.e., kTProfile2Ds) - // =============================== - // 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}); - // 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}); - // 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}); - // =============================== - // 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}); - // =============================== - // 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}); - - // =============================== - // 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}); - // 1D Mass dependence of observable numerator: - histos.add((folder + "/QA/hRingObservableNumMass").c_str(), "hRingObservableNumMass", 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}); - // --- 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}); - // --- 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}); - - // =============================== - // 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}); - // TProfile2D: vs Mass (DeltaPhi) - 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}); - // --- 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 --- - histos.add((folder + "/p3dRingObservableDeltaThetaVsMassVsLambdaPt").c_str(), "p3dRingObservableDeltaThetaVsMassVsLambdaPt;#Delta#theta;m_{p#pi};p_{T}^{#Lambda};<#it{R}>", 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}); - // --- 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}); - - // =============================== - // 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}); - // Useful TProfiles: - // --- TProfile1D: Integrated vs Centrality: - histos.add((folder + "/pRingIntVsCentrality").c_str(), "pRingIntVsCentrality; 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 --- - histos.add((folder + "/p3dRingObservableDeltaPhiVsMassVsCent").c_str(), "p3dRingObservableDeltaPhiVsMassVsCent;#Delta#varphi;m_{p#pi};Centrality;<#it{R}>", 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}); - - // =============================== - // QA histograms - Useful numbers - // =============================== - // (TODO: implement these!) - // (TODO: implement momentum imbalance checks for jets!) - // 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 - }; - // Execute local lambda to register histogram families: - addRingObservableFamily("Ring"); - addRingObservableFamily("RingKinematicCuts"); - 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"); - - // 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"); - } - - // Helper to get centrality (same from TableProducer, thanks to templating!): - template - auto getCentrality(TCollision const& collision) - { - if (centralityEstimator == kCentFT0M) return collision.centFT0M(); - else if (centralityEstimator == kCentFT0C) return collision.centFT0C(); - else if (centralityEstimator == kCentFV0A) return collision.centFV0A(); - 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 - - // Preslices for correct collisions association: - // (TODO: test using custom grouping) - Preslice perColJets = o2::aod::lambdajetpol::collisionId; - Preslice perColV0s = o2::aod::lambdajetpol::collisionId; - Preslice perColLeadPs = o2::aod::lambdajetpol::collisionId; - void processPolarizationData(o2::aod::RingCollisions const& collisions, o2::aod::RingJets const& jets, o2::aod::RingLaV0s const& v0s, - o2::aod::RingLeadP const& leadPs){ - for (auto const& collision : collisions) { - const auto collId = collision.collisionId(); - const double centrality = getCentrality(collision); - - // 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: - if (!v0sInColl.size()) continue; - - // 2) We require at least a leading particle, then we get the leading jet only if it exists: - // (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) - 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(); - break; - } - // Discard events with no leading particle (FastJet didn't even run in these cases!): - if (leadPPt < 0.) continue; - - // Build leading particle unit vector, outside the V0 loop for performance: - XYZVector leadPUnitVec = XYZVector(leadPPx, leadPPy, leadPPz).Unit(); - - // 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; - } - } - - // Some useful bools to check if we have a leading jet and a subleading jet: - const bool hasValidLeadingJet = leadingJetPt > 0.; - const bool hasValidSubJet = subleadingJetPt > 0.; - - // 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 = leadingJet->jetEta(); - 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 displacemente in \theta becomes a large displacement in \eta space - double smearSigma = 0.05 * jetRForSmuding; - 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)) - // 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. - } - } - - float subleadingJetEta = 0.; - float subleadingJetPhi = 0.; - XYZVector subJetUnitVec(1., 0., 0.); - if (hasValidSubJet) { - subleadingJetEta = subleadingJet->jetEta(); - subleadingJetPhi = subleadingJet->jetPhi(); - // Using internal getters to make code cleaner: - subJetUnitVec = XYZVector(subleadingJet->jetPx(), subleadingJet->jetPy(), subleadingJet->jetPz()).Unit(); - } - - // (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; - - 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 - // 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 float v0pt = v0.v0Pt(); - const float v0eta = v0.v0Eta(); - const float v0phi = v0.v0Phi(); - - float v0LambdaLikeMass = 0; // Initialized just to catch any stray behavior - float protonLikePt = 0; - float protonLikeEta = 0; - float protonLikePhi = 0; - if (isLambda){ - if (!analyseLambda) continue; - v0LambdaLikeMass = v0.massLambda(); - protonLikePt = v0.posPt(); - protonLikeEta = v0.posEta(); - protonLikePhi = v0.posPhi(); - } - else if (isAntiLambda){ // (TODO: add a split histogram where you consider Lambda and AntiLambda polarization separately?) - if (!analyseAntiLambda) continue; - v0LambdaLikeMass = v0.massAntiLambda(); - protonLikePt = v0.negPt(); - protonLikeEta = v0.negEta(); - protonLikePhi = v0.negPhi(); - } - - PtEtaPhiMVector lambdaLike4Vec(v0pt, v0eta, v0phi, v0LambdaLikeMass); - PtEtaPhiMVector protonLike4Vec(protonLikePt, protonLikeEta, protonLikePhi, protonMass); - 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 - auto protonLike4VecStar = ROOT::Math::VectorUtil::boost(protonLike4Vec, beta); - - // Getting unit vectors and 3-components: - XYZVector lambdaLike3Vec = lambdaLike4Vec.Vect(); - XYZVector protonLikeStarUnit3Vec = protonLike4VecStar.Vect().Unit(); - - //////////////////////////////////////////// - // Ring observable: Leading particle proxy - // Always computed -- leading particle existence is guaranteed by the second check above - //////////////////////////////////////////// - // 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 - - ////////////////////////////////////////// - // Ring observable: Leading jet proxy - // Only computed when a leading jet exists in this collision. - ////////////////////////////////////////// - float ringObservable = 0.; - float deltaPhiJet = 0.; - float deltaThetaJet = 0.; - if (hasValidLeadingJet) { - // Cross product - XYZVector cross = leadingJetUnitVec.Cross(lambdaLike3Vec); - ringObservable = protonLikeStarUnit3Vec.Dot(cross) / cross.R(); - // Adding prefactor - if (!forcePolSignQA) ringObservable *= (isLambda) ? polPrefactorLambda : polPrefactorAntiLambda; - else ringObservable *= (isLambda) ? polPrefactorLambda : -1.0 * polPrefactorAntiLambda; - // Angular variables - deltaPhiJet = wrapToPiFast(v0phi - leadingJetPhi); - deltaThetaJet = ROOT::Math::VectorUtil::Angle(leadingJetUnitVec, lambdaLike3Vec); - } - - ////////////////////////////////////////// - // Ring observable: Subleading jet proxy - // Only computed when a subleading jet exists in this collision. - ////////////////////////////////////////// - float ringObservable2ndJet = 0.; - float deltaPhi2ndJet = 0.; - float deltaTheta2ndJet = 0.; - if (hasValidSubJet) { - XYZVector cross2ndJet = subJetUnitVec.Cross(lambdaLike3Vec); - ringObservable2ndJet = protonLikeStarUnit3Vec.Dot(cross2ndJet) / cross2ndJet.R(); - // Adding prefactor - if (!forcePolSignQA) ringObservable2ndJet *= (isLambda) ? polPrefactorLambda : polPrefactorAntiLambda; - else ringObservable2ndJet *= (isLambda) ? polPrefactorLambda : -1.0 * polPrefactorAntiLambda; - // Angular variables - deltaPhi2ndJet = wrapToPiFast(v0phi - subleadingJetPhi); - deltaTheta2ndJet = ROOT::Math::VectorUtil::Angle(subJetUnitVec, lambdaLike3Vec); - } - - // 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 if (isAntiLambda){ - PolStarX = polPrefactorAntiLambda * protonLikeStarUnit3Vec.X(); - PolStarY = polPrefactorAntiLambda * protonLikeStarUnit3Vec.Y(); - PolStarZ = polPrefactorAntiLambda * protonLikeStarUnit3Vec.Z(); - } - - 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 - POLARIZATION_PROFILE_FILL_LIST(APPLY_HISTO_FILL, "Ring") - - if (hasValidLeadingJet) { - RING_OBSERVABLE_FILL_LIST(APPLY_HISTO_FILL, "Ring") - histos.fill(HIST("pRingCuts"), 0, ringObservable); - } - if (hasValidSubJet) { - RING_OBSERVABLE_2NDJET_FILL_LIST(APPLY_HISTO_FILL, "Ring") - histos.fill(HIST("pRingCutsSubLeadingJet"), 0, ringObservable2ndJet); - } - - // 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); - POLARIZATION_PROFILE_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts") - if (hasValidLeadingJet) { - RING_OBSERVABLE_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts") - histos.fill(HIST("pRingCuts"), 1, ringObservable); - } - if (hasValidSubJet) { - RING_OBSERVABLE_2NDJET_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts") - histos.fill(HIST("pRingCutsSubLeadingJet"), 1, ringObservable2ndJet); - } - } - - // 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") - histos.fill(HIST("pRingCuts"), 2, ringObservable); - POLARIZATION_PROFILE_FILL_LIST(APPLY_HISTO_FILL, "JetKinematicCuts") - } - - // 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); - POLARIZATION_PROFILE_FILL_LIST(APPLY_HISTO_FILL, "JetAndLambdaKinematicCuts") - } - - // Same variations for the leading particle and for the subleading jet: - if (kinematicLeadPCheck){ - RING_OBSERVABLE_LEADP_FILL_LIST(APPLY_HISTO_FILL, "JetKinematicCuts") - histos.fill(HIST("pRingCutsLeadingP"), 2, ringObservableLeadP); - } - if (kinematic2ndJetCheck){ - RING_OBSERVABLE_2NDJET_FILL_LIST(APPLY_HISTO_FILL, "JetKinematicCuts") - histos.fill(HIST("pRingCutsSubLeadingJet"), 2, ringObservable2ndJet); - } - if (kinematicLambdaCheck && kinematicLeadPCheck){ - RING_OBSERVABLE_LEADP_FILL_LIST(APPLY_HISTO_FILL, "JetAndLambdaKinematicCuts") - histos.fill(HIST("pRingCutsLeadingP"), 3, ringObservableLeadP); - } - if (kinematicLambdaCheck && kinematic2ndJetCheck){ - RING_OBSERVABLE_2NDJET_FILL_LIST(APPLY_HISTO_FILL, "JetAndLambdaKinematicCuts") - histos.fill(HIST("pRingCutsSubLeadingJet"), 3, ringObservable2ndJet); - } - } // end v0s loop - } // end collisions - } - - PROCESS_SWITCH(lambdajetpolarizationionsderived, processPolarizationData, "Process derived data in Run 3 Data", true); + // Define histogram registries: + HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + // Master analysis switches + Configurable analyseLambda{"analyseLambda", true, "process Lambda-like candidates"}; + Configurable analyseAntiLambda{"analyseAntiLambda", false, "process AntiLambda-like candidates"}; + 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 + + // 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 jetRForSmuding{"jetRForSmuding", 0.4, "QA quantity: the chosen R scale for the jet direction smudge"}; + + ///////////////////////// + // Configurable blocks: + // Histogram axes configuration: + 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 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 axisDeltaTheta{"axisDeltaTheta", {40, 0, constants::math::PI}, "#Delta #theta_{jet}"}; + ConfigurableAxis axisDeltaPhi{"axisDeltaPhi", {40, -constants::math::PI, constants::math::PI}, "#Delta #phi_{jet}"}; + + // 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. + // A coarser axis (sigma is still well estimated, with about 8 bins in the peak region) + 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 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)"}; + + // (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"}; + } axisConfigurations; + + // Helper functions: + // Fast wrapping into [-PI, PI) (restricted to this interval for function speed) + inline double wrapToPiFast(double phi) + { + constexpr double TwoPi = o2::constants::math::TwoPI; + constexpr double Pi = o2::constants::math::PI; + if (phi >= Pi) + phi -= TwoPi; + else if (phi < -Pi) + phi += TwoPi; + return phi; + } + + void init(InitContext const&) + { + // Ring observable histograms: + // Helper to register one full histogram family (kinematic cut variation of ring observable) + auto addRingObservableFamily = [&](const std::string& folder) { + // =============================== + // 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/hIntegrated").c_str(), "hIntegrated", kTH1D, {{1, -0.5, 0.5}}); + // =============================== + // 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}); + // =============================== + // 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 + // =============================== + // 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}); + // =============================== + // 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}); + + // 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 + // -- If any efficiency corrections arise, you can fill with the kTH1D as (deltaPhiJet, ringObservable, weight) + // instead of the simple (deltaPhiJet, ringObservable) --> Notice TProfile knows how to accept 3 entries + // for a TH1D-like object! + // -- CAUTION! The TProfile does not utilize unbiased variance estimators with N-1 instead of N in the denominator, + // so you might get biased errors when counts are too low in higher-dimensional profiles (i.e., kTProfile2Ds) + // =============================== + // 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}); + // 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}); + // 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}); + // =============================== + // 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}); + // =============================== + // 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}); + + // =============================== + // 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}); + // 1D Mass dependence of observable numerator: + histos.add((folder + "/QA/hRingObservableNumMass").c_str(), "hRingObservableNumMass", 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}); + // --- 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}); + // --- 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}); + + // =============================== + // 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}); + // TProfile2D: vs Mass (DeltaPhi) + 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}); + // --- 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 --- + histos.add((folder + "/p3dRingObservableDeltaThetaVsMassVsLambdaPt").c_str(), "p3dRingObservableDeltaThetaVsMassVsLambdaPt;#Delta#theta;m_{p#pi};p_{T}^{#Lambda};<#it{R}>", 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}); + // --- 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}); + + // =============================== + // 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}); + // Useful TProfiles: + // --- TProfile1D: Integrated vs Centrality: + histos.add((folder + "/pRingIntVsCentrality").c_str(), "pRingIntVsCentrality; 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 --- + histos.add((folder + "/p3dRingObservableDeltaPhiVsMassVsCent").c_str(), "p3dRingObservableDeltaPhiVsMassVsCent;#Delta#varphi;m_{p#pi};Centrality;<#it{R}>", 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}); + + // =============================== + // QA histograms - Useful numbers + // =============================== + // (TODO: implement these!) + // (TODO: implement momentum imbalance checks for jets!) + // 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 + }; + // Execute local lambda to register histogram families: + addRingObservableFamily("Ring"); + addRingObservableFamily("RingKinematicCuts"); + 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"); + + // 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"); + } + + // Helper to get centrality (same from TableProducer, thanks to templating!): + template + auto getCentrality(TCollision const& collision) + { + if (centralityEstimator == kCentFT0M) + return collision.centFT0M(); + else if (centralityEstimator == kCentFT0C) + return collision.centFT0C(); + else if (centralityEstimator == kCentFV0A) + return collision.centFV0A(); + 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 + + // Preslices for correct collisions association: + // (TODO: test using custom grouping) + Preslice perColJets = o2::aod::lambdajetpol::collisionId; + Preslice perColV0s = o2::aod::lambdajetpol::collisionId; + Preslice perColLeadPs = o2::aod::lambdajetpol::collisionId; + void processPolarizationData(o2::aod::RingCollisions const& collisions, o2::aod::RingJets const& jets, o2::aod::RingLaV0s const& v0s, + o2::aod::RingLeadP const& leadPs) + { + for (auto const& collision : collisions) { + const auto collId = collision.collisionId(); + const double centrality = getCentrality(collision); + + // 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: + if (!v0sInColl.size()) + continue; + + // 2) We require at least a leading particle, then we get the leading jet only if it exists: + // (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) + 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(); + break; + } + // Discard events with no leading particle (FastJet didn't even run in these cases!): + if (leadPPt < 0.) + continue; + + // Build leading particle unit vector, outside the V0 loop for performance: + XYZVector leadPUnitVec = XYZVector(leadPPx, leadPPy, leadPPz).Unit(); + + // 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; + } + } + + // Some useful bools to check if we have a leading jet and a subleading jet: + const bool hasValidLeadingJet = leadingJetPt > 0.; + const bool hasValidSubJet = subleadingJetPt > 0.; + + // 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 = leadingJet->jetEta(); + 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 displacemente in \theta becomes a large displacement in \eta space + double smearSigma = 0.05 * jetRForSmuding; + 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)) + // 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. + } + } + + float subleadingJetEta = 0.; + float subleadingJetPhi = 0.; + XYZVector subJetUnitVec(1., 0., 0.); + if (hasValidSubJet) { + subleadingJetEta = subleadingJet->jetEta(); + subleadingJetPhi = subleadingJet->jetPhi(); + // Using internal getters to make code cleaner: + subJetUnitVec = XYZVector(subleadingJet->jetPx(), subleadingJet->jetPy(), subleadingJet->jetPz()).Unit(); + } + + // (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; + + 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 + // 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 float v0pt = v0.v0Pt(); + const float v0eta = v0.v0Eta(); + const float v0phi = v0.v0Phi(); + + float v0LambdaLikeMass = 0; // Initialized just to catch any stray behavior + float protonLikePt = 0; + float protonLikeEta = 0; + float protonLikePhi = 0; + if (isLambda) { + if (!analyseLambda) + continue; + v0LambdaLikeMass = v0.massLambda(); + protonLikePt = v0.posPt(); + protonLikeEta = v0.posEta(); + protonLikePhi = v0.posPhi(); + } else if (isAntiLambda) { // (TODO: add a split histogram where you consider Lambda and AntiLambda polarization separately?) + if (!analyseAntiLambda) + continue; + v0LambdaLikeMass = v0.massAntiLambda(); + protonLikePt = v0.negPt(); + protonLikeEta = v0.negEta(); + protonLikePhi = v0.negPhi(); + } + + PtEtaPhiMVector lambdaLike4Vec(v0pt, v0eta, v0phi, v0LambdaLikeMass); + PtEtaPhiMVector protonLike4Vec(protonLikePt, protonLikeEta, protonLikePhi, protonMass); + 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 + auto protonLike4VecStar = ROOT::Math::VectorUtil::boost(protonLike4Vec, beta); + + // Getting unit vectors and 3-components: + XYZVector lambdaLike3Vec = lambdaLike4Vec.Vect(); + XYZVector protonLikeStarUnit3Vec = protonLike4VecStar.Vect().Unit(); + + //////////////////////////////////////////// + // Ring observable: Leading particle proxy + // Always computed -- leading particle existence is guaranteed by the second check above + //////////////////////////////////////////// + // 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 + + ////////////////////////////////////////// + // Ring observable: Leading jet proxy + // Only computed when a leading jet exists in this collision. + ////////////////////////////////////////// + float ringObservable = 0.; + float deltaPhiJet = 0.; + float deltaThetaJet = 0.; + if (hasValidLeadingJet) { + // Cross product + XYZVector cross = leadingJetUnitVec.Cross(lambdaLike3Vec); + ringObservable = protonLikeStarUnit3Vec.Dot(cross) / cross.R(); + // Adding prefactor + if (!forcePolSignQA) + ringObservable *= (isLambda) ? polPrefactorLambda : polPrefactorAntiLambda; + else + ringObservable *= (isLambda) ? polPrefactorLambda : -1.0 * polPrefactorAntiLambda; + // Angular variables + deltaPhiJet = wrapToPiFast(v0phi - leadingJetPhi); + deltaThetaJet = ROOT::Math::VectorUtil::Angle(leadingJetUnitVec, lambdaLike3Vec); + } + + ////////////////////////////////////////// + // Ring observable: Subleading jet proxy + // Only computed when a subleading jet exists in this collision. + ////////////////////////////////////////// + float ringObservable2ndJet = 0.; + float deltaPhi2ndJet = 0.; + float deltaTheta2ndJet = 0.; + if (hasValidSubJet) { + XYZVector cross2ndJet = subJetUnitVec.Cross(lambdaLike3Vec); + ringObservable2ndJet = protonLikeStarUnit3Vec.Dot(cross2ndJet) / cross2ndJet.R(); + // Adding prefactor + if (!forcePolSignQA) + ringObservable2ndJet *= (isLambda) ? polPrefactorLambda : polPrefactorAntiLambda; + else + ringObservable2ndJet *= (isLambda) ? polPrefactorLambda : -1.0 * polPrefactorAntiLambda; + // Angular variables + deltaPhi2ndJet = wrapToPiFast(v0phi - subleadingJetPhi); + deltaTheta2ndJet = ROOT::Math::VectorUtil::Angle(subJetUnitVec, lambdaLike3Vec); + } + + // 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 if (isAntiLambda) { + PolStarX = polPrefactorAntiLambda * protonLikeStarUnit3Vec.X(); + PolStarY = polPrefactorAntiLambda * protonLikeStarUnit3Vec.Y(); + PolStarZ = polPrefactorAntiLambda * protonLikeStarUnit3Vec.Z(); + } + + 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 + POLARIZATION_PROFILE_FILL_LIST(APPLY_HISTO_FILL, "Ring") + + if (hasValidLeadingJet) { + RING_OBSERVABLE_FILL_LIST(APPLY_HISTO_FILL, "Ring") + histos.fill(HIST("pRingCuts"), 0, ringObservable); + } + if (hasValidSubJet) { + RING_OBSERVABLE_2NDJET_FILL_LIST(APPLY_HISTO_FILL, "Ring") + histos.fill(HIST("pRingCutsSubLeadingJet"), 0, ringObservable2ndJet); + } + + // 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); + POLARIZATION_PROFILE_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts") + if (hasValidLeadingJet) { + RING_OBSERVABLE_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts") + histos.fill(HIST("pRingCuts"), 1, ringObservable); + } + if (hasValidSubJet) { + RING_OBSERVABLE_2NDJET_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts") + histos.fill(HIST("pRingCutsSubLeadingJet"), 1, ringObservable2ndJet); + } + } + + // 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") + histos.fill(HIST("pRingCuts"), 2, ringObservable); + POLARIZATION_PROFILE_FILL_LIST(APPLY_HISTO_FILL, "JetKinematicCuts") + } + + // 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); + POLARIZATION_PROFILE_FILL_LIST(APPLY_HISTO_FILL, "JetAndLambdaKinematicCuts") + } + + // Same variations for the leading particle and for the subleading jet: + if (kinematicLeadPCheck) { + RING_OBSERVABLE_LEADP_FILL_LIST(APPLY_HISTO_FILL, "JetKinematicCuts") + histos.fill(HIST("pRingCutsLeadingP"), 2, ringObservableLeadP); + } + if (kinematic2ndJetCheck) { + RING_OBSERVABLE_2NDJET_FILL_LIST(APPLY_HISTO_FILL, "JetKinematicCuts") + histos.fill(HIST("pRingCutsSubLeadingJet"), 2, ringObservable2ndJet); + } + if (kinematicLambdaCheck && kinematicLeadPCheck) { + RING_OBSERVABLE_LEADP_FILL_LIST(APPLY_HISTO_FILL, "JetAndLambdaKinematicCuts") + histos.fill(HIST("pRingCutsLeadingP"), 3, ringObservableLeadP); + } + if (kinematicLambdaCheck && kinematic2ndJetCheck) { + RING_OBSERVABLE_2NDJET_FILL_LIST(APPLY_HISTO_FILL, "JetAndLambdaKinematicCuts") + histos.fill(HIST("pRingCutsSubLeadingJet"), 3, ringObservable2ndJet); + } + } // end v0s loop + } // end collisions + } + + PROCESS_SWITCH(lambdajetpolarizationionsderived, processPolarizationData, "Process derived data in Run 3 Data", true); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) @@ -760,4 +773,4 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) } // Avoid macro leakage! -#undef APPLY_HISTO_FILL \ No newline at end of file +#undef APPLY_HISTO_FILL From 5d3cb83deb1f4304f1c2aee54c527c3d4192fa13 Mon Sep 17 00:00:00 2001 From: ALICE Action Bot Date: Wed, 4 Mar 2026 18:27:34 +0000 Subject: [PATCH 40/40] Please consider the following formatting changes --- PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx | 1 - 1 file changed, 1 deletion(-) diff --git a/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx b/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx index 5c1e2a99f63..14a1f4da3c4 100644 --- a/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx +++ b/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx @@ -2000,4 +2000,3 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) return WorkflowSpec{ adaptAnalysisTask(cfgc)}; } - \ No newline at end of file