diff --git a/PWGLF/Tasks/Resonances/CMakeLists.txt b/PWGLF/Tasks/Resonances/CMakeLists.txt index c04f076bef2..f466c75e7e3 100644 --- a/PWGLF/Tasks/Resonances/CMakeLists.txt +++ b/PWGLF/Tasks/Resonances/CMakeLists.txt @@ -267,3 +267,23 @@ o2physics_add_dpl_workflow(phi-1020-spherocity-analysis SOURCES phi1020SpherocityAnalysis.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(phitutorial + SOURCES phitutorial.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(phitutorial-step0 + SOURCES phitutorial_step0.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(phitutorial-step1 + SOURCES phitutorial_step1.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(phitutorial-step2 + SOURCES phitutorial_step2.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(phitutorial-step3 + SOURCES phitutorial_step3.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) diff --git a/PWGLF/Tasks/Resonances/phitutorial.cxx b/PWGLF/Tasks/Resonances/phitutorial.cxx new file mode 100644 index 00000000000..5e93f7c39bc --- /dev/null +++ b/PWGLF/Tasks/Resonances/phitutorial.cxx @@ -0,0 +1,227 @@ +// Copyright 2019-2025 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// \file phitutorial.cxx +/// \brief Phi meson analysis tutorial +/// \author Adrian Fereydon Nassirpour + +// IMPORTANT INCLUDES +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponse.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include "Framework/ASoA.h" +#include "Framework/AnalysisDataModel.h" +#include "Framework/AnalysisTask.h" +#include "ReconstructionDataFormats/Track.h" +#include +#include + +// ROOT Includes (optional) +#include + +// C++ includes +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +// MAIN STRUCT +struct phitutorial { + + //*************************************// + // SLICECACHE AND REGISTRY DEFS + //*************************************// + SliceCache cache; + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + //*************************************// + // INIT FUNCTION AND HISTOGRAM BOOKING + //*************************************// + void init(o2::framework::InitContext&) + { + const AxisSpec ptAxis = {200, 0, 20.0}; + const AxisSpec MinvAxis = {200, 0.85, 1.25}; + + histos.add("Nch_pT", "Nch_pT", kTH1F, {ptAxis}); + histos.add("Nch_USS_Minv", "Nch_USS_Minv", kTH1F, {MinvAxis}); + histos.add("Nch_LSS_Minv", "Nch_LSS_Minv", kTH1F, {MinvAxis}); + + histos.add("Nch_ME_Minv", "Nch_ME_Minv", kTH1F, {MinvAxis}); + + }; // end of init + + //*************************************// + // TIME TO BUILD TRACK AND EVENT CANDIDATES + //*************************************// + using EventCandidates = soa::Join; + using TrackCandidates = soa::Join; + double massKa = o2::constants::physics::MassKPlus; + + //***************************************// + // PREAMBLE COMPLETE, NOW WE DO HELPER FCNS + //**************************************// + template + bool eventSelection(const EventType event) + { + if (!event.sel8()) + return false; + if (std::abs(event.posZ()) > 10) + return false; + if (!event.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) + return false; + if (!event.selection_bit(aod::evsel::kNoSameBunchPileup)) + return false; + if (!event.selection_bit(aod::evsel::kNoCollInTimeRangeStandard)) + return false; + + return true; + }; + //********************************************// + template + bool trackSelection(const TracksType track) + { + if (!track.isGlobalTrack()) + return false; + if (track.pt() < 0.15) + return false; + if (std::abs(track.eta()) > 1.0) + return false; + + return true; + }; + //********************************************// + template + bool trackPIDKaon(const TrackPID& candidate) + { + bool tpcPIDPassed{false}, tofPIDPassed{false}; + // TPC + if (std::abs(candidate.tpcNSigmaKa()) < 3) + tpcPIDPassed = true; + // TOF + if (candidate.hasTOF()) { + if (std::abs(candidate.tofNSigmaKa()) < 3) { + tofPIDPassed = true; + } + } else { + tofPIDPassed = true; + } + // TPC & TOF + if (tpcPIDPassed && tofPIDPassed) { + return true; + } + return false; + } + + //********************************************// + // HELPER FCNS COMPLETE, NOW WE DO PROCESS FCNS + //********************************************// + + // SAME EVENT + int nEvents = 0; + void processDataSameEvent(EventCandidates::iterator const& collision, TrackCandidates const& tracks) + { + nEvents++; + if ((nEvents + 1) % 10000 == 0) { + std::cout << "Processed Data Events: " << nEvents << std::endl; + } + + if (!eventSelection(collision)) + return; + + for (const auto& track : tracks) { + histos.fill(HIST("Nch_pT"), track.pt()); + } + + for (const auto& [trk1, trk2] : combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(tracks, tracks))) { + if (!trackSelection(trk1) || !trackSelection(trk2)) { + continue; + } + if (!trackPIDKaon(trk1) || !trackPIDKaon(trk2)) { + continue; + } + + ROOT::Math::PxPyPzMVector lDecayDaughter1, lDecayDaughter2, lResonance; + lDecayDaughter1 = ROOT::Math::PxPyPzMVector(trk1.px(), trk1.py(), trk1.pz(), massKa); + lDecayDaughter2 = ROOT::Math::PxPyPzMVector(trk2.px(), trk2.py(), trk2.pz(), massKa); + + lResonance = lDecayDaughter1 + lDecayDaughter2; + double conjugate = trk1.sign() * trk2.sign(); + if (conjugate < 0) { + histos.fill(HIST("Nch_USS_Minv"), lResonance.M()); + } else { + histos.fill(HIST("Nch_LSS_Minv"), lResonance.M()); + } + } // Invariant mass combinations + + } // proccessSameEvent + PROCESS_SWITCH(phitutorial, processDataSameEvent, "process Data Same Event", false); + + //**************************************************************************************************************************// + + // MIXED EVENT + + //*********************************************************// + // DEFINITION OF SLICE CACHE, BINNING AND MIXING STRUCTURE + //*********************************************************// + Preslice perCollision = aod::track::collisionId; + std::vector zBins{10, -10, 10}; + std::vector multBins{VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 100.1}; + using BinningType = ColumnBinningPolicy; + BinningType binning{{zBins, multBins}, true}; + SameKindPair pair{binning, 5, -1, &cache}; + + void processDataMixedEvent(EventCandidates const& collisions, TrackCandidates const& tracks) + { + LOGF(info, "Input data Collisions %d, Tracks %d ", collisions.size(), tracks.size()); + + for (const auto& [c1, tracks1, c2, tracks2] : pair) { + + if (!eventSelection(c1) || !eventSelection(c2)) + continue; + + for (const auto& [track1, track2] : combinations(o2::soa::CombinationsFullIndexPolicy(tracks1, tracks2))) { + + if (track1.sign() * track2.sign() > 0) + continue; + + if (!trackSelection(track1) || !trackSelection(track2)) { + continue; + } + if (!trackPIDKaon(track1) || !trackPIDKaon(track2)) { + continue; + } + + ROOT::Math::PxPyPzMVector lDecayDaughter1, lDecayDaughter2, mother; + lDecayDaughter1 = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), massKa); + lDecayDaughter2 = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), massKa); + + mother = lDecayDaughter1 + lDecayDaughter2; + + histos.fill(HIST("Nch_ME_Minv"), mother.M()); + } + } + } // processMixedEvent + PROCESS_SWITCH(phitutorial, processDataMixedEvent, "process Data Mixed Event", false); +}; + +//***************************************// +// TASK COMPLETE! +//**************************************// + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +}; diff --git a/PWGLF/Tasks/Resonances/phitutorial_step0.cxx b/PWGLF/Tasks/Resonances/phitutorial_step0.cxx new file mode 100644 index 00000000000..16394389c91 --- /dev/null +++ b/PWGLF/Tasks/Resonances/phitutorial_step0.cxx @@ -0,0 +1,134 @@ +// Copyright 2019-2025 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// \file phitutorial.cxx +/// \brief Phi meson analysis tutorial +/// \author Adrian Fereydon Nassirpour + +// IMPORTANT INCLUDES +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponse.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include "Framework/ASoA.h" +#include "Framework/AnalysisDataModel.h" +#include "Framework/AnalysisTask.h" +#include "ReconstructionDataFormats/Track.h" +#include +#include + +// ROOT Includes (optional) +#include + +// C++ includes +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +// MAIN STRUCT +struct phitutorial_step0 { + + //*************************************// + // SLICECACHE AND REGISTRY DEFS + //*************************************// + SliceCache cache; + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + //*************************************// + // INIT FUNCTION AND HISTOGRAM BOOKING + //*************************************// + void init(o2::framework::InitContext&) + { + const AxisSpec ptAxis = {200, 0, 20.0}; + const AxisSpec MinvAxis = {200, 0.85, 1.25}; + + histos.add("Nch_pT", "Nch_pT", kTH1F, {ptAxis}); + + }; // end of init + + //*************************************// + // TIME TO BUILD TRACK AND EVENT CANDIDATES + //*************************************// + using EventCandidates = soa::Join; + using TrackCandidates = soa::Join; + double massKa = o2::constants::physics::MassKPlus; + + //***************************************// + // PREAMBLE COMPLETE, NOW WE DO HELPER FCNS + //**************************************// + template + bool eventSelection(const EventType event) + { + if (!event.sel8()) // This is required to extract good events + return false; + + return true; + }; + //********************************************// + // Space for more helper functions! + + //********************************************// + // HELPER FCNS COMPLETE, NOW WE DO PROCESS FCNS + //********************************************// + + // SAME EVENT + int nEvents = 0; + void processDataSameEvent(EventCandidates::iterator const& collision, TrackCandidates const& tracks) + { + nEvents++; + if ((nEvents + 1) % 10000 == 0) { + std::cout << "Processed Data Events: " << nEvents << std::endl; + } + + if (!eventSelection(collision)) + return; + + // Now, time to start coding the task! + // Keep in mind that: + // M_inv = sqrt( (E1+E2)^2 - |P1 + P2|^2 ) + // Where you use the energies and momenta of the individual Kaons. + + // You should fill: histos.fill(HIST("Minv"), M_inv), calculated as above. + + // Usefull tips: + // E = sqrt(p^2 + m^2). The Kaon mass is found above in the constant massKa + // pz = pT*sinh(eta) + // track.pt() + // track.eta(); + // std::sinh(x) + + // For more concise techinques, check out: + // ROOT::Math::PxPyPzMVector //Check google + // combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy.... //check ALICE O2 documentation + + for (const auto& track : tracks) { + histos.fill(HIST("Nch_pT"), track.pt()); + //.. + //.. + //.. + } + + } // proccessSameEvent + PROCESS_SWITCH(phitutorial_step0, processDataSameEvent, "process Data Same Event", false); + + //***************************************// + // TASK COMPLETE! + //**************************************// +}; +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +}; diff --git a/PWGLF/Tasks/Resonances/phitutorial_step1.cxx b/PWGLF/Tasks/Resonances/phitutorial_step1.cxx new file mode 100644 index 00000000000..32b1760b809 --- /dev/null +++ b/PWGLF/Tasks/Resonances/phitutorial_step1.cxx @@ -0,0 +1,140 @@ +// Copyright 2019-2025 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// \file phitutorial.cxx +/// \brief Phi meson analysis tutorial +/// \author Adrian Fereydon Nassirpour + +// IMPORTANT INCLUDES +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponse.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include "Framework/ASoA.h" +#include "Framework/AnalysisDataModel.h" +#include "Framework/AnalysisTask.h" +#include "ReconstructionDataFormats/Track.h" +#include +#include + +// ROOT Includes (optional) +#include + +// C++ includes +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +// MAIN STRUCT +struct phitutorial_step1 { + + //*************************************// + // SLICECACHE AND REGISTRY DEFS + //*************************************// + SliceCache cache; + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + //*************************************// + // INIT FUNCTION AND HISTOGRAM BOOKING + //*************************************// + void init(o2::framework::InitContext&) + { + const AxisSpec ptAxis = {200, 0, 20.0}; + const AxisSpec MinvAxis = {200, 0.85, 1.25}; + + histos.add("Nch_pT", "Nch_pT", kTH1F, {ptAxis}); + histos.add("Nch_USS_Minv", "Nch_USS_Minv", kTH1F, {MinvAxis}); + + }; // end of init + + //*************************************// + // TIME TO BUILD TRACK AND EVENT CANDIDATES + //*************************************// + using EventCandidates = soa::Join; + using TrackCandidates = soa::Join; + double massKa = o2::constants::physics::MassKPlus; + + //***************************************// + // PREAMBLE COMPLETE, NOW WE DO HELPER FCNS + //**************************************// + template + bool eventSelection(const EventType event) + { + if (!event.sel8()) // This is required to extract good events + return false; + + return true; + }; + //********************************************// + // Space for more helper functions! + + //********************************************// + // HELPER FCNS COMPLETE, NOW WE DO PROCESS FCNS + //********************************************// + + // SAME EVENT + int nEvents = 0; + void processDataSameEvent(EventCandidates::iterator const& collision, TrackCandidates const& tracks) + { + nEvents++; + if ((nEvents + 1) % 10000 == 0) { + std::cout << "Processed Data Events: " << nEvents << std::endl; + } + + if (!eventSelection(collision)) + return; + + // Now, we want to add some kind of filter for our tracks! + // Tracks we want to accept: + // track.isGlobalTrack() <.... this menas that it is a good track + // track.pt() >0.15 <.... we want to remove really low momentum tracks + // -0.8 0.15f; + // then you have to modify the subscription + // soa::Filtered const& tracks + + for (const auto& track : tracks) { + histos.fill(HIST("Nch_pT"), track.pt()); + } + + for (const auto& [trk1, trk2] : combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(tracks, tracks))) { + + ROOT::Math::PxPyPzMVector lDecayDaughter1, lDecayDaughter2, lResonance; + lDecayDaughter1 = ROOT::Math::PxPyPzMVector(trk1.px(), trk1.py(), trk1.pz(), massKa); + lDecayDaughter2 = ROOT::Math::PxPyPzMVector(trk2.px(), trk2.py(), trk2.pz(), massKa); + + lResonance = lDecayDaughter1 + lDecayDaughter2; + double conjugate = trk1.sign() * trk2.sign(); + if (conjugate < 0) { + histos.fill(HIST("Nch_USS_Minv"), lResonance.M()); + } + } // Invariant mass combinations + + } // proccessSameEvent + PROCESS_SWITCH(phitutorial_step1, processDataSameEvent, "process Data Same Event", false); + + //***************************************// + // TASK COMPLETE! + //**************************************// +}; +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +}; diff --git a/PWGLF/Tasks/Resonances/phitutorial_step2.cxx b/PWGLF/Tasks/Resonances/phitutorial_step2.cxx new file mode 100644 index 00000000000..0a97f6d1afe --- /dev/null +++ b/PWGLF/Tasks/Resonances/phitutorial_step2.cxx @@ -0,0 +1,161 @@ +// Copyright 2019-2025 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// \file phitutorial.cxx +/// \brief Phi meson analysis tutorial +/// \author Adrian Fereydon Nassirpour + +// IMPORTANT INCLUDES +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponse.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include "Framework/ASoA.h" +#include "Framework/AnalysisDataModel.h" +#include "Framework/AnalysisTask.h" +#include "ReconstructionDataFormats/Track.h" +#include +#include + +// ROOT Includes (optional) +#include + +// C++ includes +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +// MAIN STRUCT +struct phitutorial_step2 { + + //*************************************// + // SLICECACHE AND REGISTRY DEFS + //*************************************// + SliceCache cache; + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + //*************************************// + // INIT FUNCTION AND HISTOGRAM BOOKING + //*************************************// + void init(o2::framework::InitContext&) + { + const AxisSpec ptAxis = {200, 0, 20.0}; + const AxisSpec MinvAxis = {200, 0.85, 1.25}; + + histos.add("Nch_pT", "Nch_pT", kTH1F, {ptAxis}); + histos.add("Nch_USS_Minv", "Nch_USS_Minv", kTH1F, {MinvAxis}); + + }; // end of init + + //*************************************// + // TIME TO BUILD TRACK AND EVENT CANDIDATES + //*************************************// + using EventCandidates = soa::Join; + using TrackCandidates = soa::Join; + double massKa = o2::constants::physics::MassKPlus; + + //***************************************// + // PREAMBLE COMPLETE, NOW WE DO HELPER FCNS + //**************************************// + template + bool eventSelection(const EventType event) + { + if (!event.sel8()) // This is required to extract good events + return false; + + return true; + }; + //********************************************// + template + bool trackSelection(const TracksType track) + { + if (!track.isGlobalTrack()) + return false; + if (track.pt() < 0.15) + return false; + if (std::abs(track.eta()) > 1.0) + return false; + + return true; + }; + // Space for more helper functions! + //********************************************// + + //********************************************// + // HELPER FCNS COMPLETE, NOW WE DO PROCESS FCNS + //********************************************// + + // SAME EVENT + int nEvents = 0; + void processDataSameEvent(EventCandidates::iterator const& collision, TrackCandidates const& tracks) + { + nEvents++; + if ((nEvents + 1) % 10000 == 0) { + std::cout << "Processed Data Events: " << nEvents << std::endl; + } + + if (!eventSelection(collision)) + return; + + // Now, we want to add some PID to ensure that we are with a higher likelhood pairing Kaons. + // Three ways to do this: + // 1.) Directly cut on the tracks in the looping functions (not recommended) + + // 2.) Create a helper function above similar to trackSelection + + // 3.) Partition your tracks with a preselection by adding this outside of your process function: + // Partition kaon (nabs(aod::pidtpc::tpcNSigmaKa) <= X); // X is a cfg value or a hardcoded integer. + // Then inside the function: auto tracks1 = kaon->sliceByCached(aod::track::collisionId, collision1.globalIndex(), cache); Do the same for tracks2. + + // Getters for PID: + // tracks.tpcNSigmaKa() + // tracks.tofNSigmaKa() + // Good starting value for the selected nsigma value is "3". + // You might not want to have a STRICT TOF cut, a lot of tracks with good TPC PID does not have TOF information. You can make a conditional cut on TOF by only implementing the TOF cut if track.hasTOF() returns TRUE. + + for (const auto& track : tracks) { + if (!trackSelection(track)) { + continue; + } + histos.fill(HIST("Nch_pT"), track.pt()); + } + + for (const auto& [trk1, trk2] : combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(tracks, tracks))) { + if (!trackSelection(trk1) || !trackSelection(trk2)) { + continue; + } + ROOT::Math::PxPyPzMVector lDecayDaughter1, lDecayDaughter2, lResonance; + lDecayDaughter1 = ROOT::Math::PxPyPzMVector(trk1.px(), trk1.py(), trk1.pz(), massKa); + lDecayDaughter2 = ROOT::Math::PxPyPzMVector(trk2.px(), trk2.py(), trk2.pz(), massKa); + + lResonance = lDecayDaughter1 + lDecayDaughter2; + double conjugate = trk1.sign() * trk2.sign(); + if (conjugate < 0) { + histos.fill(HIST("Nch_USS_Minv"), lResonance.M()); + } + } // Invariant mass combinations + + } // proccessSameEvent + PROCESS_SWITCH(phitutorial_step2, processDataSameEvent, "process Data Same Event", false); + + //***************************************// + // TASK COMPLETE! + //**************************************// +}; +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +}; diff --git a/PWGLF/Tasks/Resonances/phitutorial_step3.cxx b/PWGLF/Tasks/Resonances/phitutorial_step3.cxx new file mode 100644 index 00000000000..1ccf59aa9c2 --- /dev/null +++ b/PWGLF/Tasks/Resonances/phitutorial_step3.cxx @@ -0,0 +1,211 @@ +// Copyright 2019-2025 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// \file phitutorial.cxx +/// \brief Phi meson analysis tutorial +/// \author Adrian Fereydon Nassirpour + +// IMPORTANT INCLUDES +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponse.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include "Framework/ASoA.h" +#include "Framework/AnalysisDataModel.h" +#include "Framework/AnalysisTask.h" +#include "ReconstructionDataFormats/Track.h" +#include +#include + +// ROOT Includes (optional) +#include + +// C++ includes +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +// MAIN STRUCT +struct phitutorial_step3 { + + //*************************************// + // SLICECACHE AND REGISTRY DEFS + //*************************************// + SliceCache cache; + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + //*************************************// + // INIT FUNCTION AND HISTOGRAM BOOKING + //*************************************// + void init(o2::framework::InitContext&) + { + const AxisSpec ptAxis = {200, 0, 20.0}; + const AxisSpec MinvAxis = {200, 0.85, 1.25}; + + histos.add("Nch_pT", "Nch_pT", kTH1F, {ptAxis}); + histos.add("Nch_USS_Minv", "Nch_USS_Minv", kTH1F, {MinvAxis}); + + histos.add("Nch_LSS_Minv", "Nch_LSS_Minv", kTH1F, {MinvAxis}); + + histos.add("Nch_ME_Minv", "Nch_ME_Minv", kTH1F, {MinvAxis}); + + }; // end of init + + //*************************************// + // TIME TO BUILD TRACK AND EVENT CANDIDATES + //*************************************// + using EventCandidates = soa::Join; + using TrackCandidates = soa::Join; + double massKa = o2::constants::physics::MassKPlus; + + //***************************************// + // PREAMBLE COMPLETE, NOW WE DO HELPER FCNS + //**************************************// + template + bool eventSelection(const EventType event) + { + if (!event.sel8()) // This is required to extract good events + return false; + + return true; + }; + //********************************************// + template + bool trackSelection(const TracksType track) + { + if (!track.isGlobalTrack()) + return false; + if (track.pt() < 0.15) + return false; + if (std::abs(track.eta()) > 1.0) + return false; + + return true; + }; + + //********************************************// + + template + bool trackPIDKaon(const TrackPID& candidate) + { + bool tpcPIDPassed{false}, tofPIDPassed{false}; + // TPC + if (std::abs(candidate.tpcNSigmaKa()) < 3) + tpcPIDPassed = true; + // TOF + if (candidate.hasTOF()) { + if (std::abs(candidate.tofNSigmaKa()) < 3) { + tofPIDPassed = true; + } + } else { + tofPIDPassed = true; + } + // TPC & TOF + if (tpcPIDPassed && tofPIDPassed) { + return true; + } + return false; + } + + //********************************************// + // HELPER FCNS COMPLETE, NOW WE DO PROCESS FCNS + //********************************************// + + // SAME EVENT + int nEvents = 0; + void processDataSameEvent(EventCandidates::iterator const& collision, TrackCandidates const& tracks) + { + nEvents++; + if ((nEvents + 1) % 10000 == 0) { + std::cout << "Processed Data Events: " << nEvents << std::endl; + } + + if (!eventSelection(collision)) + return; + + // Last step, we want to remove the cominbatorial background to get a clean peak. We want to fill our new two booked historams, Nch_LSS_Minv and Nch_ME_Minv + + // LSS is easy, you simply need to fill the histogram if the conjugate argument below is NOT true. + // For event mixing, we have to now copy our logic into a new process function below, and iterate over track pairs between different events! + + for (const auto& track : tracks) { + if (!trackSelection(track)) { + continue; + } + histos.fill(HIST("Nch_pT"), track.pt()); + } + + for (const auto& [trk1, trk2] : combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(tracks, tracks))) { + if (!trackSelection(trk1) || !trackSelection(trk2)) { + continue; + } + if (!trackPIDKaon(trk1) || !trackPIDKaon(trk2)) { + continue; + } + + ROOT::Math::PxPyPzMVector lDecayDaughter1, lDecayDaughter2, lResonance; + lDecayDaughter1 = ROOT::Math::PxPyPzMVector(trk1.px(), trk1.py(), trk1.pz(), massKa); + lDecayDaughter2 = ROOT::Math::PxPyPzMVector(trk2.px(), trk2.py(), trk2.pz(), massKa); + + lResonance = lDecayDaughter1 + lDecayDaughter2; + double conjugate = trk1.sign() * trk2.sign(); + if (conjugate < 0) { + histos.fill(HIST("Nch_USS_Minv"), lResonance.M()); + } + } // Invariant mass combinations + + } // proccessSameEvent + PROCESS_SWITCH(phitutorial_step3, processDataSameEvent, "process Data Same Event", false); + + //**************************************************************************************************************************// + + // MIXED EVENT + + //*********************************************************// + // DEFINITION OF SLICE CACHE, BINNING AND MIXING STRUCTURE + //*********************************************************// + Preslice perCollision = aod::track::collisionId; + // We ensure here that we mix events that have relatively similar characteristics. + std::vector zBins{10, -10, 10}; + std::vector multBins{VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 100.1}; + using BinningType = ColumnBinningPolicy; + BinningType binning{{zBins, multBins}, true}; + SameKindPair pair{binning, 5, -1, &cache}; + + void processDataMixedEvent(EventCandidates const& collisions, TrackCandidates const& tracks) // notice the collisions subscrition, it is not an iterator here! + { + LOGF(info, "Input data Collisions %d, Tracks %d ", collisions.size(), tracks.size()); + + for (const auto& [c1, tracks1, c2, tracks2] : pair) { + + if (!eventSelection(c1) || !eventSelection(c2)) + continue; + // Fill your event mixing logic here. + //.. + //.. + //.. + } // pairs + } // processMixedEvent + PROCESS_SWITCH(phitutorial_step3, processDataMixedEvent, "process Data Mixed Event", false); + + //***************************************// + // TASK COMPLETE! + //**************************************// +}; +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +};