From 449e066e1464dd5b3cfb29ada763cbe18b4b5b96 Mon Sep 17 00:00:00 2001 From: GijsvWeelden <55794847+GijsvWeelden@users.noreply.github.com> Date: Fri, 27 Sep 2024 17:30:11 +0200 Subject: [PATCH] [PWGLF & PWGJE] Create V0 Selector Task * Creates a table containing a flag denoting whether a V0 is possible signal or if it would be rejected by cuts made in the analysis * Allows filtering out V0s that would never be reconstructed as signal before passing them on to other table producing tasks (important for V0 jet studies) * pT-dependent cuts for K0S, Lambda, and LambdaBar separately * Optionally cut on invariant mass * Optionally apply competing mass cuts * Optionally downscale potential signal collection (important for JE checks) * [PWGJE] Added QA for V0 flags --- PWGJE/Tasks/v0qa.cxx | 33 +++ PWGLF/DataModel/V0SelectorTables.h | 55 ++++ .../TableProducer/Strangeness/CMakeLists.txt | 5 + .../TableProducer/Strangeness/v0selector.cxx | 260 ++++++++++++++++++ 4 files changed, 353 insertions(+) create mode 100644 PWGLF/DataModel/V0SelectorTables.h create mode 100644 PWGLF/TableProducer/Strangeness/v0selector.cxx diff --git a/PWGJE/Tasks/v0qa.cxx b/PWGJE/Tasks/v0qa.cxx index d8dffddcf5f..18f40bb2580 100644 --- a/PWGJE/Tasks/v0qa.cxx +++ b/PWGJE/Tasks/v0qa.cxx @@ -32,6 +32,7 @@ #include "PWGJE/Core/JetFinder.h" #include "PWGJE/Core/JetUtilities.h" #include "PWGJE/Core/JetFindingUtilities.h" +#include "PWGLF/DataModel/V0SelectorTables.h" using namespace o2; using namespace o2::framework; @@ -90,6 +91,9 @@ struct V0QA { const AxisSpec axisLambdaM{binInvMassLambda, "M(p #pi^{-}) (GeV/c^{2})"}; const AxisSpec axisAntiLambdaM{binInvMassLambda, "M(#bar{p} #pi^{+}) (GeV/c^{2})"}; + if (doprocessFlags) { + registry.add("inclusive/V0Flags", "V0Flags", HistType::kTH2D, {{4, -0.5, 3.5}, {4, -0.5, 3.5}}); + } if (doprocessMcD) { registry.add("inclusive/hEvents", "Events", {HistType::kTH1D, {{2, 0.0f, 2.0f}}}); registry.add("inclusive/K0SPtEtaMass", "K0S Pt, Eta, Mass", HistType::kTH3D, {axisV0Pt, axisEta, axisK0SM}); @@ -230,6 +234,35 @@ struct V0QA { void processDummy(CandidatesV0MCD const&) {} PROCESS_SWITCH(V0QA, processDummy, "Dummy process function turned on by default", true); + void processFlags(soa::Join::iterator const& v0) + { + int isK0S = static_cast(v0.isK0SCandidate()); + int isLambda = static_cast((v0.isLambdaCandidate())); + int isAntiLambda = static_cast(v0.isAntiLambdaCandidate()); + int isRejected = static_cast(v0.isRejectedCandidate()); + + registry.fill(HIST("inclusive/V0Flags"), 0, 0, isK0S); + registry.fill(HIST("inclusive/V0Flags"), 1, 1, isLambda); + registry.fill(HIST("inclusive/V0Flags"), 2, 2, isAntiLambda); + registry.fill(HIST("inclusive/V0Flags"), 3, 3, isRejected); + + registry.fill(HIST("inclusive/V0Flags"), 0, 1, isK0S * isLambda); + registry.fill(HIST("inclusive/V0Flags"), 1, 0, isK0S * isLambda); + registry.fill(HIST("inclusive/V0Flags"), 0, 2, isK0S * isAntiLambda); + registry.fill(HIST("inclusive/V0Flags"), 2, 0, isK0S * isAntiLambda); + registry.fill(HIST("inclusive/V0Flags"), 0, 3, isK0S * isRejected); + registry.fill(HIST("inclusive/V0Flags"), 3, 0, isK0S * isRejected); + + registry.fill(HIST("inclusive/V0Flags"), 1, 2, isLambda * isAntiLambda); + registry.fill(HIST("inclusive/V0Flags"), 2, 1, isLambda * isAntiLambda); + registry.fill(HIST("inclusive/V0Flags"), 1, 3, isLambda * isRejected); + registry.fill(HIST("inclusive/V0Flags"), 3, 1, isLambda * isRejected); + + registry.fill(HIST("inclusive/V0Flags"), 2, 3, isAntiLambda * isRejected); + registry.fill(HIST("inclusive/V0Flags"), 3, 2, isAntiLambda * isRejected); + } + PROCESS_SWITCH(V0QA, processFlags, "V0 flags", false); + void processMcD(soa::Filtered::iterator const& jcoll, JetMcCollisions const&, soa::Join const& v0s, aod::McParticles const&) { registry.fill(HIST("inclusive/hEvents"), 0.5); diff --git a/PWGLF/DataModel/V0SelectorTables.h b/PWGLF/DataModel/V0SelectorTables.h new file mode 100644 index 00000000000..5724187e59a --- /dev/null +++ b/PWGLF/DataModel/V0SelectorTables.h @@ -0,0 +1,55 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +#ifndef PWGLF_DATAMODEL_V0SELECTORTABLES_H +#define PWGLF_DATAMODEL_V0SELECTORTABLES_H + +namespace o2::aod +{ + +namespace v0flags +{ + +enum V0Flags : uint8_t { + FK0S = 0x1, // K0S candidate + FLAMBDA = 0x2, // Lambda candidate + FANTILAMBDA = 0x4, // AntiLambda candidate + FREJECTED = 0x8 // Does not satisfy any of the above, or is randomly rejected +}; + +DECLARE_SOA_COLUMN(SignalFlag, signalFlag, uint8_t); +DECLARE_SOA_DYNAMIC_COLUMN(IsK0SCandidate, isK0SCandidate, //! Flag to check if V0 is a K0S candidate + [](uint8_t flag) -> bool { return flag & o2::aod::v0flags::FK0S; }); +DECLARE_SOA_DYNAMIC_COLUMN(IsLambdaCandidate, isLambdaCandidate, //! Flag to check if V0 is a Lambda candidate + [](uint8_t flag) -> bool { return flag & o2::aod::v0flags::FLAMBDA; }); +DECLARE_SOA_DYNAMIC_COLUMN(IsAntiLambdaCandidate, isAntiLambdaCandidate, //! Flag to check if V0 is a AntiLambda candidate + [](uint8_t flag) -> bool { return flag & o2::aod::v0flags::FANTILAMBDA; }); +DECLARE_SOA_DYNAMIC_COLUMN(IsRejectedCandidate, isRejectedCandidate, //! Flag to check if V0 is rejected + [](uint8_t flag) -> bool { return flag & o2::aod::v0flags::FREJECTED; }); +} // namespace v0flags + +DECLARE_SOA_TABLE(V0SignalFlags, "AOD", "V0SIGNALFLAGS", + v0flags::SignalFlag, + v0flags::IsK0SCandidate, + v0flags::IsLambdaCandidate, + v0flags::IsAntiLambdaCandidate, + v0flags::IsRejectedCandidate); + +DECLARE_SOA_TABLE(StoredV0SignalFlags, "AOD1", "V0SIGNALFLAGS", + v0flags::SignalFlag, + v0flags::IsK0SCandidate, + v0flags::IsLambdaCandidate, + v0flags::IsAntiLambdaCandidate, + v0flags::IsRejectedCandidate, + o2::soa::Marker<1>); + +} // namespace o2::aod + +#endif // PWGLF_DATAMODEL_V0SELECTORTABLES_H diff --git a/PWGLF/TableProducer/Strangeness/CMakeLists.txt b/PWGLF/TableProducer/Strangeness/CMakeLists.txt index 27aec6b4e62..04e614c1811 100644 --- a/PWGLF/TableProducer/Strangeness/CMakeLists.txt +++ b/PWGLF/TableProducer/Strangeness/CMakeLists.txt @@ -107,6 +107,11 @@ o2physics_add_dpl_workflow(strangederivedbuilder PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DetectorsBase COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(v0-selector + SOURCES v0selector.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(v0qaanalysis SOURCES v0qaanalysis.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore diff --git a/PWGLF/TableProducer/Strangeness/v0selector.cxx b/PWGLF/TableProducer/Strangeness/v0selector.cxx new file mode 100644 index 00000000000..bd4e14ed3e4 --- /dev/null +++ b/PWGLF/TableProducer/Strangeness/v0selector.cxx @@ -0,0 +1,260 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \brief Task to select V0s based on cuts +/// +/// \author Gijs van Weelden + +#include +#include +#include + +#include "Framework/runDataProcessing.h" +#include "Framework/AnalysisTask.h" +#include "Framework/AnalysisDataModel.h" +#include "Framework/ASoA.h" + +#include "CommonConstants/PhysicsConstants.h" +#include "Common/Core/RecoDecay.h" + +#include "PWGLF/DataModel/LFStrangenessTables.h" +#include "PWGLF/DataModel/V0SelectorTables.h" + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +struct V0SelectorTask { + Produces v0FlagTable; + + Configurable> K0SPtBins{"K0SPtBins", {0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 10.0, 15.0, 20.0, 25.0, 30.0}, "K0S pt Vals"}; + Configurable> K0SRVals{"K0SRVals", {1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0}, "K0S min R values"}; + Configurable> K0SCtauVals{"K0SCtauVals", {20.0, 20.0, 20.0, 20.0, 20.0, 20.0, 20.0, 20.0, 20.0, 20.0}, "K0S max ctau values"}; + Configurable> K0SCosPAVals{"K0SCosPAVals", {0.997, 0.997, 0.997, 0.997, 0.997, 0.997, 0.997, 0.997, 0.997, 0.997}, "K0S min cosPA values"}; + Configurable> K0SDCAVals{"K0SDCAVals", {0.20, 0.20, 0.20, 0.20, 0.20, 0.20, 0.15, 0.15, 0.10, 0.10}, "K0S min DCA +- values"}; + Configurable> K0SDCAdVals{"K0SDCAdVals", {1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0}, "K0S max DCAd values"}; + + Configurable> LambdaPtBins{"LambdaPtBins", {0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 10.0, 15.0, 20.0}, "Lambda pt Vals"}; + Configurable> LambdaRVals{"LambdaRVals", {1.0, 10.0, 10.0, 10.0, 10.0, 10.0, 20.0, 20.0}, "Lambda min R values"}; + Configurable> LambdaCtauVals{"LambdaCtauVals", {22.5, 25.0, 25.0, 25.0, 25.0, 25.0, 25.0, 25.0}, "Lambda max ctau values"}; + Configurable> LambdaCosPAVals{"LambdaCosPAVals", {0.997, 0.997, 0.997, 0.997, 0.997, 0.997, 0.997, 0.997}, "Lambda min cosPA values"}; + Configurable> LambdaDCApVals{"LambdaDCApVals", {0.20, 0.10, 0.10, 0.10, 0.10, 0.10, 0.10, 0.10}, "Lambda min DCA+ values"}; + Configurable> LambdaDCAnVals{"LambdaDCAnVals", {0.20, 0.20, 0.20, 0.20, 0.20, 0.20, 0.15, 0.15}, "Lambda min DCA- values"}; + Configurable> LambdaDCAdVals{"LambdaDCAdVals", {1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0}, "Lambda max DCAd values"}; + + Configurable> AntiLambdaPtBins{"AntiLambdaPtBins", {0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 10.0, 15.0, 20.0}, "AntiLambda pt Vals"}; + Configurable> AntiLambdaRVals{"AntiLambdaRVals", {10.0, 10.0, 10.0, 10.0, 20.0, 20.0, 20.0, 20.0}, "AntiLambda min R values"}; + Configurable> AntiLambdaCtauVals{"AntiLambdaCtauVals", {22.5, 25.0, 25.0, 25.0, 25.0, 25.0, 25.0, 25.0}, "AntiLambda max ctau values"}; + Configurable> AntiLambdaCosPAVals{"AntiLambdaCosPAVals", {0.997, 0.997, 0.997, 0.997, 0.997, 0.997, 0.997, 0.997}, "AntiLambda min cosPA values"}; + Configurable> AntiLambdaDCApVals{"AntiLambdaDCApVals", {0.20, 0.20, 0.20, 0.20, 0.20, 0.20, 0.20, 0.20}, "AntiLambda min DCA+ values"}; + Configurable> AntiLambdaDCAnVals{"AntiLambdaDCAnVals", {0.20, 0.10, 0.10, 0.10, 0.10, 0.10, 0.10, 0.10}, "AntiLambda min DCA- values"}; + Configurable> AntiLambdaDCAdVals{"AntiLambdaDCAdVals", {1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0}, "AntiLambda max DCAd values"}; + + Configurable massCuts{"massCuts", true, "Apply mass cuts"}; + Configurable competingMassCuts{"competingMassCuts", true, "Apply competing mass cuts"}; + Configurable> K0SMassLowVals{"K0SMassLowVals", {0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4}, "K0S mass cut lower values (MeV)"}; + Configurable> K0SMassHighVals{"K0SMassHighVals", {0.6, 0.6, 0.6, 0.6, 0.6, 0.6, 0.6, 0.6, 0.6, 0.6}, "K0S mass cut upper values (MeV)"}; + Configurable> LambdaMassLowVals{"LambdaMassLowVals", {1.08, 1.08, 1.08, 1.08, 1.08, 1.08, 1.08, 1.08}, "Lambda mass cut lower values (MeV)"}; + Configurable> LambdaMassHighVals{"LambdaMassHighVals", {1.125, 1.125, 1.125, 1.125, 1.125, 1.125, 1.125, 1.125}, "Lambda mass cut upper values (MeV)"}; + Configurable> AntiLambdaMassLowVals{"AntiLambdaMassLowVals", {1.08, 1.08, 1.08, 1.08, 1.08, 1.08, 1.08, 1.08}, "AntiLambda mass cut lower values (MeV)"}; + Configurable> AntiLambdaMassHighVals{"AntiLambdaMassHighVals", {1.125, 1.125, 1.125, 1.125, 1.125, 1.125, 1.125, 1.125}, "AntiLambda mass cut upper values (MeV)"}; + + Configurable randomSelection{"randomSelection", true, "Randomly select V0s"}; + Configurable> K0SFraction{"randomSelectionFraction", {2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0}, "Fraction of K0S to randomly select"}; + Configurable> LambdaFraction{"LambdaFraction", {2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0}, "Fraction of Lambda to randomly select"}; + Configurable> AntiLambdaFraction{"AntiLambdaFraction", {2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0}, "Fraction of AntiLambda to randomly select"}; + + void init(InitContext const&) + {} + + template + bool K0SCuts(T const& collision, U const& v0) + { + if (v0.pt() < K0SPtBins->at(0) || v0.pt() > K0SPtBins->at(K0SPtBins->size() - 1)) { + return false; + } + int ptBin = std::distance(K0SPtBins->begin(), std::upper_bound(K0SPtBins->begin(), K0SPtBins->end(), v0.pt())) - 1; + if (v0.v0radius() < K0SRVals->at(ptBin)) { + return false; + } + if (v0.v0cosPA() < K0SCosPAVals->at(ptBin)) { + return false; + } + if (v0.dcaV0daughters() > K0SDCAdVals->at(ptBin)) { + return false; + } + if (TMath::Abs(v0.dcapostopv()) < K0SDCAVals->at(ptBin)) { + return false; + } + if (TMath::Abs(v0.dcanegtopv()) < K0SDCAVals->at(ptBin)) { + return false; + } + float ctau = v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * o2::constants::physics::MassK0Short; + if (ctau < K0SCtauVals->at(ptBin)) { + return false; + } + // Apply mass cuts only if requested + if (!massCuts) { + return true; + } + if (v0.mK0Short() < K0SMassLowVals->at(ptBin) || v0.mK0Short() > K0SMassHighVals->at(ptBin)) { + return false; + } + if (!competingMassCuts) { + return true; + } + if (v0.mLambda() > LambdaMassLowVals->at(ptBin) && v0.mLambda() < LambdaMassHighVals->at(ptBin)) { + return false; + } + if (v0.mAntiLambda() > AntiLambdaMassLowVals->at(ptBin) && v0.mAntiLambda() < AntiLambdaMassHighVals->at(ptBin)) { + return false; + } + return true; + } + template + bool LambdaCuts(T const& collision, U const& v0) + { + if (v0.pt() < LambdaPtBins->at(0) || v0.pt() > LambdaPtBins->at(LambdaPtBins->size() - 1)) { + return false; + } + int ptBin = std::distance(LambdaPtBins->begin(), std::upper_bound(LambdaPtBins->begin(), LambdaPtBins->end(), v0.pt())) - 1; + if (v0.v0radius() < LambdaRVals->at(ptBin)) { + return false; + } + if (v0.v0cosPA() < LambdaCosPAVals->at(ptBin)) { + return false; + } + if (v0.dcaV0daughters() > LambdaDCAdVals->at(ptBin)) { + return false; + } + if (TMath::Abs(v0.dcapostopv()) < LambdaDCApVals->at(ptBin)) { + return false; + } + if (TMath::Abs(v0.dcanegtopv()) < LambdaDCAnVals->at(ptBin)) { + return false; + } + float ctau = v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * o2::constants::physics::MassLambda0; + if (ctau < LambdaCtauVals->at(ptBin)) { + return false; + } + // Apply mass cuts only if requested + if (!massCuts) { + return true; + } + if (v0.mLambda() < LambdaMassLowVals->at(ptBin) || v0.mLambda() > LambdaMassHighVals->at(ptBin)) { + return false; + } + if (!competingMassCuts) { + return true; + } + if (v0.mK0Short() > K0SMassLowVals->at(ptBin) && v0.mK0Short() < K0SMassHighVals->at(ptBin)) { + return false; + } + return true; + } + template + bool AntiLambdaCuts(T const& collision, U const& v0) + { + if (v0.pt() < AntiLambdaPtBins->at(0) || v0.pt() > AntiLambdaPtBins->at(AntiLambdaPtBins->size() - 1)) { + return false; + } + int ptBin = std::distance(AntiLambdaPtBins->begin(), std::upper_bound(AntiLambdaPtBins->begin(), AntiLambdaPtBins->end(), v0.pt())) - 1; + if (v0.v0radius() < AntiLambdaRVals->at(ptBin)) { + return false; + } + if (v0.v0cosPA() < AntiLambdaCosPAVals->at(ptBin)) { + return false; + } + if (v0.dcaV0daughters() > AntiLambdaDCAdVals->at(ptBin)) { + return false; + } + if (TMath::Abs(v0.dcapostopv()) < AntiLambdaDCApVals->at(ptBin)) { + return false; + } + if (TMath::Abs(v0.dcanegtopv()) < AntiLambdaDCAnVals->at(ptBin)) { + return false; + } + float ctau = v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * o2::constants::physics::MassLambda0; + if (ctau < AntiLambdaCtauVals->at(ptBin)) { + return false; + } + // Apply mass cuts only if requested + if (!massCuts) { + return true; + } + if (v0.mAntiLambda() < AntiLambdaMassLowVals->at(ptBin) || v0.mAntiLambda() > AntiLambdaMassHighVals->at(ptBin)) { + return false; + } + if (!competingMassCuts) { + return true; + } + if (v0.mK0Short() > K0SMassLowVals->at(ptBin) && v0.mK0Short() < K0SMassHighVals->at(ptBin)) { + return false; + } + return true; + } + template + bool RandomlyReject(T const& v0, uint8_t flag) + { + if (!(flag & aod::v0flags::FK0S || flag & aod::v0flags::FLAMBDA || flag & aod::v0flags::FANTILAMBDA)) { + return true; + } + + // In case of multiple candidate types, only check the lowest threshold value + float threshold = 2.; + if (flag & aod::v0flags::FK0S) { + int ptBin = std::distance(K0SPtBins->begin(), std::upper_bound(K0SPtBins->begin(), K0SPtBins->end(), v0.pt())) - 1; + if (threshold > K0SFraction->at(ptBin)) { + threshold = K0SFraction->at(ptBin); + } + } + if (flag & aod::v0flags::FLAMBDA) { + int ptBin = std::distance(LambdaPtBins->begin(), std::upper_bound(LambdaPtBins->begin(), LambdaPtBins->end(), v0.pt())) - 1; + if (threshold > LambdaFraction->at(ptBin)) { + threshold = LambdaFraction->at(ptBin); + } + } + if (flag & aod::v0flags::FANTILAMBDA) { + int ptBin = std::distance(AntiLambdaPtBins->begin(), std::upper_bound(AntiLambdaPtBins->begin(), AntiLambdaPtBins->end(), v0.pt())) - 1; + if (threshold > AntiLambdaFraction->at(ptBin)) { + threshold = AntiLambdaFraction->at(ptBin); + } + } + return (gRandom->Uniform() > threshold); + } + + void processV0(aod::Collision const& collision, aod::V0Datas const& v0s) + { + for (const auto& v0 : v0s) { + bool candidateK0S = K0SCuts(collision, v0); + bool candidateLambda = LambdaCuts(collision, v0); + bool candidateAntiLambda = AntiLambdaCuts(collision, v0); + uint8_t flag = 0; + flag += candidateK0S * aod::v0flags::FK0S; + flag += candidateLambda * aod::v0flags::FLAMBDA; + flag += candidateAntiLambda * aod::v0flags::FANTILAMBDA; + + if (candidateK0S + candidateLambda + candidateAntiLambda == 0) { + flag += aod::v0flags::FREJECTED; + } else if (randomSelection) { + flag += RandomlyReject(v0, flag) * aod::v0flags::FREJECTED; + } + v0FlagTable(flag); + } + } + PROCESS_SWITCH(V0SelectorTask, processV0, "flags V0 candidates as potential signal", true); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc, TaskName{"v0-selector"})}; +}