From 2451710ed58ad4cacc6a09d17f17b1a02a688c29 Mon Sep 17 00:00:00 2001 From: Meiyi Chen Date: Thu, 30 Jul 2026 15:03:25 +0800 Subject: [PATCH 01/15] Update CMakeLists.txt --- PWGCF/Femto/FemtoNuclei/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/PWGCF/Femto/FemtoNuclei/CMakeLists.txt b/PWGCF/Femto/FemtoNuclei/CMakeLists.txt index 27462d99baf..03068dcd4aa 100644 --- a/PWGCF/Femto/FemtoNuclei/CMakeLists.txt +++ b/PWGCF/Femto/FemtoNuclei/CMakeLists.txt @@ -10,3 +10,5 @@ # or submit itself to any jurisdiction. add_subdirectory(TableProducer) +add_subdirectory(Tasks) + From 3a6eeb8d7401fde700d7759a9366ebfd7a1d4ede Mon Sep 17 00:00:00 2001 From: Meiyi Chen Date: Thu, 30 Jul 2026 15:04:57 +0800 Subject: [PATCH 02/15] Create Task --- PWGCF/Femto/FemtoNuclei/Task | 1 + 1 file changed, 1 insertion(+) create mode 100644 PWGCF/Femto/FemtoNuclei/Task diff --git a/PWGCF/Femto/FemtoNuclei/Task b/PWGCF/Femto/FemtoNuclei/Task new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/PWGCF/Femto/FemtoNuclei/Task @@ -0,0 +1 @@ + From f6899d516a8733198343c12e13252fe66d12314f Mon Sep 17 00:00:00 2001 From: Meiyi Chen Date: Thu, 30 Jul 2026 15:08:11 +0800 Subject: [PATCH 03/15] Delete PWGCF/Femto/FemtoNuclei/Task --- PWGCF/Femto/FemtoNuclei/Task | 1 - 1 file changed, 1 deletion(-) delete mode 100644 PWGCF/Femto/FemtoNuclei/Task diff --git a/PWGCF/Femto/FemtoNuclei/Task b/PWGCF/Femto/FemtoNuclei/Task deleted file mode 100644 index 8b137891791..00000000000 --- a/PWGCF/Femto/FemtoNuclei/Task +++ /dev/null @@ -1 +0,0 @@ - From cceb576ce14879611d2b6bf3d45f19ea049d3577 Mon Sep 17 00:00:00 2001 From: Meiyi Chen Date: Thu, 30 Jul 2026 15:09:25 +0800 Subject: [PATCH 04/15] Create CMakeLists.txt --- PWGCF/Femto/FemtoNuclei/Tasks/CMakeLists.txt | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 PWGCF/Femto/FemtoNuclei/Tasks/CMakeLists.txt diff --git a/PWGCF/Femto/FemtoNuclei/Tasks/CMakeLists.txt b/PWGCF/Femto/FemtoNuclei/Tasks/CMakeLists.txt new file mode 100644 index 00000000000..cd86c49795b --- /dev/null +++ b/PWGCF/Femto/FemtoNuclei/Tasks/CMakeLists.txt @@ -0,0 +1,14 @@ +# Copyright 2019-2026 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted by this license. + +o2physics_add_dpl_workflow(pidefemtosystematics + SOURCES PiDeFemtoSystematics.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils + COMPONENT_NAME Analysis) From defa3774a6a72e25eb44636e1b341258af28d995 Mon Sep 17 00:00:00 2001 From: Meiyi Chen Date: Thu, 30 Jul 2026 15:09:59 +0800 Subject: [PATCH 05/15] Add files via upload --- .../Tasks/PiDeFemtoSystematics.cxx | 842 ++++++++++++++++++ 1 file changed, 842 insertions(+) create mode 100644 PWGCF/Femto/FemtoNuclei/Tasks/PiDeFemtoSystematics.cxx diff --git a/PWGCF/Femto/FemtoNuclei/Tasks/PiDeFemtoSystematics.cxx b/PWGCF/Femto/FemtoNuclei/Tasks/PiDeFemtoSystematics.cxx new file mode 100644 index 00000000000..20296613a12 --- /dev/null +++ b/PWGCF/Femto/FemtoNuclei/Tasks/PiDeFemtoSystematics.cxx @@ -0,0 +1,842 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted by this license. + +/// \file PiDeFemtoSystematics.cxx +/// \brief Histogram-only pion-deuteron femtoscopy task for train subwagon cut variations +/// +/// The task intentionally produces no derived AOD tables. A train subwagon +/// supplies one complete set of cuts. The default wagon and every systematic +/// subwagon therefore run the same event and pair workflow, while a systematic +/// subwagon changes exactly one configurable cut. + +#include "Common/Core/Zorro.h" +#include "Common/Core/ZorroSummary.h" +#include "Common/Core/trackUtilities.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponseITS.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; + +using CollisionsFull = soa::Join; +using TrackCandidates = soa::Join; + +namespace +{ +constexpr std::array BetheBlochDeDefault{-136.71, 0.441, 0.2269, 1.347, 0.8035, 0.09}; +const std::vector betheBlochParticleNames{"De"}; +const std::vector betheBlochParameterNames{"p0", "p1", "p2", "p3", "p4", "resolution"}; +constexpr std::array tpcRadii{{85.f, 105.f, 125.f, 145.f, 165.f, 185.f, 205.f, 225.f, 245.f}}; +constexpr int NumberOfBetheBlochShapeParameters = 5; +constexpr int BetheBlochResolutionIndex = 5; +constexpr double AutomaticMagneticFieldThreshold = -998.; +constexpr float NominalL3Current = 30000.f; +constexpr float InvalidPhiStar = 999.f; +constexpr int ClosePairRadiusModeAtPV = 0; +constexpr int ClosePairRadiusModeAtSpecificRadius = 2; +using PairLorentzVector = ROOT::Math::LorentzVector>; + +enum SelectionStep { + kAllTracks = 0, + kTrackCuts, + kPidCuts, + kNSelectionSteps +}; + +enum PairChannel { + kLikeSignMatter = 0, + kLikeSignAntimatter, + kUnlikeSignMatter, + kUnlikeSignAntimatter, + kNPairChannels +}; +} // namespace + +struct PiDeFemtoSystematics { + struct : ConfigurableGroup { + std::string prefix{"event"}; + Configurable vertexZMax{"vertexZMax", 10.f, "Maximum absolute collision z vertex"}; + Configurable numberOfMixedEvents{"numberOfMixedEvents", 5, "Number of mixed events per event"}; + } eventCuts; + + struct : ConfigurableGroup { + std::string prefix{"pion"}; + Configurable ptMin{"ptMin", 0.14f, "Minimum pion pT"}; + Configurable ptMax{"ptMax", 4.0f, "Maximum pion pT"}; + Configurable etaMax{"etaMax", 0.8f, "Maximum absolute pion eta"}; + Configurable itsInnerBarrelClustersMin{"itsInnerBarrelClustersMin", 3, "Minimum pion ITS inner-barrel clusters"}; + Configurable itsClustersMin{"itsClustersMin", 7, "Minimum pion ITS clusters"}; + Configurable tpcClustersMin{"tpcClustersMin", 80, "Minimum pion found TPC clusters"}; + Configurable tpcCrossedRowsMin{"tpcCrossedRowsMin", 90, "Minimum pion crossed TPC rows"}; + Configurable dcaXYOffset{"dcaXYOffset", 0.004f, "Pion DCAxy cut offset"}; + Configurable dcaXYPtCoefficient{"dcaXYPtCoefficient", 0.013f, "Pion DCAxy inverse-pT coefficient"}; + Configurable dcaZOffset{"dcaZOffset", 0.004f, "Pion DCAz cut offset"}; + Configurable dcaZPtCoefficient{"dcaZPtCoefficient", 0.013f, "Pion DCAz inverse-pT coefficient"}; + Configurable combinedPidMomentumMin{"combinedPidMomentumMin", 0.5f, "Momentum above which pion TPC+TOF PID is required"}; + Configurable tpcNsigmaMax{"tpcNsigmaMax", 3.f, "Maximum absolute pion TPC n-sigma below the TOF threshold"}; + Configurable combinedNsigmaMax{"combinedNsigmaMax", 3.f, "Maximum pion combined TPC+TOF n-sigma"}; + } pionCuts; + + struct : ConfigurableGroup { + std::string prefix{"deuteron"}; + Configurable ptMin{"ptMin", 0.6f, "Minimum deuteron pT"}; + Configurable ptMax{"ptMax", 2.0f, "Maximum deuteron pT"}; + Configurable etaMax{"etaMax", 0.8f, "Maximum absolute deuteron eta"}; + Configurable tpcClustersMin{"tpcClustersMin", 110, "Minimum deuteron found TPC clusters"}; + Configurable tpcCrossedRowsMin{"tpcCrossedRowsMin", 100, "Minimum deuteron crossed TPC rows"}; + Configurable itsClustersMin{"itsClustersMin", 5, "Minimum deuteron ITS clusters"}; + Configurable itsInnerBarrelClustersMin{"itsInnerBarrelClustersMin", 1, "Minimum deuteron ITS inner-barrel clusters"}; + Configurable sharedTPCClustersMax{"sharedTPCClustersMax", 160, "Maximum deuteron shared TPC clusters"}; + Configurable sharedTPCFractionMax{"sharedTPCFractionMax", 1.f, "Maximum deuteron shared TPC-cluster fraction"}; + Configurable tpcInnerParamMin{"tpcInnerParamMin", 0.f, "Minimum deuteron TPC inner parameter"}; + Configurable tofMomentumMin{"tofMomentumMin", 1.2f, "TPC inner parameter above which combined TPC+TOF PID is required"}; + Configurable nsigmaMax{"nsigmaMax", 2.5f, "Common deuteron TPC, ITS and combined PID threshold"}; + Configurable requireIndividualNsigma{"requireIndividualNsigma", false, "Also apply individual TPC and TOF cuts in the combined PID branch"}; + Configurable dcaXYOffset{"dcaXYOffset", 0.004f, "Deuteron DCAxy cut offset"}; + Configurable dcaXYPtCoefficient{"dcaXYPtCoefficient", 0.013f, "Deuteron DCAxy inverse-pT coefficient"}; + Configurable dcaZOffset{"dcaZOffset", 0.004f, "Deuteron DCAz cut offset"}; + Configurable dcaZPtCoefficient{"dcaZPtCoefficient", 0.013f, "Deuteron DCAz inverse-pT coefficient"}; + } deuteronCuts; + + struct : ConfigurableGroup { + std::string prefix{"pair"}; + Configurable enableClosePairRejection{"enableClosePairRejection", true, "Enable pion-deuteron close-pair rejection"}; + Configurable closePairDeltaEtaMax{"closePairDeltaEtaMax", 0.01f, "CPR ellipse delta-eta radius"}; + Configurable closePairDeltaPhiMax{"closePairDeltaPhiMax", 0.01f, "CPR ellipse delta-phi-star radius"}; + Configurable closePairRadiusMode{"closePairRadiusMode", 1, "CPR mode: 0 PV, 1 average TPC phi-star, 2 one TPC radius"}; + Configurable closePairSpecificRadius{"closePairSpecificRadius", 85.f, "TPC radius in cm for CPR mode 2"}; + } pairCuts; + + struct : ConfigurableGroup { + std::string prefix{"analysis"}; + Configurable useBetheBlochDeuteronNsigma{"useBetheBlochDeuteronNsigma", false, "Compute deuteron TPC n-sigma from the configured Bethe-Bloch parameters"}; + Configurable lowKstarYieldMax{"lowKstarYieldMax", 0.3f, "Upper kstar used for the SE-yield stability check"}; + } analysisSettings; + + struct : ConfigurableGroup { + std::string prefix{"zorro"}; + Configurable skimmedProcessing{"skimmedProcessing", false, "Process a Zorro-selected deuteron skim"}; + } zorroSettings; + + struct : ConfigurableGroup { + std::string prefix{"ccdb"}; + Configurable magneticField{"magneticField", -999., "Magnetic field; -999 reads it from CCDB"}; + Configurable url{"url", "http://alice-ccdb.cern.ch", "CCDB URL"}; + Configurable grpPath{"grpPath", "GLO/GRP/GRP", "GRP object path"}; + Configurable grpMagPath{"grpMagPath", "GLO/Config/GRPMagField", "GRP magnetic-field object path"}; + Configurable materialCorrection{"materialCorrection", static_cast(o2::base::Propagator::MatCorrType::USEMatCorrNONE), "Material correction used by the optional pair DCA fitter"}; + } ccdbSettings; + + struct : ConfigurableGroup { + std::string prefix{"pidCalibration"}; + Configurable> betheBlochParameters{"betheBlochParameters", + {BetheBlochDeDefault.data(), 1, 6, betheBlochParticleNames, betheBlochParameterNames}, + "Deuteron TPC Bethe-Bloch parameters"}; + } pidCalibration; + + ConfigurableAxis mixingVertexAxis{"mixingVertexAxis", {30, -10., 10.}, "Event-mixing z-vertex bins"}; + ConfigurableAxis mixingCentralityAxis{"mixingCentralityAxis", {40, 0., 100.}, "Event-mixing centrality bins"}; + ConfigurableAxis kstarAxis{"kstarAxis", {300, 0., 3.}, "kstar axis"}; + ConfigurableAxis mtAxis{"mtAxis", {240, 0.8, 3.2}, "pair mT axis"}; + ConfigurableAxis centralityAxis{"centralityAxis", {100, 0., 100.}, "centrality axis"}; + + using MixingBinning = ColumnBinningPolicy; + MixingBinning mixingBinning{{mixingVertexAxis, mixingCentralityAxis}, true}; + SliceCache cache; + SameKindPair mixedEventPairs{ + mixingBinning, eventCuts.numberOfMixedEvents, -1, &cache}; + Preslice tracksPerCollision = aod::track::collisionId; + + HistogramRegistry registry{"PiDeFemtoSystematics", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; + + Service ccdb; + Zorro zorro; + OutputObj zorroSummary{"zorroSummary"}; + o2::vertexing::DCAFitterN<2> pairFitter; + + std::array betheBlochDe{}; + int currentRunNumber{0}; + float magneticField{0.f}; + + void init(InitContext const&) + { + const AxisSpec channelAxis{kNPairChannels, -0.5, static_cast(kNPairChannels) - 0.5, "pair channel"}; + registry.add("Pairs/hSE", "Same-event pion-deuteron pairs", HistType::kTHnSparseF, + {kstarAxis, mtAxis, centralityAxis, channelAxis}); + registry.add("Pairs/hME", "Mixed-event pion-deuteron pairs", HistType::kTHnSparseF, + {kstarAxis, mtAxis, centralityAxis, channelAxis}); + registry.add("Pairs/hSELowKstarYield", "SE low-kstar yield;pair channel;Entries", + HistType::kTH1F, {channelAxis}); + registry.add("Pairs/hMELowKstarYield", "ME low-kstar yield;pair channel;Entries", + HistType::kTH1F, {channelAxis}); + registry.add("Pairs/hCPRBefore", "CPR before selection;#Delta#eta;#Delta#varphi^{*}", + HistType::kTH2F, {{160, -2., 2.}, {160, -3.2, 3.2}}); + registry.add("Pairs/hCPRAfter", "CPR after selection;#Delta#eta;#Delta#varphi^{*}", + HistType::kTH2F, {{160, -2., 2.}, {160, -3.2, 3.2}}); + registry.add("Event/hCounter", "Event selection;step;Entries", + HistType::kTH1F, {{3, -0.5, 2.5}}); + registry.add("Event/hVertexZ", "Selected collision vertex;z (cm);Entries", + HistType::kTH1F, {{200, -20., 20.}}); + registry.add("Event/hCentrality", "Selected collision centrality;FT0C percentile;Entries", + HistType::kTH1F, {{100, 0., 100.}}); + registry.add("Pion/hSelection", "Pion selection;step;Entries", + HistType::kTH1F, {{kNSelectionSteps, -0.5, static_cast(kNSelectionSteps) - 0.5}}); + registry.add("Pion/hPt", "Selected pions;signed p_{T} (GeV/#it{c});Entries", + HistType::kTH1F, {{400, -5., 5.}}); + registry.add("Pion/hEta", "Selected pions;#eta;Entries", + HistType::kTH1F, {{200, -1., 1.}}); + registry.add("Pion/hTPCNsigma", "Selected pions;signed p_{T} (GeV/#it{c});n#sigma_{TPC}", + HistType::kTH2F, {{400, -5., 5.}, {200, -5., 5.}}); + registry.add("Pion/hTOFNsigma", "Selected pions;signed p_{T} (GeV/#it{c});n#sigma_{TOF}", + HistType::kTH2F, {{280, -7., 7.}, {200, -5., 5.}}); + registry.add("Pion/hITSNsigma", "Selected pions;signed p_{T} (GeV/#it{c});n#sigma_{ITS}", + HistType::kTH2F, {{280, -7., 7.}, {120, -3., 3.}}); + registry.add("Pion/hTPCTOFNsigma", "Selected pions;signed p_{T} (GeV/#it{c});n#sigma_{TPC+TOF}", + HistType::kTH2F, {{280, -7., 7.}, {100, 0., 5.}}); + registry.add("Deuteron/hSelection", "Deuteron selection;step;Entries", + HistType::kTH1F, {{kNSelectionSteps, -0.5, static_cast(kNSelectionSteps) - 0.5}}); + registry.add("Deuteron/hPt", "Selected deuterons;signed p_{T} (GeV/#it{c});Entries", + HistType::kTH1F, {{400, -5., 5.}}); + registry.add("Deuteron/hEta", "Selected deuterons;#eta;Entries", + HistType::kTH1F, {{200, -1., 1.}}); + registry.add("Deuteron/hTPCNsigma", "Selected deuterons;signed p_{T} (GeV/#it{c});n#sigma_{TPC}", + HistType::kTH2F, {{400, -5., 5.}, {200, -5., 5.}}); + registry.add("Deuteron/hTOFNsigma", "Selected deuterons;signed p_{T} (GeV/#it{c});n#sigma_{TOF}", + HistType::kTH2F, {{280, -7., 7.}, {200, -5., 5.}}); + registry.add("Deuteron/hITSNsigma", "Selected deuterons;signed p_{T} (GeV/#it{c});n#sigma_{ITS}", + HistType::kTH2F, {{280, -7., 7.}, {120, -3., 3.}}); + registry.add("Deuteron/hTPCTOFNsigma", "Selected deuterons;signed p_{T} (GeV/#it{c});n#sigma_{TPC+TOF}", + HistType::kTH2F, {{280, -7., 7.}, {100, 0., 5.}}); + registry.add("Deuteron/hTPCClusters", "Selected deuterons;TPC clusters;Entries", + HistType::kTH1F, {{161, -0.5, 160.5}}); + registry.add("Deuteron/hTPCCrossedRows", "Selected deuterons;TPC crossed rows;Entries", + HistType::kTH1F, {{161, -0.5, 160.5}}); + + addCutConfigurationHistogram(); + setAxisLabels(); + + zorroSummary.setObject(zorro.getZorroSummary()); + ccdb->setURL(ccdbSettings.url.value); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + ccdb->setFatalWhenNull(false); + + pairFitter.setPropagateToPCA(true); + pairFitter.setMaxR(200.); + pairFitter.setMinParamChange(1.e-3); + pairFitter.setMinRelChi2Change(0.9); + pairFitter.setMaxDZIni(1.e9); + pairFitter.setMaxChi2(1.e9); + pairFitter.setUseAbsDCA(true); + pairFitter.setMatCorrType( + static_cast(ccdbSettings.materialCorrection.value)); + + for (int i = 0; i < NumberOfBetheBlochShapeParameters; ++i) { + betheBlochDe[i] = pidCalibration.betheBlochParameters->get("De", Form("p%i", i)); + } + betheBlochDe[BetheBlochResolutionIndex] = pidCalibration.betheBlochParameters->get("De", "resolution"); + } + + void setAxisLabels() + { + const std::array channelLabels{ + "LS matter", "LS antimatter", "US matter", "US antimatter"}; + const auto setSparseChannelLabels = [&channelLabels](THnSparse* histogram) { + for (int channel = 0; channel < kNPairChannels; ++channel) { + histogram->GetAxis(3)->SetBinLabel(channel + 1, channelLabels[channel]); + } + }; + setSparseChannelLabels(registry.get(HIST("Pairs/hSE")).get()); + setSparseChannelLabels(registry.get(HIST("Pairs/hME")).get()); + + const auto setYieldChannelLabels = [&channelLabels](TH1* histogram) { + for (int channel = 0; channel < kNPairChannels; ++channel) { + histogram->GetXaxis()->SetBinLabel(channel + 1, channelLabels[channel]); + } + }; + setYieldChannelLabels(registry.get(HIST("Pairs/hSELowKstarYield")).get()); + setYieldChannelLabels(registry.get(HIST("Pairs/hMELowKstarYield")).get()); + + const std::array selectionLabels{"all", "track cuts", "PID"}; + const auto setSelectionLabels = [&selectionLabels](TH1* histogram) { + for (int step = 0; step < kNSelectionSteps; ++step) { + histogram->GetXaxis()->SetBinLabel(step + 1, selectionLabels[step]); + } + }; + setSelectionLabels(registry.get(HIST("Pion/hSelection")).get()); + setSelectionLabels(registry.get(HIST("Deuteron/hSelection")).get()); + auto eventCounter = registry.get(HIST("Event/hCounter")); + eventCounter->GetXaxis()->SetBinLabel(1, "all"); + eventCounter->GetXaxis()->SetBinLabel(2, "sel8 and vertex"); + eventCounter->GetXaxis()->SetBinLabel(3, "Zorro"); + } + + void addCutConfigurationHistogram() + { + const std::vector> cuts{ + {"pionPtMin", pionCuts.ptMin.value}, + {"pionPtMax", pionCuts.ptMax.value}, + {"pionEtaMax", pionCuts.etaMax.value}, + {"pionITSInnerBarrelClustersMin", pionCuts.itsInnerBarrelClustersMin.value}, + {"pionITSClustersMin", pionCuts.itsClustersMin.value}, + {"pionTPCClustersMin", pionCuts.tpcClustersMin.value}, + {"pionTPCCrossedRowsMin", pionCuts.tpcCrossedRowsMin.value}, + {"pionDCAxyOffset", pionCuts.dcaXYOffset.value}, + {"pionDCAxyPtCoefficient", pionCuts.dcaXYPtCoefficient.value}, + {"pionDCAzOffset", pionCuts.dcaZOffset.value}, + {"pionDCAzPtCoefficient", pionCuts.dcaZPtCoefficient.value}, + {"pionCombinedPidMomentumMin", pionCuts.combinedPidMomentumMin.value}, + {"pionTPCNsigmaMax", pionCuts.tpcNsigmaMax.value}, + {"pionCombinedNsigmaMax", pionCuts.combinedNsigmaMax.value}, + {"deuteronPtMin", deuteronCuts.ptMin.value}, + {"deuteronPtMax", deuteronCuts.ptMax.value}, + {"deuteronEtaMax", deuteronCuts.etaMax.value}, + {"deuteronTPCClustersMin", deuteronCuts.tpcClustersMin.value}, + {"deuteronTPCCrossedRowsMin", deuteronCuts.tpcCrossedRowsMin.value}, + {"deuteronITSClustersMin", deuteronCuts.itsClustersMin.value}, + {"deuteronITSInnerBarrelClustersMin", deuteronCuts.itsInnerBarrelClustersMin.value}, + {"deuteronSharedTPCClustersMax", deuteronCuts.sharedTPCClustersMax.value}, + {"deuteronSharedTPCFractionMax", deuteronCuts.sharedTPCFractionMax.value}, + {"deuteronTPCInnerParamMin", deuteronCuts.tpcInnerParamMin.value}, + {"deuteronTOFMomentumMin", deuteronCuts.tofMomentumMin.value}, + {"deuteronNsigmaMax", deuteronCuts.nsigmaMax.value}, + {"requireIndividualNsigma", deuteronCuts.requireIndividualNsigma.value}, + {"deuteronDCAxyOffset", deuteronCuts.dcaXYOffset.value}, + {"deuteronDCAxyPtCoefficient", deuteronCuts.dcaXYPtCoefficient.value}, + {"deuteronDCAzOffset", deuteronCuts.dcaZOffset.value}, + {"deuteronDCAzPtCoefficient", deuteronCuts.dcaZPtCoefficient.value}, + {"enableCPR", pairCuts.enableClosePairRejection.value}, + {"cprDeltaEtaMax", pairCuts.closePairDeltaEtaMax.value}, + {"cprDeltaPhiMax", pairCuts.closePairDeltaPhiMax.value}, + {"cprRadiusMode", pairCuts.closePairRadiusMode.value}, + {"cprSpecificRadius", pairCuts.closePairSpecificRadius.value}, + {"useBetheBlochDeuteronNsigma", analysisSettings.useBetheBlochDeuteronNsigma.value}, + {"skimmedProcessing", zorroSettings.skimmedProcessing.value}, + {"vertexZMax", eventCuts.vertexZMax.value}, + {"numberOfMixedEvents", eventCuts.numberOfMixedEvents.value}}; + + const AxisSpec cutAxis{static_cast(cuts.size()), -0.5, + static_cast(cuts.size()) - 0.5, "cut"}; + registry.add("Config/hCutValues", "Configured cut values;cut;value", + HistType::kTH1D, {cutAxis}); + auto histogram = registry.get(HIST("Config/hCutValues")); + for (size_t index = 0; index < cuts.size(); ++index) { + histogram->GetXaxis()->SetBinLabel(static_cast(index) + 1, cuts[index].first.c_str()); + histogram->SetBinContent(static_cast(index) + 1, cuts[index].second); + } + } + + void initCCDB(aod::BCsWithTimestamps::iterator const& bc) + { + if (currentRunNumber == bc.runNumber()) { + return; + } + if (zorroSettings.skimmedProcessing.value) { + zorro.initCCDB(ccdb.service, bc.runNumber(), bc.timestamp(), "fDe"); + zorro.populateHistRegistry(registry, bc.runNumber()); + } + + auto* grp = ccdb->getForTimeStamp( + ccdbSettings.grpPath.value, bc.timestamp()); + if (grp) { + o2::base::Propagator::initFieldFromGRP(grp); + magneticField = ccdbSettings.magneticField.value < AutomaticMagneticFieldThreshold ? grp->getNominalL3Field() + : ccdbSettings.magneticField.value; + } else { + auto* grpMag = ccdb->getForTimeStamp( + ccdbSettings.grpMagPath.value, bc.timestamp()); + if (!grpMag) { + LOG(fatal) << "Could not load magnetic field from " << ccdbSettings.grpPath.value + << " or " << ccdbSettings.grpMagPath.value; + } + o2::base::Propagator::initFieldFromGRP(grpMag); + magneticField = ccdbSettings.magneticField.value < AutomaticMagneticFieldThreshold + ? std::lround(5.f * grpMag->getL3Current() / NominalL3Current) + : ccdbSettings.magneticField.value; + } + currentRunNumber = bc.runNumber(); + } + + template + bool isCollisionSelected(Collision const& collision, bool fillQA) + { + if (fillQA) { + registry.fill(HIST("Event/hCounter"), 0.); + } + auto bc = collision.template bc_as(); + initCCDB(bc); + if (!collision.sel8() || std::abs(collision.posZ()) > eventCuts.vertexZMax.value) { + return false; + } + if (zorroSettings.skimmedProcessing.value) { + if (!zorro.isSelected(bc.globalBC())) { + return false; + } + if (fillQA) { + registry.fill(HIST("Event/hCounter"), 2.); + } + } + if (fillQA) { + registry.fill(HIST("Event/hCounter"), 1.); + registry.fill(HIST("Event/hVertexZ"), collision.posZ()); + registry.fill(HIST("Event/hCentrality"), collision.centFT0C()); + } + return true; + } + + template + bool passPionTrackCuts(Track const& track) const + { + const float absPt = std::abs(track.pt()); + if (absPt <= 0.f || absPt < pionCuts.ptMin.value || absPt > pionCuts.ptMax.value || + std::abs(track.eta()) > pionCuts.etaMax.value) { + return false; + } + if (track.itsNClsInnerBarrel() < pionCuts.itsInnerBarrelClustersMin.value || + track.itsNCls() < pionCuts.itsClustersMin.value || + track.tpcNClsFound() < pionCuts.tpcClustersMin.value || + track.tpcNClsCrossedRows() < pionCuts.tpcCrossedRowsMin.value) { + return false; + } + const float dcaXYMax = pionCuts.dcaXYOffset.value + + pionCuts.dcaXYPtCoefficient.value / absPt; + const float dcaZMax = pionCuts.dcaZOffset.value + + pionCuts.dcaZPtCoefficient.value / absPt; + return std::abs(track.dcaXY()) <= dcaXYMax && + std::abs(track.dcaZ()) <= dcaZMax; + } + + template + bool passPionPID(Track const& track) const + { + const float tpcNsigma = track.tpcNSigmaPi(); + if (std::abs(track.p()) <= pionCuts.combinedPidMomentumMin.value) { + return std::abs(tpcNsigma) <= pionCuts.tpcNsigmaMax.value; + } + if (!track.hasTOF()) { + return false; + } + const float tofNsigma = track.tofNSigmaPi(); + if (std::hypot(tpcNsigma, tofNsigma) > + pionCuts.combinedNsigmaMax.value) { + return false; + } + return !deuteronCuts.requireIndividualNsigma.value || + (std::abs(tpcNsigma) <= pionCuts.combinedNsigmaMax.value && + std::abs(tofNsigma) <= pionCuts.combinedNsigmaMax.value); + } + + template + bool passDeuteronTrackCuts(Track const& track) const + { + const float absPt = std::abs(track.pt()); + if (absPt <= 0.f || absPt < deuteronCuts.ptMin.value || + absPt > deuteronCuts.ptMax.value || + std::abs(track.eta()) > deuteronCuts.etaMax.value) { + return false; + } + if (track.tpcNClsFound() < deuteronCuts.tpcClustersMin.value || + track.tpcNClsCrossedRows() < deuteronCuts.tpcCrossedRowsMin.value || + track.tpcNClsShared() > deuteronCuts.sharedTPCClustersMax.value || + track.tpcFractionSharedCls() > deuteronCuts.sharedTPCFractionMax.value || + track.itsNCls() < deuteronCuts.itsClustersMin.value || + track.itsNClsInnerBarrel() < deuteronCuts.itsInnerBarrelClustersMin.value) { + return false; + } + const float dcaXYMax = deuteronCuts.dcaXYOffset.value + + deuteronCuts.dcaXYPtCoefficient.value / absPt; + const float dcaZMax = deuteronCuts.dcaZOffset.value + + deuteronCuts.dcaZPtCoefficient.value / absPt; + return std::abs(track.dcaXY()) <= dcaXYMax && + std::abs(track.dcaZ()) <= dcaZMax; + } + + template + float deuteronTPCNsigma(Track const& track) const + { + if (!analysisSettings.useBetheBlochDeuteronNsigma.value) { + return track.tpcNSigmaDe(); + } + const float expected = o2::common::BetheBlochAleph( + static_cast(track.tpcInnerParam() / + o2::constants::physics::MassDeuteron), + betheBlochDe[0], betheBlochDe[1], betheBlochDe[2], + betheBlochDe[3], betheBlochDe[4]); + return static_cast((track.tpcSignal() - expected) / + (expected * betheBlochDe[BetheBlochResolutionIndex])); + } + + template + bool passDeuteronPID(Track const& track) const + { + const float tpcInnerParam = track.tpcInnerParam(); + if (std::abs(tpcInnerParam) < deuteronCuts.tpcInnerParamMin.value) { + return false; + } + const float tpcNsigma = deuteronTPCNsigma(track); + if (track.hasTOF() && + tpcInnerParam > deuteronCuts.tofMomentumMin.value) { + const float tofNsigma = track.tofNSigmaDe(); + if (std::hypot(tpcNsigma, tofNsigma) > deuteronCuts.nsigmaMax.value) { + return false; + } + return !deuteronCuts.requireIndividualNsigma.value || + (std::abs(tpcNsigma) <= deuteronCuts.nsigmaMax.value && + std::abs(tofNsigma) <= deuteronCuts.nsigmaMax.value); + } + if (tpcInnerParam <= deuteronCuts.tofMomentumMin.value) { + if (std::abs(tpcNsigma) > deuteronCuts.nsigmaMax.value) { + return false; + } + o2::aod::ITSResponse itsResponse; + const float itsNsigma = + itsResponse.nSigmaITS( + track.itsClusterSizes(), track.p(), track.eta()); + return std::abs(itsNsigma) <= deuteronCuts.nsigmaMax.value; + } + return false; + } + + template + void fillSingleParticleQA(Tracks const& tracks) + { + for (auto const& track : tracks) { + registry.fill(HIST("Pion/hSelection"), static_cast(kAllTracks)); + if (passPionTrackCuts(track)) { + registry.fill(HIST("Pion/hSelection"), static_cast(kTrackCuts)); + if (passPionPID(track)) { + registry.fill(HIST("Pion/hSelection"), static_cast(kPidCuts)); + const float signedPt = track.sign() * track.pt(); + const float tpcNsigma = track.tpcNSigmaPi(); + registry.fill(HIST("Pion/hPt"), signedPt); + registry.fill(HIST("Pion/hEta"), track.eta()); + registry.fill(HIST("Pion/hTPCNsigma"), signedPt, tpcNsigma); + o2::aod::ITSResponse itsResponse; + const float itsNsigma = itsResponse.nSigmaITS( + track.itsClusterSizes(), track.p(), track.eta()); + registry.fill(HIST("Pion/hITSNsigma"), signedPt, itsNsigma); + if (track.hasTOF() && + std::abs(track.p()) > pionCuts.combinedPidMomentumMin.value) { + const float tofNsigma = track.tofNSigmaPi(); + registry.fill(HIST("Pion/hTOFNsigma"), signedPt, tofNsigma); + registry.fill(HIST("Pion/hTPCTOFNsigma"), signedPt, + std::hypot(tpcNsigma, tofNsigma)); + } + } + } + + registry.fill(HIST("Deuteron/hSelection"), + static_cast(kAllTracks)); + if (passDeuteronTrackCuts(track)) { + registry.fill(HIST("Deuteron/hSelection"), + static_cast(kTrackCuts)); + if (passDeuteronPID(track)) { + registry.fill(HIST("Deuteron/hSelection"), + static_cast(kPidCuts)); + const float signedPt = track.sign() * track.pt(); + const float tpcNsigma = deuteronTPCNsigma(track); + registry.fill(HIST("Deuteron/hPt"), signedPt); + registry.fill(HIST("Deuteron/hEta"), track.eta()); + registry.fill(HIST("Deuteron/hTPCNsigma"), signedPt, tpcNsigma); + if (track.tpcInnerParam() <= deuteronCuts.tofMomentumMin.value) { + o2::aod::ITSResponse itsResponse; + const float itsNsigma = + itsResponse.nSigmaITS( + track.itsClusterSizes(), track.p(), track.eta()); + registry.fill(HIST("Deuteron/hITSNsigma"), signedPt, itsNsigma); + } else if (track.hasTOF()) { + const float tofNsigma = track.tofNSigmaDe(); + registry.fill(HIST("Deuteron/hTOFNsigma"), signedPt, tofNsigma); + registry.fill(HIST("Deuteron/hTPCTOFNsigma"), signedPt, + std::hypot(tpcNsigma, tofNsigma)); + } + registry.fill(HIST("Deuteron/hTPCClusters"), + static_cast(track.tpcNClsFound())); + registry.fill(HIST("Deuteron/hTPCCrossedRows"), + static_cast(track.tpcNClsCrossedRows())); + } + } + } + } + + template + float phiAtRadius(Track const& track, float radius) const + { + const float absPt = std::abs(track.pt()); + if (absPt <= 0.f) { + return InvalidPhiStar; + } + const float argument = + 0.3f * static_cast(track.sign()) * 0.1f * magneticField * + radius * 0.01f / (2.f * absPt); + if (std::abs(argument) >= 1.f) { + return InvalidPhiStar; + } + return track.phi() - std::asin(argument); + } + + static float wrapDeltaPhi(float deltaPhi) + { + return std::atan2(std::sin(deltaPhi), std::cos(deltaPhi)); + } + + template + float averagePhiStar(FirstTrack const& first, + SecondTrack const& second) const + { + float sum = 0.f; + int entries = 0; + for (const auto& radius : tpcRadii) { + const float firstPhi = phiAtRadius(first, radius); + const float secondPhi = phiAtRadius(second, radius); + if (firstPhi == InvalidPhiStar || secondPhi == InvalidPhiStar) { + continue; + } + sum += wrapDeltaPhi(firstPhi - secondPhi); + ++entries; + } + return entries > 0 ? sum / static_cast(entries) : InvalidPhiStar; + } + + template + bool isClosePair(FirstTrack const& first, SecondTrack const& second, + bool fillQA) + { + if (!pairCuts.enableClosePairRejection.value || + first.sign() != second.sign()) { + return false; + } + const float deltaEta = first.eta() - second.eta(); + float deltaPhi = averagePhiStar(first, second); + if (pairCuts.closePairRadiusMode.value == ClosePairRadiusModeAtPV) { + deltaPhi = wrapDeltaPhi(first.phi() - second.phi()); + } else if (pairCuts.closePairRadiusMode.value == ClosePairRadiusModeAtSpecificRadius) { + const float firstPhi = + phiAtRadius(first, pairCuts.closePairSpecificRadius.value); + const float secondPhi = + phiAtRadius(second, pairCuts.closePairSpecificRadius.value); + if (firstPhi == InvalidPhiStar || secondPhi == InvalidPhiStar) { + return false; + } + deltaPhi = wrapDeltaPhi(firstPhi - secondPhi); + } + if (deltaPhi == InvalidPhiStar || + pairCuts.closePairDeltaEtaMax.value <= 0.f || + pairCuts.closePairDeltaPhiMax.value <= 0.f) { + return false; + } + if (fillQA) { + registry.fill(HIST("Pairs/hCPRBefore"), deltaEta, deltaPhi); + } + const bool rejected = + deltaEta * deltaEta / + (pairCuts.closePairDeltaEtaMax.value * pairCuts.closePairDeltaEtaMax.value) + + deltaPhi * deltaPhi / + (pairCuts.closePairDeltaPhiMax.value * pairCuts.closePairDeltaPhiMax.value) < + 1.f; + if (fillQA && !rejected) { + registry.fill(HIST("Pairs/hCPRAfter"), deltaEta, deltaPhi); + } + return rejected; + } + + template + bool hasValidPairFit(FirstTrack const& first, SecondTrack const& second) + { + try { + return pairFitter.process(getTrackParCov(first), + getTrackParCov(second)) > 0; + } catch (...) { + return false; + } + } + + float pairKstar(std::array const& pionMomentum, + std::array const& deuteronMomentum) const + { + const PairLorentzVector pion( + pionMomentum[0], pionMomentum[1], pionMomentum[2], + o2::constants::physics::MassPiPlus); + const PairLorentzVector deuteron( + deuteronMomentum[0], deuteronMomentum[1], deuteronMomentum[2], + o2::constants::physics::MassDeuteron); + const PairLorentzVector pair = pion + deuteron; + const float beta = pair.Beta(); + const ROOT::Math::Boost boost( + -beta * std::cos(pair.Phi()) * std::sin(pair.Theta()), + -beta * std::sin(pair.Phi()) * std::sin(pair.Theta()), + -beta * std::cos(pair.Theta())); + const PairLorentzVector relativeMomentum = + boost(pion) - boost(deuteron); + return 0.5f * relativeMomentum.P(); + } + + float pairMT(std::array const& pionMomentum, + std::array const& deuteronMomentum) const + { + const PairLorentzVector pion( + pionMomentum[0], pionMomentum[1], pionMomentum[2], + o2::constants::physics::MassPiPlus); + const PairLorentzVector deuteron( + deuteronMomentum[0], deuteronMomentum[1], deuteronMomentum[2], + o2::constants::physics::MassDeuteron); + const float kT = 0.5f * (pion + deuteron).Pt(); + const float averageMass = + 0.5f * (o2::constants::physics::MassPiPlus + + o2::constants::physics::MassDeuteron); + return std::hypot(kT, averageMass); + } + + static int pairChannel(int deuteronSign, int pionSign) + { + const bool unlikeSign = deuteronSign * pionSign < 0; + const bool antimatter = deuteronSign < 0; + return 2 * static_cast(unlikeSign) + + static_cast(antimatter); + } + + template + void fillPair(DeuteronTrack const& deuteron, PionTrack const& pion, + float centrality, bool mixedEvent) + { + const std::array deuteronMomentum{ + deuteron.px(), deuteron.py(), deuteron.pz()}; + const std::array pionMomentum{ + pion.px(), pion.py(), pion.pz()}; + const float kstar = pairKstar(pionMomentum, deuteronMomentum); + const float mt = pairMT(pionMomentum, deuteronMomentum); + const float channel = static_cast( + pairChannel(deuteron.sign(), pion.sign())); + if (mixedEvent) { + registry.fill(HIST("Pairs/hME"), kstar, mt, centrality, channel); + if (kstar < analysisSettings.lowKstarYieldMax.value) { + registry.fill(HIST("Pairs/hMELowKstarYield"), channel); + } + } else { + registry.fill(HIST("Pairs/hSE"), kstar, mt, centrality, channel); + if (kstar < analysisSettings.lowKstarYieldMax.value) { + registry.fill(HIST("Pairs/hSELowKstarYield"), channel); + } + } + } + + template + void buildPairs(DeuteronTracks const& deuteronTracks, + PionTracks const& pionTracks, float centrality, + bool mixedEvent) + { + for (auto const& deuteron : deuteronTracks) { + if (!passDeuteronTrackCuts(deuteron) || + !passDeuteronPID(deuteron)) { + continue; + } + for (auto const& pion : pionTracks) { + if (!mixedEvent && deuteron.globalIndex() == pion.globalIndex()) { + continue; + } + if (!passPionTrackCuts(pion) || !passPionPID(pion)) { + continue; + } + if (isClosePair(deuteron, pion, !mixedEvent)) { + continue; + } + if (!mixedEvent && !hasValidPairFit(deuteron, pion)) { + continue; + } + fillPair(deuteron, pion, centrality, mixedEvent); + } + } + } + + void processSameEvent(CollisionsFull const& collisions, + TrackCandidates const& tracks, + aod::BCsWithTimestamps const&) + { + for (auto const& collision : collisions) { + if (!isCollisionSelected(collision, true)) { + continue; + } + auto collisionTracks = + tracks.sliceBy(tracksPerCollision, collision.globalIndex()); + collisionTracks.bindExternalIndices(&tracks); + fillSingleParticleQA(collisionTracks); + buildPairs(collisionTracks, collisionTracks, + collision.centFT0C(), false); + } + } + PROCESS_SWITCH(PiDeFemtoSystematics, processSameEvent, + "Fill same-event pion-deuteron pairs", true); + + void processMixedEvent(CollisionsFull const& /*collisions*/, + TrackCandidates const& /*tracks*/, + aod::BCsWithTimestamps const&) + { + for (auto const& [collision1, tracks1, collision2, tracks2] : + mixedEventPairs) { + if (!collision1.sel8() || !collision2.sel8()) { + continue; + } + auto bc1 = collision1.template bc_as(); + auto bc2 = collision2.template bc_as(); + initCCDB(bc1); + buildPairs(tracks1, tracks2, collision1.centFT0C(), true); + initCCDB(bc2); + buildPairs(tracks2, tracks1, collision2.centFT0C(), true); + } + } + PROCESS_SWITCH(PiDeFemtoSystematics, processMixedEvent, + "Fill mixed-event pion-deuteron pairs", true); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc)}; +} From 2b6a3c79594834410b2f82ff0c84b0dde276cedc Mon Sep 17 00:00:00 2001 From: Meiyi Chen Date: Thu, 30 Jul 2026 15:10:50 +0800 Subject: [PATCH 06/15] Update HadNucleiFemto.cxx --- .../TableProducer/HadNucleiFemto.cxx | 341 +++++++++++++++--- 1 file changed, 296 insertions(+), 45 deletions(-) diff --git a/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx b/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx index 2d7b4310774..6753a0ef66b 100644 --- a/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx +++ b/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx @@ -77,7 +77,7 @@ using std::array; using CollBracket = o2::math_utils::Bracket; using CollisionsFull = soa::Join; using CollisionsFullMC = soa::Join; -using TrackCandidates = soa::Join; +using TrackCandidates = soa::Join; namespace { @@ -87,12 +87,9 @@ const std::vector betheBlochParticleNames{"De", "He3"}; const std::vector betheBlochParNames{"p0", "p1", "p2", "p3", "p4", "resolution"}; constexpr std::array tmpRadiiTPC{{85.f, 105.f, 125.f, 145.f, 165.f, 185.f, 205.f, 225.f, 245.f}}; constexpr int DeuteronPDG = o2::constants::physics::Pdg::kDeuteron; +constexpr int TritonPDG = o2::constants::physics::Pdg::kTriton; constexpr int He3PDG = o2::constants::physics::Pdg::kHelium3; -constexpr float He3RigidityMin = 0.8f; -constexpr int He3TPCNClsFoundMin = 110; constexpr float He3TPCChi2NClMin = 0.5f; -constexpr float He3TPCNSigmaMax = 3.0f; -constexpr float He3ITSNSigmaMin = -1.5f; using PairLorentzVector = ROOT::Math::LorentzVector>; enum Selections { @@ -132,12 +129,19 @@ struct HadNucandidate { float momNuTPC = -99.f; float momHadTPC = -99.f; uint8_t nTPCClustersNu = 0u; + uint8_t nTPCClustersHad = 0u; + uint8_t nTPCCrossedRowsNu = 0u; + uint8_t nTPCCrossedRowsHad = 0u; uint8_t sharedClustersNu = 0u; uint8_t sharedClustersHad = 0u; float chi2TPCNu = -10.f; float chi2TPCHad = -10.f; float nSigmaNu = -10.f; float nSigmaHad = -10.f; + float nSigmaTOFNu = -10.f; + float nSigmaITSNu = -10.f; + float nSigmaTOFHad = -10.f; + float nSigmaITSHad = -10.f; float nSigmaTPCHadPi = -10.f; float nSigmaTPCHadKa = -10.f; float nSigmaTPCHadPr = -10.f; @@ -162,6 +166,8 @@ struct HadNucandidate { int trackIDNu = -1; int trackIDHad = -1; + float deltaEta = -99.f; + float deltaPhi = -99.f; float kstar = 1.f; float mT = 1.f; @@ -247,6 +253,31 @@ struct HadNucleiFemto { Configurable settingUseProtonMassForKstarMt{"settingUseProtonMassForKstarMt", false, "If true, use proton mass instead of deuteron mass for kstar and mT"}; } deuteronPid; + struct : o2::framework::ConfigurableGroup { + // cppcheck-suppress unusedStructMember + std::string prefix{"helium3Pid"}; + Configurable settingRigidityMinHe3{"settingRigidityMinHe3", 0.8f, "Minimum He3 TPC rigidity"}; + Configurable settingTPCNClsFoundMinHe3{"settingTPCNClsFoundMinHe3", 110, "Minimum found TPC clusters for He3"}; + Configurable settingTPCCrossedRowsMinHe3{"settingTPCCrossedRowsMinHe3", 70, "Minimum crossed TPC rows for He3"}; + Configurable settingTPCNSigmaMaxHe3{"settingTPCNSigmaMaxHe3", 3.f, "Maximum absolute TPC n-sigma for He3"}; + Configurable settingITSNSigmaMinHe3{"settingITSNSigmaMinHe3", -1.5f, "Minimum ITS n-sigma for He3"}; + } helium3Pid; + + struct : o2::framework::ConfigurableGroup { + // cppcheck-suppress unusedStructMember + std::string prefix{"tritonPid"}; + // Triton track-quality, purity and PID cuts + Configurable settingPIDMomentumSplitTr{"settingPIDMomentumSplitTr", 2.f, "Momentum separating low- and high-momentum triton TPC PID"}; + Configurable settingCutTPCNsigmaLowPTr{"settingCutTPCNsigmaLowPTr", 3.f, "Maximum absolute TPC n-sigma for tritons below the momentum split"}; + Configurable settingCutITSNsigmaLowPTr{"settingCutITSNsigmaLowPTr", 3.f, "Maximum absolute ITS n-sigma for tritons below the momentum split"}; + Configurable settingCutTPCNsigmaHighPMinTr{"settingCutTPCNsigmaHighPMinTr", -2.f, "Minimum TPC n-sigma for tritons above the momentum split"}; + Configurable settingCutTPCNsigmaHighPMaxTr{"settingCutTPCNsigmaHighPMaxTr", 3.f, "Maximum TPC n-sigma for tritons above the momentum split"}; + Configurable settingTOFMassMomentumMinTr{"settingTOFMassMomentumMinTr", 1.2f, "Minimum momentum to apply the triton TOF mass cut"}; + Configurable settingTOFMassMinTr{"settingTOFMassMinTr", 2.5f, "Minimum triton TOF mass"}; + Configurable settingTOFMassMaxTr{"settingTOFMassMaxTr", 3.4f, "Maximum triton TOF mass"}; + Configurable settingTPCRejectNsig{"settingTPCRejectNsig", 3.f, "Minimum absolute TPC n-sigma from deuteron, proton and pion hypotheses"}; + } tritonPid; + struct : o2::framework::ConfigurableGroup { // cppcheck-suppress unusedStructMember std::string prefix{"CPR"}; @@ -271,8 +302,9 @@ struct HadNucleiFemto { std::string prefix{"output"}; // Output and QA controls Configurable settingFillTable{"settingFillTable", false, "Enable table filling"}; - Configurable settingFillTableLowKstarOnly{"settingFillTableLowKstarOnly", false, "If true, fill the output table only for pion-deuteron pairs below the kstar threshold"}; + Configurable settingFillTableLowKstarOnly{"settingFillTableLowKstarOnly", false, "If true, apply the configured low-kstar threshold to pion-deuteron and pion-triton pairs"}; Configurable settingFillTablePiDeKstarMax{"settingFillTablePiDeKstarMax", 0.1f, "Maximum kstar for pion-deuteron pairs written to the output table"}; + Configurable settingFillTablePiTrKstarMax{"settingFillTablePiTrKstarMax", 0.1f, "Maximum kstar for pion-triton pairs written to the output table"}; Configurable settingFillMultiplicity{"settingFillMultiplicity", false, "Fill multiplicity table"}; Configurable settingUseBBcomputeDeNsigma{"settingUseBBcomputeDeNsigma", false, "Use BB params to compute De TPC Nsigma"}; } output; @@ -380,6 +412,7 @@ struct HadNucleiFemto { {"h2NSigmaNuITS", "NsigmaNu ITS distribution; signed #it{p}_{T} (GeV/#it{c}); n#sigma_{ITS} Nu", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {120, -3.0f, 3.0f}}}}, {"h2NsigmaNuTOF", "NsigmaNu TOF distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{TOF}(Nu)", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {200, -5.0f, 5.0f}}}}, {"h2NsigmaNuTOF_preselection", "NsigmaNu TOF distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{TOF}(Nu)", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {400, -10.0f, 10.0f}}}}, + {"h2MassTOFTr", "Triton TOF mass; signed #it{p} (GeV/#it{c}); m_{TOF} (GeV/#it{c}^{2})", {HistType::kTH2F, {{240, -6.0f, 6.0f}, {200, 0.0f, 5.0f}}}}, // Hadron PID {"h2NsigmaHadComb", "NsigmaHad TPCTOF comb distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{comb}(had)", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {100, 0.0f, 5.0f}}}}, @@ -451,12 +484,14 @@ struct HadNucleiFemto { int mat{static_cast(pidCalibration.settingMaterialCorrection)}; mFitter.setMatCorrType(static_cast(mat)); - const int numParticles = 5; - const char* betheBlochLabel = nucleusBetheBlochLabel(); - for (int i = 0; i < numParticles; i++) { - mBBparamsNucleus[i] = pidCalibration.settingBetheBlochParams->get(betheBlochLabel, Form("p%i", i)); + if (!useTritonNucleus()) { + const int numParticles = 5; + const char* betheBlochLabel = nucleusBetheBlochLabel(); + for (int i = 0; i < numParticles; i++) { + mBBparamsNucleus[i] = pidCalibration.settingBetheBlochParams->get(betheBlochLabel, Form("p%i", i)); + } + mBBparamsNucleus[5] = pidCalibration.settingBetheBlochParams->get(betheBlochLabel, "resolution"); } - mBBparamsNucleus[5] = pidCalibration.settingBetheBlochParams->get(betheBlochLabel, "resolution"); std::vector selectionLabels = {"All", "Track selection", "PID"}; for (int i = 0; i < Selections::kAll; i++) { @@ -476,7 +511,7 @@ struct HadNucleiFemto { return; } if (zorro.settingSkimmedProcessing) { - mZorro.initCCDB(mCcdb.service, bc.runNumber(), bc.timestamp(), useHelium3Nucleus() ? "fHe" : "fDe"); + mZorro.initCCDB(mCcdb.service, bc.runNumber(), bc.timestamp(), zorroTriggerMask()); mZorro.populateHistRegistry(mQaRegistry, bc.runNumber()); } mRunNumber = bc.runNumber(); @@ -486,7 +521,7 @@ struct HadNucleiFemto { o2::parameters::GRPMagField* grpmag = nullptr; if (grpo) { o2::base::Propagator::initFieldFromGRP(grpo); - if (ccdb.settingDbz < defaultBzValue) { + if (ccdb.settingDbz <= defaultBzValue) { // Fetch magnetic field from ccdb for current collision mDbz = grpo->getNominalL3Field(); LOG(info) << "Retrieved GRP for timestamp " << run3GrpTimestamp << " with magnetic field of " << mDbz << " kZG"; @@ -499,7 +534,7 @@ struct HadNucleiFemto { LOG(fatal) << "Got nullptr from CCDB for path " << ccdb.settingGrpmagPath << " of object GRPMagField and " << ccdb.settingGrpPath << " of object GRPObject for timestamp " << run3GrpTimestamp; } o2::base::Propagator::initFieldFromGRP(grpmag); - if (ccdb.settingDbz < defaultBzValue) { + if (ccdb.settingDbz <= defaultBzValue) { // Fetch magnetic field from ccdb for current collision mDbz = std::lround(5.f * grpmag->getL3Current() / 30000.f); LOG(info) << "Retrieved GRP for timestamp " << run3GrpTimestamp << " with magnetic field of " << mDbz << " kZG"; @@ -528,10 +563,11 @@ struct HadNucleiFemto { return false; } if (zorro.settingSkimmedProcessing) { - bool zorroSelected = mZorro.isSelected(collision.template bc_as().globalBC()); - if (zorroSelected) { - mQaRegistry.fill(HIST("hEvents"), 2); + const bool zorroSelected = mZorro.isSelected(collision.template bc_as().globalBC()); + if (!zorroSelected) { + return false; } + mQaRegistry.fill(HIST("hEvents"), 2); } } @@ -649,16 +685,42 @@ struct HadNucleiFemto { candidate.itsNClsInnerBarrel() < minITSNClsInnerBarrel); } + template + bool selectTrackTr(const Ttrack& candidate) + { + return !(std::abs(candidate.eta()) >= 0.8 || + std::abs(candidate.dcaXY()) >= 0.2 || + std::abs(candidate.dcaZ()) >= 0.2 || + candidate.tpcNClsCrossedRows() < 70 || + candidate.tpcChi2NCl() >= 5 || + candidate.tpcFractionSharedCls() >= 0.3 || + candidate.itsNCls() < 5 || + candidate.itsChi2NCl() >= 10); + } + bool useDeuteronNucleus() const { return species.settingNuPDGCode.value == DeuteronPDG; } + bool useTritonNucleus() const + { + return species.settingNuPDGCode.value == TritonPDG; + } + bool useHelium3Nucleus() const { return species.settingNuPDGCode.value == He3PDG; } + const char* zorroTriggerMask() const + { + if (useTritonNucleus()) { + return "fTritonFemto"; + } + return useHelium3Nucleus() ? "fHe" : "fDe"; + } + const char* nucleusBetheBlochLabel() const { return useHelium3Nucleus() ? "He3" : "De"; @@ -674,6 +736,9 @@ struct HadNucleiFemto { if (useHelium3Nucleus()) { return static_cast(o2::constants::physics::MassHelium3); } + if (useTritonNucleus()) { + return static_cast(o2::constants::physics::MassTriton); + } return static_cast(o2::constants::physics::MassDeuteron); } @@ -684,11 +749,10 @@ struct HadNucleiFemto { return false; } - constexpr int minTPCNClsCrossedRows = 70; constexpr float crossedRowsToFindableRatio = 0.8f; return !(candidate.itsNCls() < trackCut.settingCutNCls || - candidate.tpcNClsFound() < He3TPCNClsFoundMin || - candidate.tpcNClsCrossedRows() < minTPCNClsCrossedRows || + candidate.tpcNClsFound() < helium3Pid.settingTPCNClsFoundMinHe3 || + candidate.tpcNClsCrossedRows() < helium3Pid.settingTPCCrossedRowsMinHe3 || candidate.tpcNClsCrossedRows() < crossedRowsToFindableRatio * candidate.tpcNClsFindable() || candidate.tpcChi2NCl() > trackCut.settingCutChi2tpcHigh || candidate.tpcChi2NCl() < He3TPCChi2NClMin || @@ -701,6 +765,9 @@ struct HadNucleiFemto { if (useHelium3Nucleus()) { return selectTrackHe3(candidate); } + if (useTritonNucleus()) { + return selectTrackTr(candidate); + } if (useDeuteronNucleus()) { return selectTrackDe(candidate); } @@ -723,12 +790,23 @@ struct HadNucleiFemto { return species.settingHadPDGCode.value == static_cast(PDG_t::kPiPlus) && useDeuteronNucleus(); } + bool isPionTritonPair() const + { + return species.settingHadPDGCode.value == static_cast(PDG_t::kPiPlus) && useTritonNucleus(); + } + bool shouldFillOutputTable(const HadNucandidate& hadNucand) const { - if (!output.settingFillTableLowKstarOnly.value || !isPionDeuteronPair()) { + if (!output.settingFillTableLowKstarOnly.value) { return true; } - return hadNucand.kstar < output.settingFillTablePiDeKstarMax.value; + if (isPionDeuteronPair()) { + return hadNucand.kstar < output.settingFillTablePiDeKstarMax.value; + } + if (isPionTritonPair()) { + return hadNucand.kstar < output.settingFillTablePiTrKstarMax.value; + } + return true; } template @@ -772,40 +850,62 @@ struct HadNucleiFemto { } template - bool isClosePair(const Ttrack1& firstTrack, const Ttrack2& secondTrack) + bool computeClosePairDeltas(const Ttrack1& firstTrack, const Ttrack2& secondTrack, float& deltaEta, float& deltaPhi) const { constexpr int closePairRadiusModePv = 0; constexpr int closePairRadiusModeSpecificTpc = 2; constexpr float invalidPhiStar = 999.f; + constexpr float invalidOutputDelta = -99.f; + + deltaEta = invalidOutputDelta; + deltaPhi = invalidOutputDelta; if (!CPR.settingEnableClosePairRejection.value) { return false; } - if (firstTrack.sign() != secondTrack.sign()) { + + float selectedDeltaPhi = averagePhiStar(firstTrack, secondTrack); + if (CPR.settingClosePairRadiusMode.value == closePairRadiusModePv) { + selectedDeltaPhi = wrapDeltaPhi(firstTrack.phi() - secondTrack.phi()); + } else if (CPR.settingClosePairRadiusMode.value == closePairRadiusModeSpecificTpc) { + const float firstPhi = phiAtSpecificRadiiTPC(firstTrack, CPR.settingClosePairSpecificRadius.value); + const float secondPhi = phiAtSpecificRadiiTPC(secondTrack, CPR.settingClosePairSpecificRadius.value); + if (firstPhi == invalidPhiStar || secondPhi == invalidPhiStar) { + return false; + } + selectedDeltaPhi = wrapDeltaPhi(firstPhi - secondPhi); + } + + if (selectedDeltaPhi == invalidPhiStar) { return false; } - const float deta = firstTrack.eta() - secondTrack.eta(); - const float dphiAtPV = wrapDeltaPhi(firstTrack.phi() - secondTrack.phi()); - const float dphiAtSpecificRadius = wrapDeltaPhi(phiAtSpecificRadiiTPC(firstTrack, CPR.settingClosePairSpecificRadius.value) - phiAtSpecificRadiiTPC(secondTrack, CPR.settingClosePairSpecificRadius.value)); - const float dphiAvg = averagePhiStar(firstTrack, secondTrack); + deltaEta = firstTrack.eta() - secondTrack.eta(); + deltaPhi = selectedDeltaPhi; + return true; + } - float dphiToCut = dphiAvg; - if (CPR.settingClosePairRadiusMode.value == closePairRadiusModePv) { - dphiToCut = dphiAtPV; - } else if (CPR.settingClosePairRadiusMode.value == closePairRadiusModeSpecificTpc) { - dphiToCut = dphiAtSpecificRadius; + template + bool isClosePair(const Ttrack1& firstTrack, const Ttrack2& secondTrack) + { + if (!CPR.settingEnableClosePairRejection.value) { + return false; + } + if (firstTrack.sign() != secondTrack.sign()) { + return false; } - if (dphiToCut == invalidPhiStar) { + float deltaEta = -99.f; + float deltaPhi = -99.f; + if (!computeClosePairDeltas(firstTrack, secondTrack, deltaEta, deltaPhi)) { return false; } - mQaRegistry.fill(HIST("h2CPRBefore"), deta, dphiToCut); - const bool isRejected = std::pow(dphiToCut, 2.f) / std::pow(CPR.settingClosePairDeltaPhiMax.value, 2.f) + - std::pow(deta, 2.f) / std::pow(CPR.settingClosePairDeltaEtaMax.value, 2.f) < + mQaRegistry.fill(HIST("h2CPRBefore"), deltaEta, deltaPhi); + const bool isRejected = std::pow(deltaPhi, 2.f) / std::pow(CPR.settingClosePairDeltaPhiMax.value, 2.f) + + std::pow(deltaEta, 2.f) / std::pow(CPR.settingClosePairDeltaEtaMax.value, 2.f) < 1.f; if (!isRejected) { - mQaRegistry.fill(HIST("h2CPRAfter"), deta, dphiToCut); + mQaRegistry.fill(HIST("h2CPRAfter"), deltaEta, deltaPhi); } return isRejected; } @@ -989,6 +1089,37 @@ struct HadNucleiFemto { return -10.f; } + template + float getHadronTOFNSigma(const Ttrack& candidate) const + { + if (species.settingHadPDGCode.value == static_cast(PDG_t::kPiPlus)) { + return candidate.tofNSigmaPi(); + } + if (species.settingHadPDGCode.value == static_cast(PDG_t::kKPlus)) { + return candidate.tofNSigmaKa(); + } + if (species.settingHadPDGCode.value == static_cast(PDG_t::kProton)) { + return candidate.tofNSigmaPr(); + } + return -10.f; + } + + template + float getHadronITSNSigma(const Ttrack& candidate) const + { + o2::aod::ITSResponse itsResponse; + if (species.settingHadPDGCode.value == static_cast(PDG_t::kPiPlus)) { + return itsResponse.nSigmaITS(candidate.itsClusterSizes(), candidate.p(), candidate.eta()); + } + if (species.settingHadPDGCode.value == static_cast(PDG_t::kKPlus)) { + return itsResponse.nSigmaITS(candidate.itsClusterSizes(), candidate.p(), candidate.eta()); + } + if (species.settingHadPDGCode.value == static_cast(PDG_t::kProton)) { + return itsResponse.nSigmaITS(candidate.itsClusterSizes(), candidate.p(), candidate.eta()); + } + return -10.f; + } + template float computeNSigmaDe(const Ttrack& candidate) { @@ -1085,20 +1216,20 @@ struct HadNucleiFemto { const float correctedTPCinnerParam = correctedTPCInnerParamHe3(candidate); mQaRegistry.fill(HIST("h2dEdx"), candidate.sign() * correctedTPCinnerParam, candidate.tpcSignal()); - if (correctedTPCinnerParam < He3RigidityMin) { + if (correctedTPCinnerParam < helium3Pid.settingRigidityMinHe3) { return false; } const float nSigmaHe3 = computeNSigmaHe3(candidate); mQaRegistry.fill(HIST("h2NsigmaNuTPC_preselection"), candidate.sign() * 2.f * candidate.pt(), nSigmaHe3); - if (std::abs(nSigmaHe3) > He3TPCNSigmaMax) { + if (std::abs(nSigmaHe3) > helium3Pid.settingTPCNSigmaMaxHe3) { return false; } o2::aod::ITSResponse itsResponse; - const float itsNsigmaHe3 = itsResponse.nSigmaITS(candidate.itsClusterSizes(), 2.f * candidate.p(), candidate.eta()); + const float itsNsigmaHe3 = itsResponse.nSigmaITS(candidate.itsClusterSizes(), candidate.p(), candidate.eta()); mQaRegistry.fill(HIST("h2NSigmaNuITS_preselection"), candidate.sign() * 2.f * candidate.pt(), itsNsigmaHe3); - if (itsNsigmaHe3 < He3ITSNSigmaMin) { + if (itsNsigmaHe3 < helium3Pid.settingITSNSigmaMinHe3) { return false; } @@ -1108,12 +1239,71 @@ struct HadNucleiFemto { return true; } + template + float computeTOFMass(const Ttrack& candidate) const + { + if (!candidate.hasTOF()) { + return -1.f; + } + float beta = o2::pid::tof::Beta::GetBeta(candidate); + beta = std::clamp(beta, 1.e-4f, 1.f - 1.e-6f); + return std::abs(candidate.tpcInnerParam()) * std::sqrt(1.f / (beta * beta) - 1.f); + } + + template + bool selectionPIDTr(const Ttrack& candidate) + { + const float tpcNSigmaTr = candidate.tpcNSigmaTr(); + const float absP = std::abs(candidate.p()); + o2::aod::ITSResponse itsResponse; + const float itsNSigmaTr = itsResponse.nSigmaITS(candidate.itsClusterSizes(), absP, candidate.eta()); + + mQaRegistry.fill(HIST("h2dEdx"), candidate.sign() * candidate.tpcInnerParam(), candidate.tpcSignal()); + mQaRegistry.fill(HIST("h2NsigmaNuTPC_preselection"), candidate.sign() * candidate.pt(), tpcNSigmaTr); + mQaRegistry.fill(HIST("h2NSigmaNuITS_preselection"), candidate.sign() * candidate.pt(), itsNSigmaTr); + + if (std::abs(candidate.tpcNSigmaDe()) < tritonPid.settingTPCRejectNsig || + std::abs(candidate.tpcNSigmaPr()) < tritonPid.settingTPCRejectNsig || + std::abs(candidate.tpcNSigmaPi()) < tritonPid.settingTPCRejectNsig) { + return false; + } + + if (absP < tritonPid.settingPIDMomentumSplitTr) { + if (std::abs(tpcNSigmaTr) >= tritonPid.settingCutTPCNsigmaLowPTr || + std::abs(itsNSigmaTr) >= tritonPid.settingCutITSNsigmaLowPTr) { + return false; + } + } else if (tpcNSigmaTr <= tritonPid.settingCutTPCNsigmaHighPMinTr || + tpcNSigmaTr >= tritonPid.settingCutTPCNsigmaHighPMaxTr) { + return false; + } + + if (absP > tritonPid.settingTOFMassMomentumMinTr) { + if (!candidate.hasTOF()) { + return false; + } + const float massTOFTr = computeTOFMass(candidate); + mQaRegistry.fill(HIST("h2MassTOFTr"), candidate.sign() * absP, massTOFTr); + if (massTOFTr <= tritonPid.settingTOFMassMinTr || massTOFTr >= tritonPid.settingTOFMassMaxTr) { + return false; + } + } + + mQaRegistry.fill(HIST("h2dEdxNucandidates"), candidate.sign() * candidate.tpcInnerParam(), candidate.tpcSignal()); + mQaRegistry.fill(HIST("h2NsigmaNuTPC"), candidate.sign() * candidate.pt(), tpcNSigmaTr); + mQaRegistry.fill(HIST("h2NSigmaNuITS"), candidate.sign() * candidate.pt(), itsNSigmaTr); + return true; + } + template bool selectionPIDNu(const Ttrack& candidate) { if (useHelium3Nucleus()) { return selectionPIDHe3(candidate); } + if (useTritonNucleus()) { + return selectionPIDTr(candidate); + } if (useDeuteronNucleus()) { return selectionPIDDe(candidate); } @@ -1126,9 +1316,43 @@ struct HadNucleiFemto { if (useHelium3Nucleus()) { return computeNSigmaHe3(candidate); } + if (useTritonNucleus()) { + return candidate.tpcNSigmaTr(); + } return computeNSigmaDe(candidate); } + template + float getNucleusTOFNSigma(const Ttrack& candidate) const + { + if (useHelium3Nucleus()) { + return candidate.tofNSigmaHe(); + } + if (useTritonNucleus()) { + return candidate.tofNSigmaTr(); + } + if (useDeuteronNucleus()) { + return candidate.tofNSigmaDe(); + } + return -10.f; + } + + template + float getNucleusITSNSigma(const Ttrack& candidate) const + { + o2::aod::ITSResponse itsResponse; + if (useHelium3Nucleus()) { + return itsResponse.nSigmaITS(candidate.itsClusterSizes(), candidate.p(), candidate.eta()); + } + if (useTritonNucleus()) { + return itsResponse.nSigmaITS(candidate.itsClusterSizes(), candidate.p(), candidate.eta()); + } + if (useDeuteronNucleus()) { + return itsResponse.nSigmaITS(candidate.itsClusterSizes(), candidate.p(), candidate.eta()); + } + return -10.f; + } + float averageClusterSizeCosl(uint32_t itsClusterSizes, float eta) { float average = 0; @@ -1256,6 +1480,7 @@ struct HadNucleiFemto { hadNucand.signNu = trackDe.sign(); hadNucand.signHad = trackHad.sign(); + computeClosePairDeltas(trackDe, trackHad, hadNucand.deltaEta, hadNucand.deltaPhi); hadNucand.dcaxyNu = trackDe.dcaXY(); hadNucand.dcaxyHad = trackHad.dcaXY(); @@ -1269,8 +1494,15 @@ struct HadNucleiFemto { hadNucand.momHadTPC = trackHad.tpcInnerParam(); hadNucand.nTPCClustersNu = trackDe.tpcNClsFound(); + hadNucand.nTPCClustersHad = trackHad.tpcNClsFound(); + hadNucand.nTPCCrossedRowsNu = trackDe.tpcNClsCrossedRows(); + hadNucand.nTPCCrossedRowsHad = trackHad.tpcNClsCrossedRows(); hadNucand.nSigmaNu = getNucleusTPCNSigma(trackDe); hadNucand.nSigmaHad = getHadronTPCNSigma(trackHad); + hadNucand.nSigmaTOFNu = getNucleusTOFNSigma(trackDe); + hadNucand.nSigmaITSNu = getNucleusITSNSigma(trackDe); + hadNucand.nSigmaTOFHad = getHadronTOFNSigma(trackHad); + hadNucand.nSigmaITSHad = getHadronITSNSigma(trackHad); hadNucand.nSigmaTPCHadPi = trackHad.tpcNSigmaPi(); hadNucand.nSigmaTPCHadKa = trackHad.tpcNSigmaKa(); hadNucand.nSigmaTPCHadPr = trackHad.tpcNSigmaPr(); @@ -1312,7 +1544,10 @@ struct HadNucleiFemto { hadNucand.massTOFHad = trackHad.tpcInnerParam() * std::sqrt(1.f / (beta * beta) - 1.f); } - const float massLightNucleusForKstarMt = useHelium3Nucleus() ? static_cast(o2::constants::physics::MassHelium3) : (deuteronPid.settingUseProtonMassForKstarMt ? static_cast(o2::constants::physics::MassProton) : static_cast(o2::constants::physics::MassDeuteron)); + float massLightNucleusForKstarMt = nucleusMass(); + if (useDeuteronNucleus() && deuteronPid.settingUseProtonMassForKstarMt) { + massLightNucleusForKstarMt = static_cast(o2::constants::physics::MassProton); + } hadNucand.kstar = computePairKstar(hadNucand.momHad, mMassHad, hadNucand.momNu, massLightNucleusForKstarMt); hadNucand.mT = computePairMT(hadNucand.momHad, mMassHad, hadNucand.momNu, massLightNucleusForKstarMt); @@ -1530,6 +1765,9 @@ struct HadNucleiFemto { hadNucand.tpcSignalHad, hadNucand.momHadTPC, hadNucand.nTPCClustersNu, + hadNucand.nTPCClustersHad, + hadNucand.nTPCCrossedRowsNu, + hadNucand.nTPCCrossedRowsHad, hadNucand.nSigmaNu, hadNucand.nSigmaTPCHadPi, hadNucand.nSigmaTPCHadKa, @@ -1546,7 +1784,14 @@ struct HadNucleiFemto { hadNucand.itsClSizeNu, hadNucand.itsClSizeHad, hadNucand.sharedClustersNu, - hadNucand.sharedClustersHad); + hadNucand.sharedClustersHad, + hadNucand.deltaEta, + hadNucand.deltaPhi, + hadNucand.nSigmaHad, + hadNucand.nSigmaTOFNu, + hadNucand.nSigmaITSNu, + hadNucand.nSigmaTOFHad, + hadNucand.nSigmaITSHad); if (output.settingFillMultiplicity) { mOutputMultiplicityTable( collision.globalIndex(), @@ -1863,8 +2108,14 @@ struct HadNucleiFemto { const float signedPtHe3 = track.sign() * 2.f * track.pt(); mQaRegistry.fill(HIST("purity/h2NsigmaNuTPC_preselection"), signedPtHe3, tpcNSigmaHe3); o2::aod::ITSResponse itsResponse; - const float itsNSigmaHe3 = itsResponse.nSigmaITS(track.itsClusterSizes(), 2.f * track.p(), track.eta()); + const float itsNSigmaHe3 = itsResponse.nSigmaITS(track.itsClusterSizes(), track.p(), track.eta()); mQaRegistry.fill(HIST("purity/h2NSigmaNuITS_preselection"), signedPtHe3, itsNSigmaHe3); + } else if (passTrackNu && useTritonNucleus()) { + const float tpcNSigmaTr = track.tpcNSigmaTr(); + o2::aod::ITSResponse itsResponse; + const float itsNSigmaTr = itsResponse.nSigmaITS(track.itsClusterSizes(), std::abs(track.p()), track.eta()); + mQaRegistry.fill(HIST("purity/h2NsigmaNuTPC_preselection"), track.sign() * track.pt(), tpcNSigmaTr); + mQaRegistry.fill(HIST("purity/h2NSigmaNuITS_preselection"), track.sign() * track.pt(), itsNSigmaTr); } const bool isHadronSelected = passTrackHad && selectionPIDHadron(track); From a6605c0b0a71073790ad13bb1e70bc6d7a0e99c8 Mon Sep 17 00:00:00 2001 From: Meiyi Chen Date: Thu, 30 Jul 2026 15:11:38 +0800 Subject: [PATCH 07/15] Update HadronNucleiTables.h --- .../DataModel/HadronNucleiTables.h | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/PWGCF/Femto/FemtoNuclei/DataModel/HadronNucleiTables.h b/PWGCF/Femto/FemtoNuclei/DataModel/HadronNucleiTables.h index ce8e83575fe..30fd0d1c610 100644 --- a/PWGCF/Femto/FemtoNuclei/DataModel/HadronNucleiTables.h +++ b/PWGCF/Femto/FemtoNuclei/DataModel/HadronNucleiTables.h @@ -50,8 +50,15 @@ DECLARE_SOA_COLUMN(InnerParamTPCNu, innerParamTPCNu, float); DECLARE_SOA_COLUMN(SignalTPCHad, signalTPCHad, float); DECLARE_SOA_COLUMN(InnerParamTPCHad, innerParamTPCHad, float); DECLARE_SOA_COLUMN(NClsTPCNu, nClsTPCNu, uint8_t); +DECLARE_SOA_COLUMN(NClsTPCHad, nClsTPCHad, uint8_t); +DECLARE_SOA_COLUMN(NCrossedRowsTPCNu, nCrossedRowsTPCNu, uint8_t); +DECLARE_SOA_COLUMN(NCrossedRowsTPCHad, nCrossedRowsTPCHad, uint8_t); DECLARE_SOA_COLUMN(NSigmaTPCNu, nSigmaTPCNu, float); DECLARE_SOA_COLUMN(NSigmaTPCHad, nSigmaTPCHad, float); +DECLARE_SOA_COLUMN(NSigmaTOFNu, nSigmaTOFNu, float); +DECLARE_SOA_COLUMN(NSigmaITSNu, nSigmaITSNu, float); +DECLARE_SOA_COLUMN(NSigmaTOFHad, nSigmaTOFHad, float); +DECLARE_SOA_COLUMN(NSigmaITSHad, nSigmaITSHad, float); DECLARE_SOA_COLUMN(NSigmaTPCHadPi, nSigmaTPCHadPi, float); DECLARE_SOA_COLUMN(NSigmaTPCHadKa, nSigmaTPCHadKa, float); DECLARE_SOA_COLUMN(NSigmaTPCHadPr, nSigmaTPCHadPr, float); @@ -73,6 +80,9 @@ DECLARE_SOA_COLUMN(ItsClusterSizeHad, itsClusterSizeHad, uint32_t); DECLARE_SOA_COLUMN(SharedClustersNu, sharedClustersNu, uint8_t); DECLARE_SOA_COLUMN(SharedClustersHad, sharedClustersHad, uint8_t); +DECLARE_SOA_COLUMN(DeltaEta, deltaEta, float); +DECLARE_SOA_COLUMN(DeltaPhi, deltaPhi, float); + DECLARE_SOA_COLUMN(IsBkgUS, isBkgUS, bool); DECLARE_SOA_COLUMN(IsBkgEM, isBkgEM, bool); @@ -101,6 +111,9 @@ DECLARE_SOA_TABLE(HadronNucleiTable, "AOD", "HADNUCLEITABLE", hadron_nuclei_tables::SignalTPCHad, hadron_nuclei_tables::InnerParamTPCHad, hadron_nuclei_tables::NClsTPCNu, + hadron_nuclei_tables::NClsTPCHad, + hadron_nuclei_tables::NCrossedRowsTPCNu, + hadron_nuclei_tables::NCrossedRowsTPCHad, hadron_nuclei_tables::NSigmaTPCNu, hadron_nuclei_tables::NSigmaTPCHadPi, hadron_nuclei_tables::NSigmaTPCHadKa, @@ -117,7 +130,14 @@ DECLARE_SOA_TABLE(HadronNucleiTable, "AOD", "HADNUCLEITABLE", hadron_nuclei_tables::ItsClusterSizeNu, hadron_nuclei_tables::ItsClusterSizeHad, hadron_nuclei_tables::SharedClustersNu, - hadron_nuclei_tables::SharedClustersHad) + hadron_nuclei_tables::SharedClustersHad, + hadron_nuclei_tables::DeltaEta, + hadron_nuclei_tables::DeltaPhi, + hadron_nuclei_tables::NSigmaTPCHad, + hadron_nuclei_tables::NSigmaTOFNu, + hadron_nuclei_tables::NSigmaITSNu, + hadron_nuclei_tables::NSigmaTOFHad, + hadron_nuclei_tables::NSigmaITSHad) DECLARE_SOA_TABLE(HadronHyperTable, "AOD", "HADHYPERTABLE", hadron_nuclei_tables::PtHyp, hadron_nuclei_tables::EtaHyp, From 2d75b85b7905bd05ead858e34762358d0c3fa9e1 Mon Sep 17 00:00:00 2001 From: Meiyi Chen Date: Thu, 30 Jul 2026 15:45:48 +0800 Subject: [PATCH 08/15] Update HadNucleiFemto.cxx --- .../TableProducer/HadNucleiFemto.cxx | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx b/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx index 6753a0ef66b..cf8af9a4e82 100644 --- a/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx +++ b/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx @@ -688,14 +688,23 @@ struct HadNucleiFemto { template bool selectTrackTr(const Ttrack& candidate) { - return !(std::abs(candidate.eta()) >= 0.8 || - std::abs(candidate.dcaXY()) >= 0.2 || - std::abs(candidate.dcaZ()) >= 0.2 || - candidate.tpcNClsCrossedRows() < 70 || - candidate.tpcChi2NCl() >= 5 || - candidate.tpcFractionSharedCls() >= 0.3 || - candidate.itsNCls() < 5 || - candidate.itsChi2NCl() >= 10); + constexpr float maxAbsEta = 0.8f; + constexpr float maxAbsDcaXY = 0.2f; + constexpr float maxAbsDcaZ = 0.2f; + constexpr int minTPCCrossedRows = 70; + constexpr float maxTPCChi2NCl = 5.f; + constexpr float maxTPCFractionSharedCls = 0.3f; + constexpr int minITSNCls = 5; + constexpr float maxITSChi2NCl = 10.f; + + return !(std::abs(candidate.eta()) >= maxAbsEta || + std::abs(candidate.dcaXY()) >= maxAbsDcaXY || + std::abs(candidate.dcaZ()) >= maxAbsDcaZ || + candidate.tpcNClsCrossedRows() < minTPCCrossedRows || + candidate.tpcChi2NCl() >= maxTPCChi2NCl || + candidate.tpcFractionSharedCls() >= maxTPCFractionSharedCls || + candidate.itsNCls() < minITSNCls || + candidate.itsChi2NCl() >= maxITSChi2NCl); } bool useDeuteronNucleus() const From a5db02894396572ddd06f8b2c5571c3f9f6a260b Mon Sep 17 00:00:00 2001 From: Meiyi Chen Date: Thu, 30 Jul 2026 15:46:35 +0800 Subject: [PATCH 09/15] Update PiDeFemtoSystematics.cxx --- PWGCF/Femto/FemtoNuclei/Tasks/PiDeFemtoSystematics.cxx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/PWGCF/Femto/FemtoNuclei/Tasks/PiDeFemtoSystematics.cxx b/PWGCF/Femto/FemtoNuclei/Tasks/PiDeFemtoSystematics.cxx index 20296613a12..848090f0100 100644 --- a/PWGCF/Femto/FemtoNuclei/Tasks/PiDeFemtoSystematics.cxx +++ b/PWGCF/Femto/FemtoNuclei/Tasks/PiDeFemtoSystematics.cxx @@ -6,9 +6,10 @@ // License v3 (GPL Version 3), copied verbatim in the file "COPYING". // // In applying this license CERN does not waive the privileges and immunities -// granted by this license. +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. -/// \file PiDeFemtoSystematics.cxx +/// \file piDeFemtoSystematics.cxx /// \brief Histogram-only pion-deuteron femtoscopy task for train subwagon cut variations /// /// The task intentionally produces no derived AOD tables. A train subwagon From 46b9d6fb0b6bd930f9caa17a2758bcaf283b3504 Mon Sep 17 00:00:00 2001 From: Meiyi Chen Date: Thu, 30 Jul 2026 15:47:08 +0800 Subject: [PATCH 10/15] Update CMakeLists.txt --- PWGCF/Femto/FemtoNuclei/Tasks/CMakeLists.txt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/PWGCF/Femto/FemtoNuclei/Tasks/CMakeLists.txt b/PWGCF/Femto/FemtoNuclei/Tasks/CMakeLists.txt index cd86c49795b..9709ae21578 100644 --- a/PWGCF/Femto/FemtoNuclei/Tasks/CMakeLists.txt +++ b/PWGCF/Femto/FemtoNuclei/Tasks/CMakeLists.txt @@ -6,9 +6,10 @@ # License v3 (GPL Version 3), copied verbatim in the file "COPYING". # # In applying this license CERN does not waive the privileges and immunities -# granted by this license. +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. -o2physics_add_dpl_workflow(pidefemtosystematics - SOURCES PiDeFemtoSystematics.cxx +o2physics_add_dpl_workflow(pi-de-femto-systematics + SOURCES piDeFemtoSystematics.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) From ec654866c7be25539e0f4b44f87cb82372b4944c Mon Sep 17 00:00:00 2001 From: Meiyi Chen Date: Thu, 30 Jul 2026 15:50:38 +0800 Subject: [PATCH 11/15] Rename PiDeFemtoSystematics.cxx to piDeFemtoSystematics.cxx --- .../Tasks/{PiDeFemtoSystematics.cxx => piDeFemtoSystematics.cxx} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename PWGCF/Femto/FemtoNuclei/Tasks/{PiDeFemtoSystematics.cxx => piDeFemtoSystematics.cxx} (100%) diff --git a/PWGCF/Femto/FemtoNuclei/Tasks/PiDeFemtoSystematics.cxx b/PWGCF/Femto/FemtoNuclei/Tasks/piDeFemtoSystematics.cxx similarity index 100% rename from PWGCF/Femto/FemtoNuclei/Tasks/PiDeFemtoSystematics.cxx rename to PWGCF/Femto/FemtoNuclei/Tasks/piDeFemtoSystematics.cxx From 112c9da2666a76a62957ee6e18b71721f66c6e50 Mon Sep 17 00:00:00 2001 From: Meiyi Chen Date: Thu, 30 Jul 2026 16:02:10 +0800 Subject: [PATCH 12/15] Update piDeFemtoSystematics.cxx --- PWGCF/Femto/FemtoNuclei/Tasks/piDeFemtoSystematics.cxx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/PWGCF/Femto/FemtoNuclei/Tasks/piDeFemtoSystematics.cxx b/PWGCF/Femto/FemtoNuclei/Tasks/piDeFemtoSystematics.cxx index 848090f0100..c34201c1265 100644 --- a/PWGCF/Femto/FemtoNuclei/Tasks/piDeFemtoSystematics.cxx +++ b/PWGCF/Femto/FemtoNuclei/Tasks/piDeFemtoSystematics.cxx @@ -102,12 +102,14 @@ enum PairChannel { struct PiDeFemtoSystematics { struct : ConfigurableGroup { + // cppcheck-suppress unusedStructMember std::string prefix{"event"}; Configurable vertexZMax{"vertexZMax", 10.f, "Maximum absolute collision z vertex"}; Configurable numberOfMixedEvents{"numberOfMixedEvents", 5, "Number of mixed events per event"}; } eventCuts; struct : ConfigurableGroup { + // cppcheck-suppress unusedStructMember std::string prefix{"pion"}; Configurable ptMin{"ptMin", 0.14f, "Minimum pion pT"}; Configurable ptMax{"ptMax", 4.0f, "Maximum pion pT"}; @@ -126,6 +128,7 @@ struct PiDeFemtoSystematics { } pionCuts; struct : ConfigurableGroup { + // cppcheck-suppress unusedStructMember std::string prefix{"deuteron"}; Configurable ptMin{"ptMin", 0.6f, "Minimum deuteron pT"}; Configurable ptMax{"ptMax", 2.0f, "Maximum deuteron pT"}; @@ -147,6 +150,7 @@ struct PiDeFemtoSystematics { } deuteronCuts; struct : ConfigurableGroup { + // cppcheck-suppress unusedStructMember std::string prefix{"pair"}; Configurable enableClosePairRejection{"enableClosePairRejection", true, "Enable pion-deuteron close-pair rejection"}; Configurable closePairDeltaEtaMax{"closePairDeltaEtaMax", 0.01f, "CPR ellipse delta-eta radius"}; @@ -156,17 +160,20 @@ struct PiDeFemtoSystematics { } pairCuts; struct : ConfigurableGroup { + // cppcheck-suppress unusedStructMember std::string prefix{"analysis"}; Configurable useBetheBlochDeuteronNsigma{"useBetheBlochDeuteronNsigma", false, "Compute deuteron TPC n-sigma from the configured Bethe-Bloch parameters"}; Configurable lowKstarYieldMax{"lowKstarYieldMax", 0.3f, "Upper kstar used for the SE-yield stability check"}; } analysisSettings; struct : ConfigurableGroup { + // cppcheck-suppress unusedStructMember std::string prefix{"zorro"}; Configurable skimmedProcessing{"skimmedProcessing", false, "Process a Zorro-selected deuteron skim"}; } zorroSettings; struct : ConfigurableGroup { + // cppcheck-suppress unusedStructMember std::string prefix{"ccdb"}; Configurable magneticField{"magneticField", -999., "Magnetic field; -999 reads it from CCDB"}; Configurable url{"url", "http://alice-ccdb.cern.ch", "CCDB URL"}; @@ -176,6 +183,7 @@ struct PiDeFemtoSystematics { } ccdbSettings; struct : ConfigurableGroup { + // cppcheck-suppress unusedStructMember std::string prefix{"pidCalibration"}; Configurable> betheBlochParameters{"betheBlochParameters", {BetheBlochDeDefault.data(), 1, 6, betheBlochParticleNames, betheBlochParameterNames}, From 45725c5b4a7bfd670c78375b90ade9c037fee5a8 Mon Sep 17 00:00:00 2001 From: Meiyi Chen Date: Thu, 30 Jul 2026 18:12:25 +0800 Subject: [PATCH 13/15] Update piDeFemtoSystematics.cxx --- .../FemtoNuclei/Tasks/piDeFemtoSystematics.cxx | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/PWGCF/Femto/FemtoNuclei/Tasks/piDeFemtoSystematics.cxx b/PWGCF/Femto/FemtoNuclei/Tasks/piDeFemtoSystematics.cxx index c34201c1265..0a9d4569181 100644 --- a/PWGCF/Femto/FemtoNuclei/Tasks/piDeFemtoSystematics.cxx +++ b/PWGCF/Femto/FemtoNuclei/Tasks/piDeFemtoSystematics.cxx @@ -47,17 +47,20 @@ #include #include #include +#include #include -#include +#include +#include #include #include +#include -#include #include #include -#include +#include #include +#include #include using namespace o2; @@ -205,7 +208,7 @@ struct PiDeFemtoSystematics { HistogramRegistry registry{"PiDeFemtoSystematics", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; - Service ccdb; + Service ccdb{}; Zorro zorro; OutputObj zorroSummary{"zorroSummary"}; o2::vertexing::DCAFitterN<2> pairFitter; @@ -376,7 +379,7 @@ struct PiDeFemtoSystematics { registry.add("Config/hCutValues", "Configured cut values;cut;value", HistType::kTH1D, {cutAxis}); auto histogram = registry.get(HIST("Config/hCutValues")); - for (size_t index = 0; index < cuts.size(); ++index) { + for (std::size_t index = 0; index < cuts.size(); ++index) { histogram->GetXaxis()->SetBinLabel(static_cast(index) + 1, cuts[index].first.c_str()); histogram->SetBinContent(static_cast(index) + 1, cuts[index].second); } @@ -761,7 +764,7 @@ struct PiDeFemtoSystematics { pion.px(), pion.py(), pion.pz()}; const float kstar = pairKstar(pionMomentum, deuteronMomentum); const float mt = pairMT(pionMomentum, deuteronMomentum); - const float channel = static_cast( + const auto channel = static_cast( pairChannel(deuteron.sign(), pion.sign())); if (mixedEvent) { registry.fill(HIST("Pairs/hME"), kstar, mt, centrality, channel); From c2a9673f8f5640ace6cb0cc36331a2ae345ab4fe Mon Sep 17 00:00:00 2001 From: Meiyi Chen Date: Thu, 30 Jul 2026 19:44:25 +0800 Subject: [PATCH 14/15] Update piDeFemtoSystematics.cxx --- PWGCF/Femto/FemtoNuclei/Tasks/piDeFemtoSystematics.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PWGCF/Femto/FemtoNuclei/Tasks/piDeFemtoSystematics.cxx b/PWGCF/Femto/FemtoNuclei/Tasks/piDeFemtoSystematics.cxx index 0a9d4569181..c12af02d7d2 100644 --- a/PWGCF/Femto/FemtoNuclei/Tasks/piDeFemtoSystematics.cxx +++ b/PWGCF/Femto/FemtoNuclei/Tasks/piDeFemtoSystematics.cxx @@ -229,9 +229,9 @@ struct PiDeFemtoSystematics { registry.add("Pairs/hMELowKstarYield", "ME low-kstar yield;pair channel;Entries", HistType::kTH1F, {channelAxis}); registry.add("Pairs/hCPRBefore", "CPR before selection;#Delta#eta;#Delta#varphi^{*}", - HistType::kTH2F, {{160, -2., 2.}, {160, -3.2, 3.2}}); + HistType::kTH2F, {{300, -0.15, 0.15}, {400, -0.2, 0.2}}); registry.add("Pairs/hCPRAfter", "CPR after selection;#Delta#eta;#Delta#varphi^{*}", - HistType::kTH2F, {{160, -2., 2.}, {160, -3.2, 3.2}}); + HistType::kTH2F, {{300, -0.15, 0.15}, {400, -0.2, 0.2}}); registry.add("Event/hCounter", "Event selection;step;Entries", HistType::kTH1F, {{3, -0.5, 2.5}}); registry.add("Event/hVertexZ", "Selected collision vertex;z (cm);Entries", From f52e7f61a6021f46dc47c31d92138d764170f8af Mon Sep 17 00:00:00 2001 From: Meiyi Chen Date: Thu, 30 Jul 2026 19:44:50 +0800 Subject: [PATCH 15/15] Update HadNucleiFemto.cxx --- PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx b/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx index cf8af9a4e82..374984b2188 100644 --- a/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx +++ b/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx @@ -395,8 +395,8 @@ struct HadNucleiFemto { {"hHadPin", "P distribution; #it{p} (GeV/#it{c})", {HistType::kTH1F, {{120, -4.0f, 4.0f}}}}, {"hHadEta", "eta distribution; #eta(had)", {HistType::kTH1F, {{200, -1.0f, 1.0f}}}}, {"hHadPhi", "phi distribution; phi(had)", {HistType::kTH1F, {{600, -4.0f, 4.0f}}}}, - {"h2CPRBefore", "Close pair rejection before cut; #Delta#eta; #Delta#phi^{*}", {HistType::kTH2F, {{160, -2.0f, 2.0f}, {160, -3.2f, 3.2f}}}}, - {"h2CPRAfter", "Close pair rejection after cut; #Delta#eta; #Delta#phi^{*}", {HistType::kTH2F, {{160, -2.0f, 2.0f}, {160, -3.2f, 3.2f}}}}, + {"h2CPRBefore", "Close pair rejection before cut; #Delta#eta; #Delta#phi^{*}", {HistType::kTH2F, {{300, -0.15f, 0.15f}, {400, -0.2f, 0.2f}}}}, + {"h2CPRAfter", "Close pair rejection after cut; #Delta#eta; #Delta#phi^{*}", {HistType::kTH2F, {{300, -0.15f, 0.15f}, {400, -0.2f, 0.2f}}}}, // dE/dx {"h2dEdxNucandidates", "dEdx distribution; #it{p} (GeV/#it{c}); dE/dx (a.u.)", {HistType::kTH2F, {{200, -5.0f, 5.0f}, {100, 0.0f, 2000.0f}}}},