diff --git a/PWGCF/DataModel/FemtoDerived.h b/PWGCF/DataModel/FemtoDerived.h index 6bcd3432daf..525b5957a6d 100644 --- a/PWGCF/DataModel/FemtoDerived.h +++ b/PWGCF/DataModel/FemtoDerived.h @@ -50,6 +50,9 @@ DECLARE_SOA_COLUMN(BitMaskTrackTwo, bitmaskTrackTwo, BitMaskType); //! Bit f DECLARE_SOA_COLUMN(BitMaskTrackThree, bitmaskTrackThree, BitMaskType); //! Bit for track three DECLARE_SOA_COLUMN(Downsample, downsample, bool); //! Flag for downsampling + +DECLARE_SOA_COLUMN(QnBin, qnBin, int); //! Bin of qn-vector of the event + } // namespace femtodreamcollision DECLARE_SOA_TABLE_STAGED(FDCollisions, "FDCOLLISION", @@ -69,6 +72,10 @@ DECLARE_SOA_TABLE(FDColMasks, "AOD", "FDCOLMASK", DECLARE_SOA_TABLE(FDDownSample, "AOD", "FDDOWNSAMPLE", femtodreamcollision::Downsample); +DECLARE_SOA_TABLE(FDExtQnCollisions, "AOD", "FDEXTQNCOLLISION", + femtodreamcollision::QnBin); +using FDExtCollision = FDExtQnCollisions::iterator; + namespace femtodreamMCcollision { DECLARE_SOA_COLUMN(MultMCgenPartEta08, multMCgenPartEta08, int); //! Multiplicity of the event as given by the generator in |eta|<0.8 diff --git a/PWGCF/Femto/CMakeLists.txt b/PWGCF/Femto/CMakeLists.txt index 3b94846cb86..62cfdcca1e8 100644 --- a/PWGCF/Femto/CMakeLists.txt +++ b/PWGCF/Femto/CMakeLists.txt @@ -9,6 +9,7 @@ # granted to it by virtue of its status as an Intergovernmental Organization # or submit itself to any jurisdiction. +#add_subdirectory(Core) #add_subdirectory(DataModel) add_subdirectory(TableProducer) - +add_subdirectory(Tasks) diff --git a/PWGCF/Femto/Core/FemtoFlowAngularContainer.h b/PWGCF/Femto/Core/FemtoFlowAngularContainer.h new file mode 100644 index 00000000000..be70e242a21 --- /dev/null +++ b/PWGCF/Femto/Core/FemtoFlowAngularContainer.h @@ -0,0 +1,262 @@ +// 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 FemtoFlowAngularContainer.h +/// \brief Definition of the FemtoFlowAngularContainer +/// \author Andi Mathis, TU München, andreas.mathis@ph.tum.de +/// \author Valentina Mantovani Sarti, valentina.mantovani-sarti@tum.de +/// \author Georgios Mantzaridis, TU München, georgios.mantzaridis@tum.de +/// \author Anton Riedel, TU München, anton.riedel@tum.de +/// \author Zuzanna Chochulska, WUT Warsaw & CTU Prague, zchochul@cern.ch + +#ifndef PWGCF_FEMTO_CORE_FEMTOFLOWANGULARCONTAINER_H_ +#define PWGCF_FEMTO_CORE_FEMTOFLOWANGULARCONTAINER_H_ + +#include "PWGCF/Femto/Core/FemtoFlowMath.h" +#include "PWGCF/Femto/DataModel/FemtoDerived.h" + +#include "Common/Core/RecoDecay.h" + +#include "Framework/ASoAHelpers.h" +#include "Framework/AnalysisTask.h" +#include "Framework/HistogramRegistry.h" +#include "Framework/O2DatabasePDGPlugin.h" + +#include "Math/Vector4D.h" +#include "TMath.h" + +#include + +#include +#include + +namespace o2::analysis::femto_flow +{ + +namespace femto_flow_angular_container +{ +/// Femtoscopic observable to be computed +enum Observable { kstar ///< kstar +}; + +/// Type of the event processind +enum EventType { same, ///< Pair from same event + mixed ///< Pair from mixed event +}; +}; // namespace femto_flow_angular_container + +/// \class FemtoFlowAngularContainer +/// \brief Container for all histogramming related to the correlation function. The two +/// particles of the pair are passed here, and the correlation function and QA histograms +/// are filled according to the specified observable +/// \tparam eventType Type of the event (same/mixed) +/// \tparam obs Observable to be computed (k*/Q_inv/...) +template +class FemtoFlowAngularContainer +{ + public: + /// Destructor + virtual ~FemtoFlowAngularContainer() = default; + + /// Initializes histograms for the task + /// Called by init both in case of reconstructed data/ Monte Carlo, and for Monte Carlo Truth + /// \tparam T type of the axis Object + /// \param folderName Name of the directory in the output file (no suffix for reconstructed data/ Monte Carlo; "_MC" for Monte Carlo Truth) + /// \param femtoObs Title of the femto observable axis + /// \param femtoObsAxis axis object for the femto observable axis + /// \param multAxis axis object for the multiplicity axis + /// \param kTAxis axis object for the kT axis + /// \param mTAxis axis object for the mT axis + template + void initBase(std::string folderName, std::string /*femtoObs*/, T /*femtoObsAxis*/, T /*multAxis*/, T /*kTAxis*/, T /*mTAxis*/, T /*multAxis3D*/, T /*mTAxis3D*/, T etaAxis, T phiAxis, bool use3dplots) + { + using namespace o2::framework; + + mHistogramRegistry->add((folderName + "/DeltaEtaDeltaPhi").c_str(), "; #Delta#varphi (rad); #Delta#eta", kTH2F, {phiAxis, etaAxis}); + if (use3dplots) { + // use 3d plots + } + } + + /// Initializes specialized Monte Carlo truth histograms for the task + /// internal function called by init only in case of Monte Carlo truth + /// \tparam T type of the xxis Object + /// \param folderName Name of the directory in the output file (no suffix for reconstructed data/ Monte Carlo; "_MC" for Monte Carlo Truth) + /// \param femtoObsAxis axis object for the femto observable axis + template + void initMC(std::string /*folderName*/, std::string /*femtoObs*/, T /*femtoObsAxis*/, T /*multAxis*/, T /*mTAxis*/) + { + } + + /// Templated function to initialize the histograms for the task + /// Always calls initBase to initialize the histograms for data/ Monte Carlo reconstructed + /// In case of Monte Carlo, calls initBase again for Monte Carlo truth and the specialized function initMC for additional histogramms + /// \tparam T type of the configurable for the axis configuration + /// \param registry Histogram registry to be passed + /// \param kstarBins k* binning for the histograms + /// \param multBins multiplicity binning for the histograms + /// \param kTBins kT binning for the histograms + /// \param mTBins mT binning for the histograms + /// \param etaBins eta binning for the histograms + /// \param phiBins phi binning for the histograms + /// \param isMC add Monte Carlo truth histograms to the output file + template + void init(o2::framework::HistogramRegistry* registry, T& kstarBins, T& multBins, T& kTBins, T& mTBins, T& multBins3D, T& mTBins3D, P& etaBins, P& phiBins, bool isMC, bool use3dplots) + { + using namespace o2::framework; + + mHistogramRegistry = registry; + std::string femtoObs; + if constexpr (FemtoObs == femto_flow_angular_container::Observable::kstar) { + femtoObs = "#it{k*} (GeV/#it{c})"; + } + + std::vector tmpVecMult = multBins; + o2::framework::AxisSpec multAxis = {tmpVecMult, "Multiplicity"}; + o2::framework::AxisSpec femtoObsAxis = {kstarBins, femtoObs.c_str()}; + o2::framework::AxisSpec kTAxis = {kTBins, "#it{k}_{T} (GeV/#it{c})"}; + o2::framework::AxisSpec mTAxis = {mTBins, "#it{m}_{T} (GeV/#it{c}^{2})"}; + + o2::framework::AxisSpec multAxis3D = {multBins3D, "Multiplicity"}; + o2::framework::AxisSpec mTAxis3D = {mTBins3D, "#it{m}_{T} (GeV/#it{c})"}; + + // angular correlations + mPhiLow = (-static_cast(phiBins / 4) + 0.5) * o2::constants::math::TwoPI / phiBins; + mPhiHigh = o2::constants::math::TwoPI + (-static_cast(phiBins / 4) + 0.5) * o2::constants::math::TwoPI / phiBins; + o2::framework::AxisSpec phiAxis = {phiBins, mPhiLow, mPhiHigh}; + o2::framework::AxisSpec etaAxis = {etaBins, -2.0, 2.0}; + + std::string folderName = static_cast(FolderSuffix[EventType]) + static_cast(o2::aod::femtoflow_mc_particle::MCTypeName[o2::aod::femtoflow_mc_particle::MCType::kRecon]); + + initBase(folderName, femtoObs, femtoObsAxis, multAxis, kTAxis, mTAxis, multAxis3D, mTAxis3D, etaAxis, phiAxis, use3dplots); + if (isMC) { + folderName = static_cast(FolderSuffix[EventType]) + static_cast(o2::aod::femtoflow_mc_particle::MCTypeName[o2::aod::femtoflow_mc_particle::MCType::kTruth]); + initBase(folderName, femtoObs, femtoObsAxis, multAxis, kTAxis, mTAxis, multAxis3D, mTAxis3D, etaAxis, phiAxis, use3dplots); + initMC(folderName, femtoObs, femtoObsAxis, multAxis, mTAxis); + } + } + + /// Set the PDG codes of the two particles involved + /// \param PDG code of particle one + /// \param pdg2 PDG code of particle two + void setPDGCodesMass(const int pdg1, const int pdg2, const double mass1, const int mass2) + { + mMassOne = mass1; + mMassTwo = mass2; + mPDGOne = pdg1; + mPDGTwo = pdg2; + } + + /// Pass a pair to the container and compute all the relevant observables + /// Called by setPair both in case of data/ and Monte Carlo reconstructed and for Monte Carlo truth + /// \tparam T type of the femtoflowparticle + /// \param part1 Particle one + /// \param part2 Particle two + /// \param mult Multiplicity of the event + template + void setPairBase(const float /*femtoObs*/, const float /*mT*/, T const& part1, T const& part2, const int /*mult*/, bool use3dplots, float weight = 1.0f) + { + using namespace o2::framework; + + deltaEta = part1.eta() - part2.eta(); + + deltaPhi = part1.phi() - part2.phi(); + + deltaPhi = RecoDecay::constrainAngle(deltaPhi, mPhiLow, 1); + + // while (deltaPhi < mPhiLow) { + // deltaPhi += o2::constants::math::TwoPI; + // } + // while (deltaPhi > mPhiHigh) { + // deltaPhi -= o2::constants::math::TwoPI; + // } + + mHistogramRegistry->fill(HIST(FolderSuffix[EventType]) + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("/DeltaEtaDeltaPhi"), deltaPhi, deltaEta, weight); + if (use3dplots) { + // use 3d plots + } + } + + /// Called by setPair only in case of Monte Carlo truth + /// Fills MC truth specific histogramms: + /// - kstar distribution plots with RECONSTRUCTED information but ONLY for non-fake candidates; needed for purity calculations of tracks + /// - kstar resolution matrix + /// Note: Standard histogramms with MC truth information are filled with the setPairBase function + /// \param part1 Particle one + /// \param part2 Particle two + /// \param mult Multiplicity of the event + void setPairMC(const float /*femtoObsMC*/, const float /*femtoObs*/, const float /*mT*/, const int /*mult*/) + { + if (mHistogramRegistry) { + // Fill the kstar distributions with the reconstructed information but only for particles with the right PDG code + } + } + + /// Templated function to handle data/ Monte Carlo reconstructed and Monte Carlo truth + /// Always calls setPairBase to compute the observables with reconstructed data + /// In case of Monte Carlo, calls setPairBase with MC info and specialized function setPairMC for additional histogramms + /// \tparam T type of the femtoflowparticle + /// \param part1 Particle one + /// \param part2 Particle two + /// \param mult Multiplicity of the event + template + void setPair(T const& part1, T const& part2, const int mult, bool use3dplots, float weight = 1.0f) + { + using namespace o2::framework; + + float femtoObs, femtoObsMC; + // Calculate femto observable and the mT with reconstructed information + if constexpr (FemtoObs == femto_flow_angular_container::Observable::kstar) { + femtoObs = FemtoFlowMath::getkstar(part1, mMassOne, part2, mMassTwo); + } + const float mT = FemtoFlowMath::getmT(part1, mMassOne, part2, mMassTwo); + + if (mHistogramRegistry) { + setPairBase(femtoObs, mT, part1, part2, mult, use3dplots, weight); + + if constexpr (isMC) { + if (part1.has_fDMCParticle() && part2.has_fDMCParticle()) { + // calculate the femto observable and the mT with MC truth information + if constexpr (FemtoObs == femto_flow_angular_container::Observable::kstar) { + femtoObsMC = FemtoFlowMath::getkstar(part1.fDMCParticle(), mMassOne, part2.fDMCParticle(), mMassTwo); + } + const float mTMC = FemtoFlowMath::getmT(part1.fDMCParticle(), mMassOne, part2.fDMCParticle(), mMassTwo); + + if (std::abs(part1.fDMCParticle().pdgMCTruth()) == std::abs(mPDGOne) && std::abs(part2.fDMCParticle().pdgMCTruth()) == std::abs(mPDGTwo)) { // Note: all pair-histogramms are filled with MC truth information ONLY in case of non-fake candidates + setPairBase(femtoObsMC, mTMC, part1.fDMCParticle(), part2.fDMCParticle(), mult, use3dplots, weight); + setPairMC(femtoObsMC, femtoObs, mT, mult); + } else { + } + + } else { + } + } + } + } + + protected: + o2::framework::HistogramRegistry* mHistogramRegistry = nullptr; ///< For QA output + static constexpr std::string_view FolderSuffix[2] = {"SameEvent", "MixedEvent"}; ///< Folder naming for the output according to EventType + static constexpr femto_flow_angular_container::Observable FemtoObs = obs; ///< Femtoscopic observable to be computed (according to femto_flow_angular_container::Observable) + static constexpr int EventType = eventType; ///< Type of the event (same/mixed, according to femto_flow_angular_container::EventType) + float mMassOne = 0.f; ///< PDG mass of particle 1 + float mMassTwo = 0.f; ///< PDG mass of particle 2 + int mPDGOne = 0; ///< PDG code of particle 1 + int mPDGTwo = 0; ///< PDG code of particle 2 + double mPhiLow; + double mPhiHigh; + double deltaEta; + double deltaPhi; +}; + +} // namespace o2::analysis::femto_flow + +#endif // PWGCF_FEMTO_CORE_FEMTOFLOWANGULARCONTAINER_H_ diff --git a/PWGCF/Femto/Core/FemtoFlowCollisionSelection.h b/PWGCF/Femto/Core/FemtoFlowCollisionSelection.h new file mode 100644 index 00000000000..28db72c1486 --- /dev/null +++ b/PWGCF/Femto/Core/FemtoFlowCollisionSelection.h @@ -0,0 +1,368 @@ +// 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 FemtoFlowCollisionSelection.h +/// \brief FemtoFlowCollisionSelection - event selection within the o2femtoflow framework +/// \author Wenya Wu, TU München, wenya.wu@cern.ch +/// \note The femtoflow borrow and copy the framework from femtodream and femtouniverse + +#ifndef PWGCF_FEMTO_CORE_FEMTOFLOWCOLLISIONSELECTION_H_ +#define PWGCF_FEMTO_CORE_FEMTOFLOWCOLLISIONSELECTION_H_ + +#include "Common/CCDB/TriggerAliases.h" +#include "Common/Core/EventPlaneHelper.h" +#include "Common/DataModel/Qvectors.h" + +#include "Framework/HistogramRegistry.h" +#include "Framework/Logger.h" + +#include + +namespace o2::analysis::femto_flow +{ + +/// \class FemtoFlowCollisionSelection +/// \brief Small selection class to check whether a given collision fulfills the specified selections +class FemtoFlowCollisionSelection +{ + public: + /// Destructor + virtual ~FemtoFlowCollisionSelection() = default; + + /// Pass the selection criteria to the class + /// \param zvtxMax Maximal value of the z-vertex + /// \param checkTrigger Whether or not to check for the trigger alias + /// \param trig Requested trigger alias + /// \param checkOffline Whether or not to check for offline selection criteria + /// \param checkRun3 To check for the Run3 data + /// \param centmin Minimum value of centrality selection + /// \param centmax Maximum value of centrality selection + void setCuts(float zvtxMax, bool checkTrigger, int trig, bool checkOffline, bool checkRun3, float centmin, float centmax) + // void setCuts(float zvtxMax, bool checkTrigger, int trig, bool checkOffline, bool checkRun3) + { + mCutsSet = true; + mZvtxMax = zvtxMax; + mCheckTrigger = checkTrigger; + mTrigger = static_cast(trig); + mCheckOffline = checkOffline; + mCheckIsRun3 = checkRun3; + mCentMin = centmin; + mCentMax = centmax; + } + + /// Initializes histograms for the task + /// \param registry Histogram registry to be passed + void init(o2::framework::HistogramRegistry* registry) + { + using namespace o2::framework; + + if (!mCutsSet) { + LOGF(error, "Event selection not set - quitting!"); + } + mHistogramRegistry = registry; + mHistogramRegistry->add("Event/zvtxhist", "; vtx_{z} (cm); Entries", kTH1F, {{300, -12.5, 12.5}}); + mHistogramRegistry->add("Event/MultV0M", "; vMultV0M; Entries", kTH1F, {{16384, 0, 32768}}); + mHistogramRegistry->add("Event/MultT0M", "; vMultT0M; Entries", kTH1F, {{4096, 0, 8192}}); + mHistogramRegistry->add("Event/MultNTracksPV", "; vMultNTracksPV; Entries", kTH1F, {{120, 0, 120}}); + mHistogramRegistry->add("Event/MultNTracklets", "; vMultNTrackslets; Entries", kTH1F, {{300, 0, 300}}); + mHistogramRegistry->add("Event/MultTPC", "; vMultTPC; Entries", kTH1I, {{600, 0, 600}}); + mHistogramRegistry->add("Event/Sphericity", "; Sphericity; Entries", kTH1I, {{200, 0, 3}}); + mHistogramRegistry->add("Event/qnvector", "; Centrality; qn", kTH2F, {{100, 0, 100}, {1000, 0, 1000}}); + mHistogramRegistry->add("Event/SphrVsqn", "; qn; Sphericity", kTH2F, {{1000, 0, 1000}, {200, 0, 3}}); + } + + /// Print some debug information + void printCuts() + { + LOG(info) << "Debug information for FemtoFlowCollisionSelection"; + LOG(info) << "Max. z-vertex: " << mZvtxMax; + LOG(info) << "Check trigger: " << mCheckTrigger; + LOG(info) << "Trigger: " << mTrigger; + LOG(info) << " Check offline: " << mCheckOffline; + LOG(info) << " Minimum Centrality: " << mCentMin; + LOG(info) << " Maximum Centrality: " << mCentMax; + } + + /// Check whether the collisions fulfills the specified selections + /// \tparam T type of the collision + /// \param col Collision + /// \return whether or not the collisions fulfills the specified selections + template + bool isSelected(T const& col) + { + if (std::abs(col.posZ()) > mZvtxMax) { + return false; + } + if ((col.centFT0C() < mCentMin) || (col.centFT0C() > mCentMax)) { + return false; + } + if (mCheckIsRun3) { + if (mCheckOffline && !col.sel8()) { + return false; + } + } else { + if (mCheckTrigger && !col.alias_bit(mTrigger)) { + return false; + } + if (mCheckOffline && !col.sel7()) { + return false; + } + } + return true; + } + + /// Check whether the collisions fulfills the specified selections for Run3 + /// \tparam T type of the collision + /// \param col Collision + /// \return whether or not the collisions fulfills the specified selections + template + bool isSelectedRun3(T const& col) + { + if (std::abs(col.posZ()) > mZvtxMax) { + return false; + } + if (mCheckOffline && !col.sel8()) { + return false; + } + if ((col.centFT0C() < mCentMin) || (col.centFT0C() > mCentMax)) { + return false; + } + return true; + } + + /// Some basic QA of the event + /// \tparam T type of the collision + /// \param col Collision + template + void fillQA(T const& col) + { + using namespace o2::framework; + + if (mHistogramRegistry) { + mHistogramRegistry->fill(HIST("Event/zvtxhist"), col.posZ()); + mHistogramRegistry->fill(HIST("Event/MultT0M"), col.multFT0M()); + mHistogramRegistry->fill(HIST("Event/MultNTracksPV"), col.multNTracksPV()); + mHistogramRegistry->fill(HIST("Event/MultNTracklets"), col.multTracklets()); + mHistogramRegistry->fill(HIST("Event/MultTPC"), col.multTPC()); + if (mCheckIsRun3) { + mHistogramRegistry->fill(HIST("Event/MultV0M"), col.multFV0M()); + } else { + mHistogramRegistry->fill(HIST("Event/MultV0M"), 0.5 * (col.multFV0M())); // in AliPhysics, the VOM was defined by (V0A + V0C)/2. + } + } + } + + /// Compute the sphericity of an event + /// \tparam T1 type of the collision + /// \tparam T2 type of the tracks + /// \param col Collision + /// \param tracks All tracks + /// \return value of the sphericity of the event + template + float computeSphericity(T1 const& /*col*/, T2 const& tracks) + { + double kS00 = 0; + double kS11 = 0; + double kS10 = 0; + double sumPt = 0; + int partNumber = 0; + double spher = 0; + + const double zeroVal = 0; + const double countPartNumLimit = 2; + const double defualtSphr = 2; + + for (const auto& p : tracks) { + double phi = p.phi(); + double pT = p.pt(); + double px = pT * std::cos(phi); + double py = pT * std::sin(phi); + + kS00 = kS00 + px * px / pT; + kS11 = kS11 + py * py / pT; + kS10 = kS10 + px * py / pT; + sumPt = sumPt + pT; + partNumber++; + } + + if (sumPt != 0) { + kS00 = kS00 / sumPt; + kS11 = kS11 / sumPt; + kS10 = kS10 / sumPt; + + double lambda1 = (kS00 + kS11 + std::sqrt((kS00 + kS11) * (kS00 + kS11) - 4.0 * (kS00 * kS11 - kS10 * kS10))) / 2.0; + double lambda2 = (kS00 + kS11 - std::sqrt((kS00 + kS11) * (kS00 + kS11) - 4.0 * (kS00 * kS11 - kS10 * kS10))) / 2.0; + + if ((lambda1 + lambda2) != zeroVal && partNumber > countPartNumLimit) { + spher = 2 * lambda2 / (lambda1 + lambda2); + } else { + spher = defualtSphr; + } + } else { + spher = defualtSphr; + } + + if (mHistogramRegistry) { + mHistogramRegistry->fill(HIST("Event/Sphericity"), spher); + mSphericity = spher; + } + return spher; + } + + // Qn-vector calculation + template + float computeqnVec(T const& col) + { + double qn = std::sqrt(col.qvecFT0CReVec()[0] * col.qvecFT0CReVec()[0] + col.qvecFT0CImVec()[0] * col.qvecFT0CImVec()[0]) * std::sqrt(col.sumAmplFT0C()); + if (mHistogramRegistry) { + mHistogramRegistry->fill(HIST("Event/qnvector"), col.centFT0C(), qn); + mHistogramRegistry->fill(HIST("Event/SphrVsqn"), qn, mSphericity); + } + return qn; + } + + // Qn-vector calculation + template + int myqnBin(T const& col, float centBinLength = 1.f) + { + int qnBin = -999; + float qn = computeqnVec(col); + int mycentBin = static_cast(col.centFT0C() / centBinLength); + if (mycentBin >= static_cast(mCentMax / centBinLength)) + return qnBin; + + for (int iqn(0); iqn < static_cast(std::size(mqnBinSeparator[mycentBin])) - 1; ++iqn) { + if (qn > mqnBinSeparator[mycentBin][iqn] && qn <= mqnBinSeparator[mycentBin][iqn + 1]) { + qnBin = iqn; + break; + } else { + continue; + } + } + + return qnBin; + } + + private: + o2::framework::HistogramRegistry* mHistogramRegistry = nullptr; ///< For QA output + bool mCutsSet = false; ///< Protection against running without cuts + bool mCheckTrigger = false; ///< Check for trigger + bool mCheckOffline = false; ///< Check for offline criteria (might change) + bool mCheckIsRun3 = false; ///< Check if running on Pilot Beam + triggerAliases mTrigger = kINT7; ///< Trigger to check for + float mZvtxMax = 999.f; ///< Maximal deviation from nominal z-vertex (cm) + float mCentMin = 0.0; ///< Minimum centrality value + float mCentMax = 98.0; ///< Maximum centrality value + float mSphericity = 2.; + float mqnBinSeparator[98][11] = { + {0.50, 68.50, 100.50, 126.50, 151.50, 176.50, 203.50, 232.50, 269.50, 322.50, 833.50}, // cent 0 to 1 + {0.50, 66.50, 97.50, 122.50, 147.50, 171.50, 197.50, 226.50, 261.50, 313.50, 821.50}, // cent 1 to 2 + {0.50, 65.50, 95.50, 120.50, 144.50, 168.50, 193.50, 221.50, 256.50, 307.50, 876.50}, // cent 2 to 3 + {0.50, 64.50, 93.50, 118.50, 141.50, 165.50, 190.50, 217.50, 251.50, 300.50, 836.50}, // cent 3 to 4 + {0.50, 63.50, 92.50, 116.50, 139.50, 162.50, 186.50, 214.50, 247.50, 294.50, 732.50}, // cent 4 to 5 + {0.50, 62.50, 91.50, 115.50, 137.50, 160.50, 183.50, 210.50, 242.50, 288.50, 692.50}, // cent 5 to 6 + {0.50, 62.50, 90.50, 114.50, 136.50, 158.50, 181.50, 207.50, 238.50, 283.50, 696.50}, // cent 6 to 7 + {0.50, 61.50, 89.50, 113.50, 134.50, 156.50, 178.50, 203.50, 233.50, 277.50, 681.50}, // cent 7 to 8 + {0.50, 61.50, 88.50, 111.50, 133.50, 154.50, 176.50, 200.50, 229.50, 271.50, 648.50}, // cent 8 to 9 + {0.50, 61.50, 88.50, 110.50, 131.50, 152.50, 173.50, 197.50, 225.50, 265.50, 616.50}, // cent 9 to 10 + {0.50, 60.50, 87.50, 109.50, 129.50, 149.50, 170.50, 193.50, 221.50, 260.50, 615.50}, // cent 10 to 11 + {0.50, 59.50, 86.50, 108.50, 128.50, 147.50, 168.50, 190.50, 217.50, 254.50, 586.50}, // cent 11 to 12 + {0.50, 59.50, 85.50, 106.50, 126.50, 145.50, 165.50, 187.50, 213.50, 249.50, 583.50}, // cent 12 to 13 + {0.50, 58.50, 84.50, 105.50, 124.50, 143.50, 162.50, 183.50, 209.50, 244.50, 542.50}, // cent 13 to 14 + {0.50, 58.50, 83.50, 104.50, 122.50, 141.50, 160.50, 180.50, 205.50, 239.50, 544.50}, // cent 14 to 15 + {0.50, 57.50, 82.50, 102.50, 120.50, 138.50, 157.50, 177.50, 201.50, 234.50, 534.50}, // cent 15 to 16 + {0.50, 56.50, 81.50, 101.50, 119.50, 136.50, 154.50, 174.50, 197.50, 230.50, 530.50}, // cent 16 to 17 + {0.50, 56.50, 80.50, 99.50, 117.50, 134.50, 151.50, 170.50, 193.50, 225.50, 508.50}, // cent 17 to 18 + {0.50, 55.50, 78.50, 98.50, 115.50, 132.50, 149.50, 167.50, 190.50, 221.50, 478.50}, // cent 18 to 19 + {0.50, 54.50, 77.50, 96.50, 113.50, 129.50, 146.50, 164.50, 186.50, 216.50, 508.50}, // cent 19 to 20 + {0.50, 53.50, 76.50, 94.50, 111.50, 127.50, 143.50, 161.50, 183.50, 212.50, 482.50}, // cent 20 to 21 + {0.50, 52.50, 75.50, 93.50, 109.50, 125.50, 141.50, 158.50, 179.50, 208.50, 468.50}, // cent 21 to 22 + {0.50, 51.50, 73.50, 91.50, 107.50, 122.50, 138.50, 155.50, 176.50, 204.50, 436.50}, // cent 22 to 23 + {0.50, 50.50, 72.50, 89.50, 105.50, 120.50, 136.50, 152.50, 172.50, 200.50, 440.50}, // cent 23 to 24 + {0.50, 49.50, 71.50, 88.50, 103.50, 118.50, 133.50, 149.50, 169.50, 196.50, 441.50}, // cent 24 to 25 + {0.50, 48.50, 69.50, 86.50, 101.50, 115.50, 130.50, 146.50, 166.50, 193.50, 412.50}, // cent 25 to 26 + {0.50, 47.50, 68.50, 84.50, 99.50, 113.50, 128.50, 144.50, 162.50, 189.50, 410.50}, // cent 26 to 27 + {0.50, 46.50, 66.50, 82.50, 97.50, 111.50, 125.50, 141.50, 159.50, 185.50, 409.50}, // cent 27 to 28 + {0.50, 46.50, 65.50, 81.50, 95.50, 109.50, 123.50, 138.50, 156.50, 182.50, 405.50}, // cent 28 to 29 + {0.50, 44.50, 63.50, 79.50, 93.50, 106.50, 120.50, 135.50, 153.50, 178.50, 392.50}, // cent 29 to 30 + {0.50, 43.50, 62.50, 77.50, 91.50, 104.50, 118.50, 132.50, 150.50, 174.50, 367.50}, // cent 30 to 31 + {0.50, 42.50, 61.50, 75.50, 89.50, 102.50, 115.50, 130.50, 147.50, 171.50, 368.50}, // cent 31 to 32 + {0.50, 41.50, 59.50, 74.50, 87.50, 100.50, 113.50, 127.50, 144.50, 168.50, 371.50}, // cent 32 to 33 + {0.50, 40.50, 58.50, 72.50, 85.50, 97.50, 110.50, 124.50, 141.50, 164.50, 369.50}, // cent 33 to 34 + {0.50, 39.50, 56.50, 70.50, 83.50, 95.50, 108.50, 121.50, 138.50, 161.50, 363.50}, // cent 34 to 35 + {0.50, 38.50, 55.50, 69.50, 81.50, 93.50, 105.50, 119.50, 135.50, 158.50, 353.50}, // cent 35 to 36 + {0.50, 37.50, 54.50, 67.50, 79.50, 91.50, 103.50, 116.50, 132.50, 154.50, 333.50}, // cent 36 to 37 + {0.50, 36.50, 52.50, 65.50, 77.50, 89.50, 101.50, 114.50, 129.50, 151.50, 333.50}, // cent 37 to 38 + {0.50, 35.50, 51.50, 64.50, 75.50, 87.50, 98.50, 111.50, 126.50, 148.50, 330.50}, // cent 38 to 39 + {0.50, 34.50, 49.50, 62.50, 73.50, 85.50, 96.50, 109.50, 124.50, 145.50, 322.50}, // cent 39 to 40 + {0.50, 33.50, 48.50, 60.50, 71.50, 82.50, 94.50, 106.50, 121.50, 142.50, 319.50}, // cent 40 to 41 + {0.50, 32.50, 47.50, 59.50, 70.50, 80.50, 91.50, 104.50, 118.50, 138.50, 321.50}, // cent 41 to 42 + {0.50, 31.50, 46.50, 57.50, 68.50, 78.50, 89.50, 101.50, 115.50, 135.50, 309.50}, // cent 42 to 43 + {0.50, 31.50, 44.50, 56.50, 66.50, 76.50, 87.50, 99.50, 113.50, 132.50, 311.50}, // cent 43 to 44 + {0.50, 30.50, 43.50, 54.50, 64.50, 74.50, 85.50, 96.50, 110.50, 129.50, 293.50}, // cent 44 to 45 + {0.50, 29.50, 42.50, 53.50, 63.50, 72.50, 82.50, 94.50, 107.50, 126.50, 307.50}, // cent 45 to 46 + {0.50, 28.50, 41.50, 51.50, 61.50, 70.50, 80.50, 91.50, 104.50, 123.50, 290.50}, // cent 46 to 47 + {0.50, 27.50, 39.50, 50.50, 59.50, 68.50, 78.50, 89.50, 102.50, 120.50, 277.50}, // cent 47 to 48 + {0.50, 26.50, 38.50, 48.50, 57.50, 67.50, 76.50, 87.50, 99.50, 117.50, 285.50}, // cent 48 to 49 + {0.50, 25.50, 37.50, 47.50, 56.50, 65.50, 74.50, 84.50, 97.50, 114.50, 264.50}, // cent 49 to 50 + {0.50, 25.50, 36.50, 45.50, 54.50, 63.50, 72.50, 82.50, 94.50, 111.50, 265.50}, // cent 50 to 51 + {0.50, 24.50, 35.50, 44.50, 53.50, 61.50, 70.50, 80.50, 91.50, 108.50, 254.50}, // cent 51 to 52 + {0.50, 23.50, 34.50, 43.50, 51.50, 59.50, 68.50, 77.50, 89.50, 105.50, 256.50}, // cent 52 to 53 + {0.50, 22.50, 33.50, 41.50, 50.50, 58.50, 66.50, 75.50, 86.50, 103.50, 235.50}, // cent 53 to 54 + {0.50, 22.50, 32.50, 40.50, 48.50, 56.50, 64.50, 73.50, 84.50, 100.50, 245.50}, // cent 54 to 55 + {0.50, 21.50, 31.50, 39.50, 47.50, 54.50, 62.50, 71.50, 82.50, 97.50, 239.50}, // cent 55 to 56 + {0.50, 20.50, 30.50, 38.50, 45.50, 52.50, 60.50, 69.50, 79.50, 94.50, 227.50}, // cent 56 to 57 + {0.50, 20.50, 29.50, 36.50, 44.50, 51.50, 58.50, 67.50, 77.50, 91.50, 229.50}, // cent 57 to 58 + {0.50, 19.50, 28.50, 35.50, 42.50, 49.50, 56.50, 65.50, 74.50, 89.50, 227.50}, // cent 58 to 59 + {0.50, 18.50, 27.50, 34.50, 41.50, 48.50, 55.50, 62.50, 72.50, 86.50, 216.50}, // cent 59 to 60 + {0.50, 18.50, 26.50, 33.50, 39.50, 46.50, 53.50, 60.50, 70.50, 83.50, 231.50}, // cent 60 to 61 + {0.50, 17.50, 25.50, 32.50, 38.50, 44.50, 51.50, 58.50, 67.50, 80.50, 194.50}, // cent 61 to 62 + {0.50, 17.50, 24.50, 31.50, 37.50, 43.50, 49.50, 57.50, 65.50, 78.50, 190.50}, // cent 62 to 63 + {0.50, 16.50, 23.50, 30.50, 36.50, 41.50, 48.50, 55.50, 63.50, 75.50, 200.50}, // cent 63 to 64 + {0.50, 15.50, 23.50, 29.50, 34.50, 40.50, 46.50, 53.50, 61.50, 73.50, 183.50}, // cent 64 to 65 + {0.50, 15.50, 22.50, 28.50, 33.50, 39.50, 44.50, 51.50, 59.50, 70.50, 187.50}, // cent 65 to 66 + {0.50, 14.50, 21.50, 27.50, 32.50, 37.50, 43.50, 49.50, 57.50, 68.50, 199.50}, // cent 66 to 67 + {0.50, 14.50, 20.50, 26.50, 31.50, 36.50, 41.50, 47.50, 55.50, 65.50, 171.50}, // cent 67 to 68 + {0.50, 13.50, 19.50, 25.50, 30.50, 34.50, 40.50, 45.50, 53.50, 63.50, 157.50}, // cent 68 to 69 + {0.50, 13.50, 19.50, 24.50, 28.50, 33.50, 38.50, 44.50, 51.50, 60.50, 156.50}, // cent 69 to 70 + {0.50, 12.50, 18.50, 23.50, 27.50, 32.50, 37.50, 42.50, 49.50, 58.50, 157.50}, // cent 70 to 71 + {0.50, 12.50, 17.50, 22.50, 26.50, 31.50, 35.50, 40.50, 47.50, 56.50, 148.50}, // cent 71 to 72 + {0.50, 11.50, 16.50, 21.50, 25.50, 29.50, 34.50, 39.50, 45.50, 54.50, 218.50}, // cent 72 to 73 + {0.50, 11.50, 16.50, 20.50, 24.50, 28.50, 32.50, 37.50, 43.50, 52.50, 201.50}, // cent 73 to 74 + {0.50, 10.50, 15.50, 19.50, 23.50, 27.50, 31.50, 36.50, 41.50, 49.50, 185.50}, // cent 74 to 75 + {0.50, 10.50, 14.50, 18.50, 22.50, 26.50, 30.50, 34.50, 40.50, 47.50, 169.50}, // cent 75 to 76 + {0.50, 9.50, 14.50, 18.50, 21.50, 25.50, 29.50, 33.50, 38.50, 45.50, 156.50}, // cent 76 to 77 + {0.50, 9.50, 13.50, 17.50, 20.50, 24.50, 27.50, 31.50, 36.50, 43.50, 150.50}, // cent 77 to 78 + {0.50, 9.50, 13.50, 16.50, 19.50, 23.50, 26.50, 30.50, 35.50, 42.50, 138.50}, // cent 78 to 79 + {0.50, 8.50, 12.50, 15.50, 19.50, 22.50, 25.50, 29.50, 33.50, 40.50, 127.50}, // cent 79 to 80 + {0.50, 8.50, 12.50, 15.50, 18.50, 21.50, 24.50, 27.50, 32.50, 38.50, 119.50}, // cent 80 to 81 + {0.50, 7.50, 11.50, 14.50, 17.50, 20.50, 23.50, 26.50, 30.50, 36.50, 105.50}, // cent 81 to 82 + {0.50, 7.50, 10.50, 13.50, 16.50, 19.50, 22.50, 25.50, 29.50, 35.50, 107.50}, // cent 82 to 83 + {0.50, 7.50, 10.50, 13.50, 15.50, 18.50, 21.50, 24.50, 28.50, 33.50, 95.50}, // cent 83 to 84 + {0.50, 6.50, 10.50, 12.50, 15.50, 17.50, 20.50, 23.50, 26.50, 31.50, 94.50}, // cent 84 to 85 + {0.50, 6.50, 9.50, 12.50, 14.50, 16.50, 19.50, 22.50, 25.50, 30.50, 89.50}, // cent 85 to 86 + {0.50, 6.50, 9.50, 11.50, 13.50, 15.50, 18.50, 20.50, 24.50, 28.50, 79.50}, // cent 86 to 87 + {0.50, 5.50, 8.50, 10.50, 13.50, 15.50, 17.50, 19.50, 22.50, 27.50, 82.50}, // cent 87 to 88 + {0.50, 5.50, 8.50, 10.50, 12.50, 14.50, 16.50, 18.50, 21.50, 25.50, 91.50}, // cent 88 to 89 + {0.50, 5.50, 7.50, 9.50, 11.50, 13.50, 15.50, 17.50, 20.50, 24.50, 74.50}, // cent 89 to 90 + {0.50, 5.50, 7.50, 9.50, 11.50, 12.50, 14.50, 16.50, 19.50, 23.50, 72.50}, // cent 90 to 91 + {0.50, 4.50, 6.50, 8.50, 10.50, 12.50, 14.50, 16.50, 18.50, 21.50, 63.50}, // cent 91 to 92 + {0.50, 4.50, 6.50, 8.50, 9.50, 11.50, 13.50, 15.50, 17.50, 20.50, 70.50}, // cent 92 to 93 + {0.50, 4.50, 6.50, 7.50, 9.50, 10.50, 12.50, 14.50, 16.50, 19.50, 56.50}, // cent 93 to 94 + {0.50, 4.50, 5.50, 7.50, 8.50, 10.50, 11.50, 13.50, 15.50, 18.50, 62.50}, // cent 94 to 95 + {0.50, 3.50, 5.50, 6.50, 8.50, 9.50, 10.50, 12.50, 14.50, 16.50, 55.50}, // cent 95 to 96 + {0.50, 3.50, 5.50, 6.50, 7.50, 8.50, 10.50, 11.50, 13.50, 15.50, 54.50}, // cent 96 to 97 + {0.50, 3.50, 4.50, 5.50, 6.50, 7.50, 9.50, 10.50, 11.50, 13.50, 44.50} // cent 97 to 98 + }; ///< qn bin edge from qn vector distributions, for per 1% centrality, 0-98% +}; +} // namespace o2::analysis::femto_flow + +#endif // PWGCF_FEMTO_CORE_FEMTOFLOWCOLLISIONSELECTION_H_ diff --git a/PWGCF/Femto/Core/FemtoFlowDetaDphiStar.h b/PWGCF/Femto/Core/FemtoFlowDetaDphiStar.h new file mode 100644 index 00000000000..1efd61f4673 --- /dev/null +++ b/PWGCF/Femto/Core/FemtoFlowDetaDphiStar.h @@ -0,0 +1,518 @@ +// 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 FemtoFlowDetaDphiStar.h +/// \brief FemtoFlowDetaDphiStar - Checks particles for the close pair rejection. +/// \author Laura Serksnyte, TU München, laura.serksnyte@tum.de +/// \author Zuzanna Chochulska, WUT Warsaw & CTU Prague, zchochul@cern.ch +/// \author Pritam Chakraborty, WUT Warsaw, pritam.chakraborty@cern.ch +/// \author Shirajum Monira, WUT Warsaw, shirajum.monira@cern.ch + +#ifndef PWGCF_FEMTO_CORE_FEMTOFLOWDETADPHISTAR_H_ +#define PWGCF_FEMTO_CORE_FEMTOFLOWDETADPHISTAR_H_ + +#include "PWGCF/Femto/Core/FemtoFlowAngularContainer.h" +#include "PWGCF/Femto/Core/FemtoFlowFemtoContainer.h" +#include "PWGCF/Femto/Core/FemtoFlowTrackSelection.h" +#include "PWGCF/Femto/DataModel/FemtoDerived.h" + +#include "Common/Core/RecoDecay.h" + +#include "Framework/HistogramRegistry.h" + +#include "TMath.h" + +#include +#include +#include + +namespace o2::analysis +{ + +namespace femto_flow +{ + +/// \class FemtoFlowDetaDphiStar +/// \brief Class to check particles for the close pair rejection. +/// \tparam partOne Type of particle 1 (Track/V0/Cascade/...) +/// \tparam partTwo Type of particle 2 (Track/V0/Cascade/...) +template +class FemtoFlowDetaDphiStar +{ + public: + FemtoFlowTrackSelection trackCuts; + /// Destructor + virtual ~FemtoFlowDetaDphiStar() = default; + /// Initialization of the histograms and setting required values + void init(o2::framework::HistogramRegistry* registry, o2::framework::HistogramRegistry* registryQA, float ldeltaphistarcutmin, float ldeltaphistarcutmax, float ldeltaetacutmin, float ldeltaetacutmax, float lchosenradii, bool lplotForEveryRadii, float lPhiMassMin = 1.014, float lPhiMassMax = 1.026, bool lisSameSignCPR = false) + { + using namespace o2::framework; + + chosenRadii = lchosenradii; + cutDeltaPhiStarMax = ldeltaphistarcutmax; + cutDeltaPhiStarMin = ldeltaphistarcutmin; + cutDeltaEtaMax = ldeltaetacutmax; + cutDeltaEtaMin = ldeltaetacutmin; + plotForEveryRadii = lplotForEveryRadii; + mHistogramRegistry = registry; + mHistogramRegistryQA = registryQA; + cutPhiInvMassLow = lPhiMassMin; + cutPhiInvMassHigh = lPhiMassMax; + isSameSignCPR = lisSameSignCPR; + + const int totNumHistEveryRadii = 9; + const int totNumHistEveryRadiiV0 = 2; + const int totNumHistEveryRadiiXi = 7; + + if constexpr (kPartOneType == o2::aod::femtoflowparticle::ParticleType::kTrack && kPartTwoType == o2::aod::femtoflowparticle::ParticleType::kTrack) { + std::string dirName = static_cast(DirNames[0]); + histdetadpisame[0][0] = mHistogramRegistry->add((dirName + static_cast(HistNamesSame[0][0])).c_str(), "; #Delta #eta; #Delta #phi", kTH2F, {{100, -0.15, 0.15}, {100, -0.15, 0.15}}); + histdetadpisame[0][1] = mHistogramRegistry->add((dirName + static_cast(HistNamesSame[1][0])).c_str(), "; #Delta #eta; #Delta #phi", kTH2F, {{100, -0.15, 0.15}, {100, -0.15, 0.15}}); + histdetadpimixed[0][0] = mHistogramRegistry->add((dirName + static_cast(HistNamesMixed[0][0])).c_str(), "; #Delta #eta; #Delta #phi", kTH2F, {{100, -0.15, 0.15}, {100, -0.15, 0.15}}); + histdetadpimixed[0][1] = mHistogramRegistry->add((dirName + static_cast(HistNamesMixed[1][0])).c_str(), "; #Delta #eta; #Delta #phi", kTH2F, {{100, -0.15, 0.15}, {100, -0.15, 0.15}}); + + histdetadpiqlcmssame = mHistogramRegistry->add((dirName + static_cast(HistNamesSame[1][7])).c_str(), "; #it{q}_{LCMS}; #Delta #eta; #Delta #phi", kTH3F, {{100, 0.0, 0.5}, {100, -0.15, 0.15}, {100, -0.15, 0.15}}); + histdetadpiqlcmsmixed = mHistogramRegistry->add((dirName + static_cast(HistNamesMixed[1][7])).c_str(), "; #it{q}_{LCMS}; #Delta #eta; #Delta #phi", kTH3F, {{100, 0.0, 0.5}, {100, -0.15, 0.15}, {100, -0.15, 0.15}}); + + if (plotForEveryRadii) { + for (int i = 0; i < totNumHistEveryRadii; i++) { + histdetadpiRadii[0][i] = mHistogramRegistryQA->add((dirName + static_cast(HistNamesRadii[0][i])).c_str(), "; #Delta #eta; #Delta #phi", kTH2F, {{100, -0.15, 0.15}, {100, -0.15, 0.15}}); + } + } + } + if constexpr (kPartOneType == o2::aod::femtoflowparticle::ParticleType::kTrack && kPartTwoType == o2::aod::femtoflowparticle::ParticleType::kV0) { + for (int i = 0; i < totNumHistEveryRadiiV0; i++) { + std::string dirName = static_cast(DirNames[1]); + histdetadpisame[i][0] = mHistogramRegistry->add((dirName + static_cast(HistNamesSame[0][i])).c_str(), "; #Delta #eta; #Delta #phi", kTH2F, {{100, -0.15, 0.15}, {100, -0.15, 0.15}}); + histdetadpisame[i][1] = mHistogramRegistry->add((dirName + static_cast(HistNamesSame[1][i])).c_str(), "; #Delta #eta; #Delta #phi", kTH2F, {{100, -0.15, 0.15}, {100, -0.15, 0.15}}); + histdetadpimixed[i][0] = mHistogramRegistry->add((dirName + static_cast(HistNamesMixed[0][i])).c_str(), "; #Delta #eta; #Delta #phi", kTH2F, {{100, -0.15, 0.15}, {100, -0.15, 0.15}}); + histdetadpimixed[i][1] = mHistogramRegistry->add((dirName + static_cast(HistNamesMixed[1][i])).c_str(), "; #Delta #eta; #Delta #phi", kTH2F, {{100, -0.15, 0.15}, {100, -0.15, 0.15}}); + + if (plotForEveryRadii) { + for (int j = 0; j < totNumHistEveryRadii; j++) { + histdetadpiRadii[i][j] = mHistogramRegistryQA->add((dirName + static_cast(HistNamesRadii[i][j])).c_str(), "; #Delta #eta; #Delta #phi", kTH2F, {{100, -0.15, 0.15}, {100, -0.15, 0.15}}); + } + } + } + } + if constexpr (kPartOneType == o2::aod::femtoflowparticle::ParticleType::kV0 && kPartTwoType == o2::aod::femtoflowparticle::ParticleType::kV0) { + /// V0-V0 combination + for (int k = 0; k < totNumHistEveryRadiiV0; k++) { + std::string dirName = static_cast(DirNames[2]); + histdetadpisame[k][0] = mHistogramRegistry->add((dirName + static_cast(HistNamesSame[0][k])).c_str(), "; #Delta #eta; #Delta #phi", kTH2F, {{100, -0.15, 0.15}, {100, -0.15, 0.15}}); + histdetadpisame[k][1] = mHistogramRegistry->add((dirName + static_cast(HistNamesSame[1][k])).c_str(), "; #Delta #eta; #Delta #phi", kTH2F, {{100, -0.15, 0.15}, {100, -0.15, 0.15}}); + histdetadpimixed[k][0] = mHistogramRegistry->add((dirName + static_cast(HistNamesMixed[0][k])).c_str(), "; #Delta #eta; #Delta #phi", kTH2F, {{100, -0.15, 0.15}, {100, -0.15, 0.15}}); + histdetadpimixed[k][1] = mHistogramRegistry->add((dirName + static_cast(HistNamesMixed[1][k])).c_str(), "; #Delta #eta; #Delta #phi", kTH2F, {{100, -0.15, 0.15}, {100, -0.15, 0.15}}); + if (plotForEveryRadii) { + for (int l = 0; l < totNumHistEveryRadii; l++) { + histdetadpiRadii[k][l] = mHistogramRegistryQA->add((dirName + static_cast(HistNamesRadii[k][l])).c_str(), "; #Delta #eta; #Delta #phi", kTH2F, {{100, -0.15, 0.15}, {100, -0.15, 0.15}}); + } + } + } + } + if constexpr (kPartOneType == o2::aod::femtoflowparticle::ParticleType::kCascade && kPartTwoType == o2::aod::femtoflowparticle::ParticleType::kCascade) { + /// Xi-Xi and Omega-Omega combination + for (int k = 0; k < totNumHistEveryRadiiXi; k++) { + std::string dirName = static_cast(DirNames[5]); + histdetadpisame[k][0] = mHistogramRegistry->add((dirName + static_cast(HistNamesSame[0][k])).c_str(), "; #Delta #eta; #Delta #phi", kTH2F, {{100, -0.15, 0.15}, {100, -0.15, 0.15}}); + histdetadpisame[k][1] = mHistogramRegistry->add((dirName + static_cast(HistNamesSame[1][k])).c_str(), "; #Delta #eta; #Delta #phi", kTH2F, {{100, -0.15, 0.15}, {100, -0.15, 0.15}}); + histdetadpimixed[k][0] = mHistogramRegistry->add((dirName + static_cast(HistNamesMixed[0][k])).c_str(), "; #Delta #eta; #Delta #phi", kTH2F, {{100, -0.15, 0.15}, {100, -0.15, 0.15}}); + histdetadpimixed[k][1] = mHistogramRegistry->add((dirName + static_cast(HistNamesMixed[1][k])).c_str(), "; #Delta #eta; #Delta #phi", kTH2F, {{100, -0.15, 0.15}, {100, -0.15, 0.15}}); + if (plotForEveryRadii) { + for (int l = 0; l < totNumHistEveryRadii; l++) { + histdetadpiRadii[k][l] = mHistogramRegistryQA->add((dirName + static_cast(HistNamesRadii[k][l])).c_str(), "; #Delta #eta; #Delta #phi", kTH2F, {{100, -0.15, 0.15}, {100, -0.15, 0.15}}); + } + } + } + } + if constexpr (kPartOneType == o2::aod::femtoflowparticle::ParticleType::kTrack && kPartTwoType == o2::aod::femtoflowparticle::ParticleType::kPhi) { + for (int i = 0; i < totNumHistEveryRadiiV0; i++) { + std::string dirName = static_cast(DirNames[3]); + histdetadpisame[i][0] = mHistogramRegistry->add((dirName + static_cast(HistNamesSame[0][i])).c_str(), "; #Delta #eta; #Delta #varphi*", kTH2F, {{400, -0.30, 0.30}, {400, -0.30, 0.30}}); + histdetadpisame[i][1] = mHistogramRegistry->add((dirName + static_cast(HistNamesSame[1][i])).c_str(), "; #Delta #eta; #Delta #varphi*", kTH2F, {{400, -0.30, 0.30}, {400, -0.30, 0.30}}); + histdetadpimixed[i][0] = mHistogramRegistry->add((dirName + static_cast(HistNamesMixed[0][i])).c_str(), "; #Delta #eta; #Delta #varphi*", kTH2F, {{400, -0.30, 0.30}, {400, -0.30, 0.30}}); + histdetadpimixed[i][1] = mHistogramRegistry->add((dirName + static_cast(HistNamesMixed[1][i])).c_str(), "; #Delta #eta; #Delta #varphi*", kTH2F, {{400, -0.30, 0.30}, {400, -0.30, 0.30}}); + + if (plotForEveryRadii) { + for (int j = 0; j < totNumHistEveryRadii; j++) { + histdetadpiRadii[i][j] = mHistogramRegistryQA->add((dirName + static_cast(HistNamesRadii[i][j])).c_str(), "; #Delta #eta; #Delta #varphi*", kTH2F, {{100, -0.15, 0.15}, {100, -0.15, 0.15}}); + } + } + } + } + if constexpr (kPartOneType == o2::aod::femtoflowparticle::ParticleType::kTrack && kPartTwoType == o2::aod::femtoflowparticle::ParticleType::kD0) { + for (int i = 0; i < totNumHistEveryRadiiV0; i++) { + std::string dirName = static_cast(DirNames[4]); + histdetadpisame[i][0] = mHistogramRegistry->add((dirName + static_cast(HistNamesSame[0][i])).c_str(), "; #Delta #eta; #Delta #phi", kTH2F, {{100, -0.15, 0.15}, {100, -0.15, 0.15}}); + histdetadpisame[i][1] = mHistogramRegistry->add((dirName + static_cast(HistNamesSame[1][i])).c_str(), "; #Delta #eta; #Delta #phi", kTH2F, {{100, -0.15, 0.15}, {100, -0.15, 0.15}}); + histdetadpimixed[i][0] = mHistogramRegistry->add((dirName + static_cast(HistNamesMixed[0][i])).c_str(), "; #Delta #eta; #Delta #phi", kTH2F, {{100, -0.15, 0.15}, {100, -0.15, 0.15}}); + histdetadpimixed[i][1] = mHistogramRegistry->add((dirName + static_cast(HistNamesMixed[1][i])).c_str(), "; #Delta #eta; #Delta #phi", kTH2F, {{100, -0.15, 0.15}, {100, -0.15, 0.15}}); + + if (plotForEveryRadii) { + for (int j = 0; j < totNumHistEveryRadii; j++) { + histdetadpiRadii[i][j] = mHistogramRegistryQA->add((dirName + static_cast(HistNamesRadii[i][j])).c_str(), "; #Delta #eta; #Delta #phi", kTH2F, {{100, -0.15, 0.15}, {100, -0.15, 0.15}}); + } + } + } + } + } + + /// Check if pair is close or not + template + bool isClosePair(Part const& part1, Part const& part2, Parts const& particles, float lmagfield, uint8_t ChosenEventType) + { + using namespace o2::framework; + + magfield = lmagfield; + + const int numEvtType = 2; + + if constexpr (kPartOneType == o2::aod::femtoflowparticle::ParticleType::kTrack && kPartTwoType == o2::aod::femtoflowparticle::ParticleType::kTrack) { + /// Track-Track combination + // check if provided particles are in agreement with the class instantiation + if (part1.partType() != o2::aod::femtoflowparticle::ParticleType::kTrack || part2.partType() != o2::aod::femtoflowparticle::ParticleType::kTrack) { + LOG(fatal) << "FemtoFlowDetaDphiStar: passed arguments don't agree with FemtoFlowDetaDphiStar instantiation! Please provide kTrack,kTrack candidates."; + return false; + } + auto deta = part1.eta() - part2.eta(); + auto dphiAvg = averagePhiStar(part1, part2, 0); + if (ChosenEventType == femto_flow_femto_container::EventType::same) { + histdetadpisame[0][0]->Fill(deta, dphiAvg); + } else if (ChosenEventType == femto_flow_femto_container::EventType::mixed) { + histdetadpimixed[0][0]->Fill(deta, dphiAvg); + } else { + LOG(fatal) << "FemtoFlowDetaDphiStar: passed arguments don't agree with FemtoFlowDetaDphiStar's type of events! Please provide same or mixed."; + } + + if (std::pow(dphiAvg, 2) / std::pow(cutDeltaPhiStarMax, 2) + std::pow(deta, 2) / std::pow(cutDeltaEtaMax, 2) < 1.) { + return true; + } else { + if (ChosenEventType == femto_flow_femto_container::EventType::same) { + histdetadpisame[0][1]->Fill(deta, dphiAvg); + } else if (ChosenEventType == femto_flow_femto_container::EventType::mixed) { + histdetadpimixed[0][1]->Fill(deta, dphiAvg); + } else { + LOG(fatal) << "FemtoFlowDetaDphiStar: passed arguments don't agree with FemtoFlowDetaDphiStar's type of events! Please provide same or mixed."; + } + return false; + } + + } else if constexpr (kPartOneType == o2::aod::femtoflowparticle::ParticleType::kTrack && kPartTwoType == o2::aod::femtoflowparticle::ParticleType::kV0) { + /// Track-V0 combination + // check if provided particles are in agreement with the class instantiation + if (part1.partType() != o2::aod::femtoflowparticle::ParticleType::kTrack || part2.partType() != o2::aod::femtoflowparticle::ParticleType::kV0) { + LOG(fatal) << "FemtoFlowDetaDphiStar: passed arguments don't agree with FemtoFlowDetaDphiStar instantiation! Please provide kTrack,kV0 candidates."; + return false; + } + + bool pass = false; + for (int i = 0; i < numEvtType; i++) { + auto indexOfDaughter = (ChosenEventType == femto_flow_femto_container::EventType::mixed ? part2.globalIndex() : part2.index()) - 2 + i; + // auto indexOfDaughter = part2.globalIndex() - 2 + i; + auto daughter = particles.begin() + indexOfDaughter; + auto deta = part1.eta() - daughter.eta(); + auto dphiAvg = averagePhiStar(part1, *daughter, i); + if (ChosenEventType == femto_flow_femto_container::EventType::same) { + histdetadpisame[i][0]->Fill(deta, dphiAvg); + } else if (ChosenEventType == femto_flow_femto_container::EventType::mixed) { + histdetadpimixed[i][0]->Fill(deta, dphiAvg); + } else { + LOG(fatal) << "FemtoFlowDetaDphiStar: passed arguments don't agree with FemtoFlowDetaDphiStar's type of events! Please provide same or mixed."; + } + + if (std::pow(dphiAvg, 2) / std::pow(cutDeltaPhiStarMax, 2) + std::pow(deta, 2) / std::pow(cutDeltaEtaMax, 2) < 1.) { + pass = true; + } else { + if (ChosenEventType == femto_flow_femto_container::EventType::same) { + histdetadpisame[i][1]->Fill(deta, dphiAvg); + } else if (ChosenEventType == femto_flow_femto_container::EventType::mixed) { + histdetadpimixed[i][1]->Fill(deta, dphiAvg); + } else { + LOG(fatal) << "FemtoFlowDetaDphiStar: passed arguments don't agree with FemtoFlowDetaDphiStar's type of events! Please provide same or mixed."; + } + } + } + return pass; + + } else if constexpr (kPartOneType == o2::aod::femtoflowparticle::ParticleType::kV0 && kPartTwoType == o2::aod::femtoflowparticle::ParticleType::kV0) { + /// V0-V0 combination + if (part1.partType() != o2::aod::femtoflowparticle::ParticleType::kV0 || part2.partType() != o2::aod::femtoflowparticle::ParticleType::kV0) { + LOG(fatal) << "FemtoFlowDetaDphiStar: passed arguments don't agree with FemtoFlowDetaDphiStar instantiation! Please provide kV0,kV0 candidates."; + return false; + } + + bool pass = false; + for (int i = 0; i < numEvtType; i++) { + auto indexOfDaughterpart1 = (ChosenEventType == femto_flow_femto_container::EventType::mixed ? part1.globalIndex() : part1.index()) - 2 + i; + auto indexOfDaughterpart2 = (ChosenEventType == femto_flow_femto_container::EventType::mixed ? part2.globalIndex() : part2.index()) - 2 + i; + auto daughterpart1 = particles.begin() + indexOfDaughterpart1; + auto daughterpart2 = particles.begin() + indexOfDaughterpart2; + auto deta = daughterpart1.eta() - daughterpart2.eta(); + auto dphiAvg = averagePhiStar(*daughterpart1, *daughterpart2, i); + if (ChosenEventType == femto_flow_femto_container::EventType::same) { + histdetadpisame[i][0]->Fill(deta, dphiAvg); + } else if (ChosenEventType == femto_flow_femto_container::EventType::mixed) { + histdetadpimixed[i][0]->Fill(deta, dphiAvg); + } else { + LOG(fatal) << "FemtoFlowDetaDphiStar: passed arguments don't agree with FemtoFlowDetaDphiStar's type of events! Please provide same or mixed."; + } + + // if (std::pow(dphiAvg, 2) / std::pow(cutDeltaPhiStarMax, 2) + std::pow(deta, 2) / std::pow(cutDeltaEtaMax, 2) < 1.) { + if ((dphiAvg > cutDeltaPhiStarMin) && (dphiAvg < cutDeltaPhiStarMax) && (deta > cutDeltaEtaMin) && (deta < cutDeltaEtaMax)) { + pass = true; + } else { + if (ChosenEventType == femto_flow_femto_container::EventType::same) { + histdetadpisame[i][1]->Fill(deta, dphiAvg); + } else if (ChosenEventType == femto_flow_femto_container::EventType::mixed) { + histdetadpimixed[i][1]->Fill(deta, dphiAvg); + } else { + LOG(fatal) << "FemtoFlowDetaDphiStar: passed arguments don't agree with FemtoFlowDetaDphiStar's type of events! Please provide same or mixed."; + } + } + } + return pass; + + } else if constexpr (kPartOneType == o2::aod::femtoflowparticle::ParticleType::kTrack && kPartTwoType == o2::aod::femtoflowparticle::ParticleType::kD0) { + /// Track-D0 combination + // check if provided particles are in agreement with the class instantiation + if (part1.partType() != o2::aod::femtoflowparticle::ParticleType::kTrack || part2.partType() != o2::aod::femtoflowparticle::ParticleType::kD0) { + LOG(fatal) << "FemtoFlowDetaDphiStar: passed arguments don't agree with FemtoFlowDetaDphiStar instantiation! Please provide kTrack, kD0 candidates."; + return false; + } + + bool pass = false; + for (int i = 0; i < numEvtType; i++) { + auto indexOfDaughter = 0; + if (ChosenEventType == femto_flow_femto_container::EventType::mixed) { + indexOfDaughter = part2.globalIndex() - 2 + i; + } else if (ChosenEventType == femto_flow_femto_container::EventType::same) { + indexOfDaughter = part2.index() - 2 + i; + } + + auto daughter = particles.begin() + indexOfDaughter; + auto deta = part1.eta() - daughter.eta(); + auto dphiAvg = averagePhiStar(part1, *daughter, i); // auto dphiAvg = calculateDphiStar(part1, *daughter); + dphiAvg = RecoDecay::constrainAngle(dphiAvg, -1 * o2::constants::math::PI, 1); + if (ChosenEventType == femto_flow_femto_container::EventType::same) { + histdetadpisame[i][0]->Fill(deta, dphiAvg); + } else if (ChosenEventType == femto_flow_femto_container::EventType::mixed) { + histdetadpimixed[i][0]->Fill(deta, dphiAvg); + } else { + LOG(fatal) << "FemtoFlowDetaDphiStar: passed arguments don't agree with FemtoFlowDetaDphiStar's type of events! Please provide same or mixed."; + } + + if ((dphiAvg > cutDeltaPhiStarMin) && (dphiAvg < cutDeltaPhiStarMax) && (deta > cutDeltaEtaMin) && (deta < cutDeltaEtaMax)) { + pass = true; // pair is close + } else { + if (ChosenEventType == femto_flow_femto_container::EventType::same) { + histdetadpisame[i][1]->Fill(deta, dphiAvg); + } else if (ChosenEventType == femto_flow_femto_container::EventType::mixed) { + histdetadpimixed[i][1]->Fill(deta, dphiAvg); + } else { + LOG(fatal) << "FemtoFlowDetaDphiStar: passed arguments don't agree with FemtoFlowDetaDphiStar's type of events! Please provide same or mixed."; + } + } + } + return pass; + } else { + LOG(fatal) << "FemtoFlowPairCleaner: Combination of objects not defined - quitting!"; + return false; + } + } + + /// Check if pair is close or not + template + void closePairqLCMS(Part const& part1, Part const& part2, float lmagfield, uint8_t ChosenEventType, double qlcms) // add typename Parts and variable parts for adding MClabels + { + using namespace o2::framework; + + magfield = lmagfield; + if constexpr (kPartOneType == o2::aod::femtoflowparticle::ParticleType::kTrack && kPartTwoType == o2::aod::femtoflowparticle::ParticleType::kTrack) { + auto deta = part1.eta() - part2.eta(); + auto dphiAvg = averagePhiStar(part1, part2, 0); + + if (ChosenEventType == femto_flow_femto_container::EventType::same) { + histdetadpiqlcmssame->Fill(qlcms, deta, dphiAvg); + } else if (ChosenEventType == femto_flow_femto_container::EventType::mixed) { + histdetadpiqlcmsmixed->Fill(qlcms, deta, dphiAvg); + } else { + LOG(fatal) << "FemtoFlowDetaDphiStar: passed arguments don't agree with FemtoFlowDetaDphiStar's type of events! Please provide same or mixed."; + } + } + } + + private: + o2::framework::HistogramRegistry* mHistogramRegistry = nullptr; ///< For main output + o2::framework::HistogramRegistry* mHistogramRegistryQA = nullptr; ///< For QA output + static constexpr std::string_view DirNames[6] = {"kTrack_kTrack/", "kTrack_kV0/", "kV0_kV0/", "kTrack_kPhi/", "kTrack_kD0/", "kCascade_kCascade/"}; + + static constexpr std::string_view HistNamesSame[2][8] = {{"detadphidetadphi0BeforeSame_0", "detadphidetadphi0BeforeSame_1", "detadphidetadphi0BeforeSame_2", + "detadphidetadphi0BeforeSame_3", "detadphidetadphi0BeforeSame_4", "detadphidetadphi0BeforeSame_5", + "detadphidetadphi0BeforeSame_6", "detadphidetadphi0BeforeSameqLCMS"}, + {"detadphidetadphi0AfterSame_0", "detadphidetadphi0AfterSame_1", "detadphidetadphi0AfterSame_2", + "detadphidetadphi0AfterSame_3", "detadphidetadphi0AfterSame_4", "detadphidetadphi0AfterSame_5", + "detadphidetadphi0AfterSame_6", "detadphidetadphi0AfterSameqLCMS"}}; + static constexpr std::string_view HistNamesMixed[2][8] = {{"detadphidetadphi0BeforeMixed_0", "detadphidetadphi0BeforeMixed_1", "detadphidetadphi0BeforeMixed_2", + "detadphidetadphi0BeforeMixed_3", "detadphidetadphi0BeforeMixed_4", "detadphidetadphi0BeforeMixed_5", + "detadphidetadphi0BeforeMixed_6", "detadphidetadphi0BeforeMixedqLCMS"}, + {"detadphidetadphi0AfterMixed_0", "detadphidetadphi0AfterMixed_1", "detadphidetadphi0AfterMixed_2", + "detadphidetadphi0AfterMixed_3", "detadphidetadphi0AfterMixed_4", "detadphidetadphi0AfterMixed_5", + "detadphidetadphi0AfterMixed_6", "detadphidetadphi0AfterMixedqLCMS"}}; + + static constexpr std::string_view HistNamesRadii[7][9] = {{"detadphidetadphi0Before_0_0", "detadphidetadphi0Before_0_1", "detadphidetadphi0Before_0_2", + "detadphidetadphi0Before_0_3", "detadphidetadphi0Before_0_4", "detadphidetadphi0Before_0_5", + "detadphidetadphi0Before_0_6", "detadphidetadphi0Before_0_7", "detadphidetadphi0Before_0_8"}, + {"detadphidetadphi0Before_1_0", "detadphidetadphi0Before_1_1", "detadphidetadphi0Before_1_2", + "detadphidetadphi0Before_1_3", "detadphidetadphi0Before_1_4", "detadphidetadphi0Before_1_5", + "detadphidetadphi0Before_1_6", "detadphidetadphi0Before_1_7", "detadphidetadphi0Before_1_8"}, + {"detadphidetadphi0Before_2_0", "detadphidetadphi0Before_2_1", "detadphidetadphi0Before_2_2", + "detadphidetadphi0Before_2_3", "detadphidetadphi0Before_2_4", "detadphidetadphi0Before_2_5", + "detadphidetadphi0Before_2_6", "detadphidetadphi0Before_2_7", "detadphidetadphi0Before_2_8"}, + {"detadphidetadphi0Before_3_0", "detadphidetadphi0Before_3_1", "detadphidetadphi0Before_3_2", + "detadphidetadphi0Before_3_3", "detadphidetadphi0Before_3_4", "detadphidetadphi0Before_3_5", + "detadphidetadphi0Before_3_6", "detadphidetadphi0Before_3_7", "detadphidetadphi0Before_3_8"}, + {"detadphidetadphi0Before_4_0", "detadphidetadphi0Before_4_1", "detadphidetadphi0Before_4_2", + "detadphidetadphi0Before_4_3", "detadphidetadphi0Before_4_4", "detadphidetadphi0Before_4_5", + "detadphidetadphi0Before_4_6", "detadphidetadphi0Before_4_7", "detadphidetadphi0Before_4_8"}, + {"detadphidetadphi0Before_5_0", "detadphidetadphi0Before_5_1", "detadphidetadphi0Before_5_2", + "detadphidetadphi0Before_5_3", "detadphidetadphi0Before_5_4", "detadphidetadphi0Before_5_5", + "detadphidetadphi0Before_5_6", "detadphidetadphi0Before_5_7", "detadphidetadphi0Before_5_8"}, + {"detadphidetadphi0Before_6_0", "detadphidetadphi0Before_6_1", "detadphidetadphi0Before_6_2", + "detadphidetadphi0Before_6_3", "detadphidetadphi0Before_6_4", "detadphidetadphi0Before_6_5", + "detadphidetadphi0Before_6_6", "detadphidetadphi0Before_6_7", "detadphidetadphi0Before_6_8"}}; + + static constexpr o2::aod::femtoflowparticle::ParticleType kPartOneType = partOne; ///< Type of particle 1 + static constexpr o2::aod::femtoflowparticle::ParticleType kPartTwoType = partTwo; ///< Type of particle 2 + + static constexpr float TmpRadiiTPC[9] = {85., 105., 125., 145., 165., 185., 205., 225., 245.}; + + static constexpr uint32_t kSignMinusMask = 1; + static constexpr uint32_t kSignPlusMask = 1 << 1; + static constexpr uint32_t kValue0 = 0; + + float chosenRadii; + float cutDeltaPhiStarMax; + float cutDeltaPhiStarMin; + float cutDeltaEtaMax; + float cutDeltaEtaMin; + float magfield; + bool plotForEveryRadii = false; + float cutPhiInvMassLow; + float cutPhiInvMassHigh; + bool isSameSignCPR = false; + + std::array, 2>, 7> histdetadpisame{}; + std::array, 2>, 7> histdetadpimixed{}; + std::array, 9>, 7> histdetadpiRadii{}; + + std::shared_ptr histdetadpiqlcmssame{}; + std::shared_ptr histdetadpiqlcmsmixed{}; + + /// Calculate phi at all required radii stored in TmpRadiiTPC + /// Magnetic field to be provided in Tesla + template + void phiAtRadiiTPC(const T& part, std::vector& tmpVec) + { + + float phi0 = part.phi(); + // Start: Get the charge from cutcontainer using masks + float charge = 0.; + if ((part.cut() & kSignMinusMask) == kValue0 && (part.cut() & kSignPlusMask) == kValue0) { + charge = 0; + } else if ((part.cut() & kSignPlusMask) == kSignPlusMask) { + charge = 1; + } else if ((part.cut() & kSignMinusMask) == kSignMinusMask) { + charge = -1; + } else { + LOG(fatal) << "FemtoFlowDetaDphiStar: Charge bits are set wrong!"; + } + // End: Get the charge from cutcontainer using masks + float pt = part.pt(); + for (size_t i = 0; i < std::size(TmpRadiiTPC); i++) { + double arg = 0.3 * charge * magfield * TmpRadiiTPC[i] * 0.01 / (2. * pt); + if (std::abs(arg) < 1.0) { + tmpVec.push_back(phi0 - std::asin(arg)); + } else { + tmpVec.push_back(999.0); + } + } + } + + /// Calculate average phi + template + float averagePhiStar(const T1& part1, const T2& part2, int iHist) + { + std::vector tmpVec1; + std::vector tmpVec2; + phiAtRadiiTPC(part1, tmpVec1); + phiAtRadiiTPC(part2, tmpVec2); + int num = tmpVec1.size(); + float dPhiAvg = 0; + float dphi = 0; + int entries = 0; + float bugNum = 999; + for (int i = 0; i < num; i++) { + if (tmpVec1.at(i) != bugNum && tmpVec2.at(i) != bugNum) { + dphi = tmpVec1.at(i) - tmpVec2.at(i); + entries++; + } else { + dphi = 0; + } + dphi = RecoDecay::constrainAngle(dphi, -1 * o2::constants::math::PI, 1); + dPhiAvg += dphi; + if (plotForEveryRadii) { + histdetadpiRadii[iHist][i]->Fill(part1.eta() - part2.eta(), dphi); + } + } + return dPhiAvg / static_cast(entries); + } + + // Get particle charge from mask + template + float getCharge(const T1& part) + { + float charge = 0; + if ((part.cut() & kSignMinusMask) == kValue0 && (part.cut() & kSignPlusMask) == kValue0) { + charge = 0; + } else if ((part.cut() & kSignPlusMask) == kSignPlusMask) { + charge = 1; + } else if ((part.cut() & kSignMinusMask) == kSignMinusMask) { + charge = -1; + } else { + LOG(fatal) << "FemtoFlowDetaDphiStar: Charge bits are set wrong!"; + } + return charge; + } + + // Calculate phi* as in https://github.com/alisw/AliPhysics/blob/master/PWGCF/FEMTOSCOPY/AliFemtoUser/AliFemtoPairCutRadialDistance.cxx + template + double calculateDphiStar(const T1& part1, const T2& part2) + { + float charge1 = getCharge(part1); + float charge2 = getCharge(part2); + + double deltaphiconstFD = 0.3 / 2; + // double deltaphiconstAF = 0.15; + double afsi0b = deltaphiconstFD * magfield * charge1 * chosenRadii / part1.pt(); + double afsi1b = deltaphiconstFD * magfield * charge2 * chosenRadii / part2.pt(); + double dphis = 0.0; + + if (std::abs(afsi0b) < 1.0 && std::abs(afsi0b) < 1.0) { + dphis = part2.phi() - part1.phi() + std::asin(afsi1b) - std::asin(afsi0b); + } + return dphis; + } +}; + +} /* namespace femto_flow */ +} /* namespace o2::analysis */ + +#endif // PWGCF_FEMTO_CORE_FEMTOFLOWDETADPHISTAR_H_ diff --git a/PWGCF/Femto/Core/FemtoFlowEventHisto.h b/PWGCF/Femto/Core/FemtoFlowEventHisto.h new file mode 100644 index 00000000000..9d40f4a47f0 --- /dev/null +++ b/PWGCF/Femto/Core/FemtoFlowEventHisto.h @@ -0,0 +1,70 @@ +// 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 FemtoFlowEventHisto.h +/// \brief FemtoFlowEventHisto - Histogram class for tracks, V0s and cascades +/// \author Andi Mathis, TU München, andreas.mathis@ph.tum.de +/// \author Zuzanna Chochulska, WUT Warsaw & CTU Prague, zchochul@cern.ch + +#ifndef PWGCF_FEMTO_CORE_FEMTOFLOWEVENTHISTO_H_ +#define PWGCF_FEMTO_CORE_FEMTOFLOWEVENTHISTO_H_ + +#include "PWGCF/Femto/DataModel/FemtoDerived.h" + +#include "Framework/HistogramRegistry.h" + +namespace o2::analysis::femto_flow +{ + +/// \class FemtoFlowEventHisto +/// \brief Class for histogramming event properties +class FemtoFlowEventHisto +{ + public: + /// Destructor + virtual ~FemtoFlowEventHisto() = default; + /// Initializes histograms for the task + /// \param registry Histogram registry to be passed + void init(o2::framework::HistogramRegistry* registry) + { + using namespace o2::framework; + + mHistogramRegistry = registry; + mHistogramRegistry->add("Event/zvtxhist", "; vtx_{z} (cm); Entries", kTH1F, {{250, -12.5, 12.5}}); + mHistogramRegistry->add("Event/MultV0M", "; vMultV0M; Entries", kTH1F, {{2000, 0, 20000}}); + mHistogramRegistry->add("Event/MultNTr", "; vMultNTr; Entries", kTH1F, {{20, 0, 200}}); + mHistogramRegistry->add("Event/MultNTrVSMultV0M", "; vMultNTr; MultV0M", kTH2F, {{200, 0, 4000}, {2000, 0, 20000}}); + mHistogramRegistry->add("Event/zvtxhist_MultNTr", "; zvtxhist; MultNTr", kTH2F, {{250, -12.5, 12.5}, {20, 0, 200}}); + } + + /// Some basic QA of the event + /// \tparam T type of the collision + /// \param col Collision + template + void fillQA(T const& col) + { + using namespace o2::framework; + + if (mHistogramRegistry) { + mHistogramRegistry->fill(HIST("Event/zvtxhist"), col.posZ()); + mHistogramRegistry->fill(HIST("Event/MultV0M"), col.multV0M()); + mHistogramRegistry->fill(HIST("Event/MultNTr"), col.multNtr()); + mHistogramRegistry->fill(HIST("Event/MultNTrVSMultV0M"), col.multNtr(), col.multV0M()); + mHistogramRegistry->fill(HIST("Event/zvtxhist_MultNTr"), col.posZ(), col.multNtr()); + } + } + + private: + o2::framework::HistogramRegistry* mHistogramRegistry; ///< For QA output +}; +} // namespace o2::analysis::femto_flow + +#endif // PWGCF_FEMTO_CORE_FEMTOFLOWEVENTHISTO_H_ diff --git a/PWGCF/Femto/Core/FemtoFlowFemtoContainer.h b/PWGCF/Femto/Core/FemtoFlowFemtoContainer.h new file mode 100644 index 00000000000..064a134b200 --- /dev/null +++ b/PWGCF/Femto/Core/FemtoFlowFemtoContainer.h @@ -0,0 +1,334 @@ +// 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 FemtoFlowFemtoContainer.h +/// \brief Definition of the FemtoFlowFemtoContainer +/// \author Andi Mathis, TU München, andreas.mathis@ph.tum.de +/// \author Valentina Mantovani Sarti, valentina.mantovani-sarti@tum.de +/// \author Georgios Mantzaridis, TU München, georgios.mantzaridis@tum.de +/// \author Anton Riedel, TU München, anton.riedel@tum.de +/// \author Zuzanna Chochulska, WUT Warsaw & CTU Prague, zchochul@cern.ch + +#ifndef PWGCF_FEMTO_CORE_FEMTOFLOWFEMTOCONTAINER_H_ +#define PWGCF_FEMTO_CORE_FEMTOFLOWFEMTOCONTAINER_H_ + +#include "PWGCF/Femto/Core/FemtoFlowMath.h" + +#include "Framework/ASoAHelpers.h" +#include "Framework/AnalysisTask.h" +#include "Framework/HistogramRegistry.h" +#include "Framework/O2DatabasePDGPlugin.h" + +#include "Math/Vector4D.h" +#include "TMath.h" + +#include + +#include +#include + +namespace o2::analysis::femto_flow +{ + +namespace femto_flow_femto_container +{ +/// Femtoscopic observable to be computed +enum Observable { kstar ///< kstar +}; + +/// Type of the event processind +enum EventType { same, ///< Pair from same event + mixed ///< Pair from mixed event +}; +}; // namespace femto_flow_femto_container + +/// \class FemtoFlowFemtoContainer +/// \brief Container for all histogramming related to the correlation function. The two +/// particles of the pair are passed here, and the correlation function and QA histograms +/// are filled according to the specified observable +/// \tparam eventType Type of the event (same/mixed) +/// \tparam obs Observable to be computed (k*/Q_inv/...) +template +class FemtoFlowFemtoContainer +{ + public: + /// Destructor + virtual ~FemtoFlowFemtoContainer() = default; + + /// Initializes histograms for the task + /// Called by init both in case of reconstructed data/ Monte Carlo, and for Monte Carlo Truth + /// \tparam T type of the axis Object + /// \param folderName Name of the directory in the output file (no suffix for reconstructed data/ Monte Carlo; "_MC" for Monte Carlo Truth) + /// \param femtoObs Title of the femto observable axis + /// \param femtoObsAxis axis object for the femto observable axis + /// \param multAxis axis object for the multiplicity axis + /// \param kTAxis axis object for the kT axis + /// \param mTAxis axis object for the mT axis + template + void initBase(std::string folderName, std::string femtoObs, T femtoObsAxis, T multAxis, T kTAxis, T mTAxis, T multAxis3D, T mTAxis3D, bool use3dplots, bool useqnDivide) + { + using namespace o2::framework; + + mHistogramRegistry->add((folderName + "/relPairDist").c_str(), ("; " + femtoObs + "; Entries").c_str(), kTH1F, {femtoObsAxis}); + mHistogramRegistry->add((folderName + "/relPairkT").c_str(), "; #it{k}_{T} (GeV/#it{c}); Entries", kTH1F, {kTAxis}); + mHistogramRegistry->add((folderName + "/relPairkstarkT").c_str(), ("; " + femtoObs + "; #it{k}_{T} (GeV/#it{c})").c_str(), kTH2F, {femtoObsAxis, kTAxis}); + mHistogramRegistry->add((folderName + "/relPairkstarmT").c_str(), ("; " + femtoObs + "; #it{m}_{T} (GeV/#it{c}^{2})").c_str(), kTH2F, {femtoObsAxis, mTAxis}); + mHistogramRegistry->add((folderName + "/relPairkstarMult").c_str(), ("; " + femtoObs + "; Multiplicity").c_str(), kTH2F, {femtoObsAxis, multAxis}); + mHistogramRegistry->add((folderName + "/kstarPtPart1").c_str(), ("; " + femtoObs + "; #it{p} _{T} Particle 1 (GeV/#it{c})").c_str(), kTH2F, {femtoObsAxis, {375, 0., 7.5}}); + mHistogramRegistry->add((folderName + "/kstarPtPart2").c_str(), ("; " + femtoObs + "; #it{p} _{T} Particle 2 (GeV/#it{c})").c_str(), kTH2F, {femtoObsAxis, {375, 0., 7.5}}); + mHistogramRegistry->add((folderName + "/MultPtPart1").c_str(), "; #it{p} _{T} Particle 1 (GeV/#it{c}); Multiplicity", kTH2F, {{375, 0., 7.5}, multAxis}); + mHistogramRegistry->add((folderName + "/MultPtPart2").c_str(), "; #it{p} _{T} Particle 2 (GeV/#it{c}); Multiplicity", kTH2F, {{375, 0., 7.5}, multAxis}); + mHistogramRegistry->add((folderName + "/PtPart1PtPart2").c_str(), "; #it{p} _{T} Particle 1 (GeV/#it{c}); #it{p} _{T} Particle 2 (GeV/#it{c})", kTH2F, {{375, 0., 7.5}, {375, 0., 7.5}}); + if (use3dplots) { + mHistogramRegistry->add((folderName + "/relPairkstarmTMult").c_str(), ("; " + femtoObs + "; #it{m}_{T} (GeV/#it{c}^{2}); Multiplicity").c_str(), kTH3F, {femtoObsAxis, mTAxis3D, multAxis3D}); + } + if (useqnDivide) { + for (int iqn(0); iqn < numqnBins; ++iqn) { + mHistogramRegistry->add((folderName + std::to_string(iqn) + "/relPairDist").c_str(), ("; " + femtoObs + "; Entries").c_str(), kTH1F, {femtoObsAxis}); + mHistogramRegistry->add((folderName + std::to_string(iqn) + "/relPairkstarmT").c_str(), ("; " + femtoObs + "; #it{m}_{T} (GeV/#it{c}^{2})").c_str(), kTH2F, {femtoObsAxis, mTAxis}); + mHistogramRegistry->add((folderName + std::to_string(iqn) + "/relPairkstarMult").c_str(), ("; " + femtoObs + "; Multiplicity").c_str(), kTH2F, {femtoObsAxis, multAxis}); + } + } + } + + /// Initializes specialized Monte Carlo truth histograms for the task + /// internal function called by init only in case of Monte Carlo truth + /// \tparam T type of the xxis Object + /// \param folderName Name of the directory in the output file (no suffix for reconstructed data/ Monte Carlo; "_MC" for Monte Carlo Truth) + /// \param femtoObsAxis axis object for the femto observable axis + template + void initMC(std::string folderName, std::string femtoObs, T femtoObsAxis, T multAxis, T mTAxis) + { + using namespace o2::framework; + + mHistogramRegistry->add((folderName + "/relPairDist_ReconNoFake").c_str(), ("; " + femtoObs + "; Entries").c_str(), kTH1F, {femtoObsAxis}); + mHistogramRegistry->add((folderName + "/relPairkstarmT_ReconNoFake").c_str(), ("; " + femtoObs + "; #it{m}_{T} (GeV/#it{c}^{2})").c_str(), kTH2F, {femtoObsAxis, mTAxis}); + mHistogramRegistry->add((folderName + "/relPairkstarMult_ReconNoFake").c_str(), ("; " + femtoObs + "; Multiplicity").c_str(), kTH2F, {femtoObsAxis, multAxis}); + mHistogramRegistry->add((folderName + "/hNoMCtruthPairsCounter").c_str(), "; Counter; Entries", kTH1I, {{1, 0, 1}}); + mHistogramRegistry->add((folderName + "/hFakePairsCounter").c_str(), "; Counter; Entries", kTH1I, {{1, 0, 1}}); + mHistogramRegistry->add((folderName + "/kstar_resolution").c_str(), "; #it{k} _{T} reconstructed (GeV/#it{c}); #it{k} _{T} truth (GeV/#it{c})", kTH2F, {femtoObsAxis, femtoObsAxis}); + } + + /// Templated function to initialize the histograms for the task + /// Always calls initBase to initialize the histograms for data/ Monte Carlo reconstructed + /// In case of Monte Carlo, calls initBase again for Monte Carlo truth and the specialized function initMC for additional histogramms + /// \tparam T type of the configurable for the axis configuration + /// \param registry Histogram registry to be passed + /// \param kstarBins k* binning for the histograms + /// \param multBins multiplicity binning for the histograms + /// \param kTBins kT binning for the histograms + /// \param mTBins mT binning for the histograms + /// \param isMC add Monte Carlo truth histograms to the output file + template + void init(o2::framework::HistogramRegistry* registry, T& kstarBins, T& multBins, T& kTBins, T& mTBins, T& multBins3D, T& mTBins3D, bool isMC, bool use3dplots, bool useqnDivide) + { + using namespace o2::framework; + + mHistogramRegistry = registry; + std::string femtoObs; + if constexpr (FemtoObs == femto_flow_femto_container::Observable::kstar) { + femtoObs = "#it{k*} (GeV/#it{c})"; + } + std::vector tmpVecMult = multBins; + o2::framework::AxisSpec multAxis = {tmpVecMult, "Multiplicity"}; + o2::framework::AxisSpec femtoObsAxis = {kstarBins, femtoObs.c_str()}; + o2::framework::AxisSpec kTAxis = {kTBins, "#it{k}_{T} (GeV/#it{c})"}; + o2::framework::AxisSpec mTAxis = {mTBins, "#it{m}_{T} (GeV/#it{c}^{2})"}; + + o2::framework::AxisSpec multAxis3D = {multBins3D, "Multiplicity"}; + o2::framework::AxisSpec mTAxis3D = {mTBins3D, "#it{m}_{T} (GeV/#it{c})"}; + + std::string folderName = static_cast(FolderSuffix[kEventType]) + static_cast(o2::aod::femtoflow_mc_particle::MCTypeName[o2::aod::femtoflow_mc_particle::MCType::kRecon]); + + initBase(folderName, femtoObs, femtoObsAxis, multAxis, kTAxis, mTAxis, multAxis3D, mTAxis3D, use3dplots, useqnDivide); + if (isMC) { + folderName = static_cast(FolderSuffix[kEventType]) + static_cast(o2::aod::femtoflow_mc_particle::MCTypeName[o2::aod::femtoflow_mc_particle::MCType::kTruth]); + initBase(folderName, femtoObs, femtoObsAxis, multAxis, kTAxis, mTAxis, multAxis3D, mTAxis3D, use3dplots, useqnDivide); + initMC(folderName, femtoObs, femtoObsAxis, multAxis, mTAxis); + } + } + + /// Set the PDG codes of the two particles involved + /// \param pdg1 PDG code of particle one + /// \param pdg2 PDG code of particle two + void setPDGCodesMass(const int pdg1, const int pdg2, const double mass1, const int mass2) + { + kMassOne = mass1; + kMassTwo = mass2; + kPDGOne = pdg1; + kPDGTwo = pdg2; + } + + /// Pass a pair to the container and compute all the relevant observables + /// Called by setPair both in case of data/ and Monte Carlo reconstructed and for Monte Carlo truth + /// \tparam T type of the femtoflowparticle + /// \param part1 Particle one + /// \param part2 Particle two + /// \param mult Multiplicity of the event + template + void setPairBase(const float femtoObs, const float mT, T const& part1, T const& part2, const int mult, bool use3dplots, bool useqnDivide, int mybinNum) + { + using namespace o2::framework; + + const float kT = FemtoFlowMath::getkT(part1, kMassOne, part2, kMassTwo); + + mHistogramRegistry->fill(HIST(FolderSuffix[kEventType]) + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("/relPairDist"), femtoObs); + mHistogramRegistry->fill(HIST(FolderSuffix[kEventType]) + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("/relPairkT"), kT); + mHistogramRegistry->fill(HIST(FolderSuffix[kEventType]) + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("/relPairkstarkT"), femtoObs, kT); + mHistogramRegistry->fill(HIST(FolderSuffix[kEventType]) + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("/relPairkstarmT"), femtoObs, mT); + mHistogramRegistry->fill(HIST(FolderSuffix[kEventType]) + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("/relPairkstarMult"), femtoObs, mult); + mHistogramRegistry->fill(HIST(FolderSuffix[kEventType]) + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("/kstarPtPart1"), femtoObs, part1.pt()); + mHistogramRegistry->fill(HIST(FolderSuffix[kEventType]) + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("/kstarPtPart2"), femtoObs, part2.pt()); + mHistogramRegistry->fill(HIST(FolderSuffix[kEventType]) + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("/MultPtPart1"), part1.pt(), mult); + mHistogramRegistry->fill(HIST(FolderSuffix[kEventType]) + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("/MultPtPart2"), part2.pt(), mult); + mHistogramRegistry->fill(HIST(FolderSuffix[kEventType]) + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("/PtPart1PtPart2"), part1.pt(), part2.pt()); + if (use3dplots) { + mHistogramRegistry->fill(HIST(FolderSuffix[kEventType]) + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("/relPairkstarmTMult"), femtoObs, mT, mult); + } + if (useqnDivide && mybinNum >= 0 && mybinNum < numqnBins) { + switch (mybinNum) { + case 0: + mHistogramRegistry->fill(HIST(FolderSuffix[kEventType]) + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("0") + HIST("/relPairDist"), femtoObs); + mHistogramRegistry->fill(HIST(FolderSuffix[kEventType]) + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("0") + HIST("/relPairkstarmT"), femtoObs, mT); + mHistogramRegistry->fill(HIST(FolderSuffix[kEventType]) + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("0") + HIST("/relPairkstarMult"), femtoObs, mult); + break; + case 1: + mHistogramRegistry->fill(HIST(FolderSuffix[kEventType]) + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("1") + HIST("/relPairDist"), femtoObs); + mHistogramRegistry->fill(HIST(FolderSuffix[kEventType]) + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("1") + HIST("/relPairkstarmT"), femtoObs, mT); + mHistogramRegistry->fill(HIST(FolderSuffix[kEventType]) + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("1") + HIST("/relPairkstarMult"), femtoObs, mult); + break; + case 2: + mHistogramRegistry->fill(HIST(FolderSuffix[kEventType]) + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("2") + HIST("/relPairDist"), femtoObs); + mHistogramRegistry->fill(HIST(FolderSuffix[kEventType]) + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("2") + HIST("/relPairkstarmT"), femtoObs, mT); + mHistogramRegistry->fill(HIST(FolderSuffix[kEventType]) + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("2") + HIST("/relPairkstarMult"), femtoObs, mult); + break; + case 3: + mHistogramRegistry->fill(HIST(FolderSuffix[kEventType]) + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("3") + HIST("/relPairDist"), femtoObs); + mHistogramRegistry->fill(HIST(FolderSuffix[kEventType]) + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("3") + HIST("/relPairkstarmT"), femtoObs, mT); + mHistogramRegistry->fill(HIST(FolderSuffix[kEventType]) + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("3") + HIST("/relPairkstarMult"), femtoObs, mult); + break; + case 4: + mHistogramRegistry->fill(HIST(FolderSuffix[kEventType]) + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("4") + HIST("/relPairDist"), femtoObs); + mHistogramRegistry->fill(HIST(FolderSuffix[kEventType]) + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("4") + HIST("/relPairkstarmT"), femtoObs, mT); + mHistogramRegistry->fill(HIST(FolderSuffix[kEventType]) + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("4") + HIST("/relPairkstarMult"), femtoObs, mult); + break; + case 5: + mHistogramRegistry->fill(HIST(FolderSuffix[kEventType]) + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("5") + HIST("/relPairDist"), femtoObs); + mHistogramRegistry->fill(HIST(FolderSuffix[kEventType]) + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("5") + HIST("/relPairkstarmT"), femtoObs, mT); + mHistogramRegistry->fill(HIST(FolderSuffix[kEventType]) + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("5") + HIST("/relPairkstarMult"), femtoObs, mult); + break; + case 6: + mHistogramRegistry->fill(HIST(FolderSuffix[kEventType]) + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("6") + HIST("/relPairDist"), femtoObs); + mHistogramRegistry->fill(HIST(FolderSuffix[kEventType]) + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("6") + HIST("/relPairkstarmT"), femtoObs, mT); + mHistogramRegistry->fill(HIST(FolderSuffix[kEventType]) + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("6") + HIST("/relPairkstarMult"), femtoObs, mult); + break; + case 7: + mHistogramRegistry->fill(HIST(FolderSuffix[kEventType]) + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("7") + HIST("/relPairDist"), femtoObs); + mHistogramRegistry->fill(HIST(FolderSuffix[kEventType]) + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("7") + HIST("/relPairkstarmT"), femtoObs, mT); + mHistogramRegistry->fill(HIST(FolderSuffix[kEventType]) + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("7") + HIST("/relPairkstarMult"), femtoObs, mult); + break; + case 8: + mHistogramRegistry->fill(HIST(FolderSuffix[kEventType]) + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("8") + HIST("/relPairDist"), femtoObs); + mHistogramRegistry->fill(HIST(FolderSuffix[kEventType]) + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("8") + HIST("/relPairkstarmT"), femtoObs, mT); + mHistogramRegistry->fill(HIST(FolderSuffix[kEventType]) + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("8") + HIST("/relPairkstarMult"), femtoObs, mult); + break; + case 9: + mHistogramRegistry->fill(HIST(FolderSuffix[kEventType]) + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("9") + HIST("/relPairDist"), femtoObs); + mHistogramRegistry->fill(HIST(FolderSuffix[kEventType]) + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("9") + HIST("/relPairkstarmT"), femtoObs, mT); + mHistogramRegistry->fill(HIST(FolderSuffix[kEventType]) + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("9") + HIST("/relPairkstarMult"), femtoObs, mult); + break; + default: + return; // invalid qn bin + } + } + } + + /// Called by setPair only in case of Monte Carlo truth + /// Fills MC truth specific histogramms: + /// - kstar distribution plots with RECONSTRUCTED information but ONLY for non-fake candidates; needed for purity calculations of tracks + /// - kstar resolution matrix + /// Note: Standard histogramms with MC truth information are filled with the setPairBase function + /// \param part1 Particle one + /// \param part2 Particle two + /// \param mult Multiplicity of the event + void setPairMC(const float femtoObsMC, const float femtoObs, const float mT, const int mult) + { + using namespace o2::framework; + + if (mHistogramRegistry) { + // Fill the kstar distributions with the reconstructed information but only for particles with the right PDG code + mHistogramRegistry->fill(HIST(FolderSuffix[kEventType]) + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[o2::aod::femtoflow_mc_particle::MCType::kTruth]) + HIST("/relPairDist_ReconNoFake"), femtoObs); + mHistogramRegistry->fill(HIST(FolderSuffix[kEventType]) + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[o2::aod::femtoflow_mc_particle::MCType::kTruth]) + HIST("/relPairkstarmT_ReconNoFake"), femtoObs, mT); + mHistogramRegistry->fill(HIST(FolderSuffix[kEventType]) + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[o2::aod::femtoflow_mc_particle::MCType::kTruth]) + HIST("/relPairkstarMult_ReconNoFake"), femtoObs, mult); + + mHistogramRegistry->fill(HIST(FolderSuffix[kEventType]) + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[o2::aod::femtoflow_mc_particle::MCType::kTruth]) + HIST("/kstar_resolution"), femtoObsMC, femtoObs); + } + } + + /// Templated function to handle data/ Monte Carlo reconstructed and Monte Carlo truth + /// Always calls setPairBase to compute the observables with reconstructed data + /// In case of Monte Carlo, calls setPairBase with MC info and specialized function setPairMC for additional histogramms + /// \tparam T type of the femtoflowparticle + /// \param part1 Particle one + /// \param part2 Particle two + /// \param mult Multiplicity of the event + template + void setPair(T const& part1, T const& part2, const int mult, bool use3dplots, bool useqnDivide, int mybinNum) + { + using namespace o2::framework; + + float femtoObs, femtoObsMC; + // Calculate femto observable and the mT with reconstructed information + if constexpr (FemtoObs == femto_flow_femto_container::Observable::kstar) { + femtoObs = FemtoFlowMath::getkstar(part1, kMassOne, part2, kMassTwo); + } + const float mT = FemtoFlowMath::getmT(part1, kMassOne, part2, kMassTwo); + + if (mHistogramRegistry) { + setPairBase(femtoObs, mT, part1, part2, mult, use3dplots, useqnDivide, mybinNum); + + if constexpr (isMC) { + if (part1.has_fDMCParticle() && part2.has_fDMCParticle()) { + // calculate the femto observable and the mT with MC truth information + if constexpr (FemtoObs == femto_flow_femto_container::Observable::kstar) { + femtoObsMC = FemtoFlowMath::getkstar(part1.fDMCParticle(), kMassOne, part2.fDMCParticle(), kMassTwo); + } + const float mTMC = FemtoFlowMath::getmT(part1.fDMCParticle(), kMassOne, part2.fDMCParticle(), kMassTwo); + + if (std::abs(part1.fDMCParticle().pdgMCTruth()) == std::abs(kPDGOne) && std::abs(part2.fDMCParticle().pdgMCTruth()) == std::abs(kPDGTwo)) { // Note: all pair-histogramms are filled with MC truth information ONLY in case of non-fake candidates + setPairBase(femtoObsMC, mTMC, part1.fDMCParticle(), part2.fDMCParticle(), mult, use3dplots, useqnDivide, mybinNum); + setPairMC(femtoObsMC, femtoObs, mT, mult); + } else { + mHistogramRegistry->fill(HIST(FolderSuffix[kEventType]) + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[o2::aod::femtoflow_mc_particle::MCType::kTruth]) + HIST("/hFakePairsCounter"), 0); + } + + } else { + mHistogramRegistry->fill(HIST(FolderSuffix[kEventType]) + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[o2::aod::femtoflow_mc_particle::MCType::kTruth]) + HIST("/hNoMCtruthPairsCounter"), 0); + } + } + } + } + + protected: + o2::framework::HistogramRegistry* mHistogramRegistry = nullptr; ///< For QA output + static constexpr std::string_view FolderSuffix[2] = {"SameEvent", "MixedEvent"}; ///< Folder naming for the output according to kEventType + static constexpr femto_flow_femto_container::Observable FemtoObs = obs; ///< Femtoscopic observable to be computed (according to femto_flow_femto_container::Observable) + static constexpr int kEventType = eventType; ///< Type of the event (same/mixed, according to femto_flow_femto_container::EventType) + float kMassOne = 0.f; ///< PDG mass of particle 1 + float kMassTwo = 0.f; ///< PDG mass of particle 2 + int kPDGOne = 0; ///< PDG code of particle 1 + int kPDGTwo = 0; + int numqnBins = 10; ///< Max num of devided qn bins +}; + +} // namespace o2::analysis::femto_flow + +#endif // PWGCF_FEMTO_CORE_FEMTOFLOWFEMTOCONTAINER_H_ diff --git a/PWGCF/Femto/Core/FemtoFlowMath.h b/PWGCF/Femto/Core/FemtoFlowMath.h new file mode 100644 index 00000000000..3f9f2319d31 --- /dev/null +++ b/PWGCF/Femto/Core/FemtoFlowMath.h @@ -0,0 +1,257 @@ +// 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 FemtoFlowMath.h +/// \brief Definition of the FemtoFlowMath Container for math calculations of quantities related to pairs +/// \author Valentina Mantovani Sarti, TU München, valentina.mantovani-sarti@tum.de +/// \author Laura Serksnyte, TU München, laura.serksnyte@cern.ch +/// \author Zuzanna Chochulska, WUT Warsaw & CTU Prague, zchochul@cern.ch +/// \author Pritam Chakraborty, WUT Warsaw, pritam.chakraborty@pw.edu.pl + +#ifndef PWGCF_FEMTO_CORE_FEMTOFLOWMATH_H_ +#define PWGCF_FEMTO_CORE_FEMTOFLOWMATH_H_ + +#include "Math/Boost.h" +#include "Math/Vector4D.h" +#include "TMath.h" + +#include +#include + +namespace o2::analysis::femto_flow +{ + +/// \class FemtoFlowMath +/// \brief Container for math calculations of quantities related to pairs +class FemtoFlowMath +{ + public: + /// Compute the k* of a pair of particles + /// \tparam T type of tracks + /// \param part1 Particle 1 + /// \param mass1 Mass of particle 1 + /// \param part2 Particle 2 + /// \param mass2 Mass of particle 2 + template + static float getkstar(const T& part1, const float mass1, const T& part2, const float mass2) + { + const ROOT::Math::PtEtaPhiMVector vecpart1(part1.pt(), part1.eta(), part1.phi(), mass1); + const ROOT::Math::PtEtaPhiMVector vecpart2(part2.pt(), part2.eta(), part2.phi(), mass2); + const ROOT::Math::PtEtaPhiMVector trackSum = vecpart1 + vecpart2; + + const float beta = trackSum.Beta(); + const float betax = beta * std::cos(trackSum.Phi()) * std::sin(trackSum.Theta()); + const float betay = beta * std::sin(trackSum.Phi()) * std::sin(trackSum.Theta()); + const float betaz = beta * std::cos(trackSum.Theta()); + + ROOT::Math::PxPyPzMVector partOneCMS(vecpart1); + ROOT::Math::PxPyPzMVector partTwoCMS(vecpart2); + + const ROOT::Math::Boost boostPRF = ROOT::Math::Boost(-betax, -betay, -betaz); + partOneCMS = boostPRF(partOneCMS); + partTwoCMS = boostPRF(partTwoCMS); + + const ROOT::Math::PxPyPzMVector trackRelK = partOneCMS - partTwoCMS; + return 0.5 * trackRelK.P(); + } + + /// Boost particles from LAB Frame to Pair Rest Frame (for lambda daughters) + /// \tparam T type of tracks + /// \param part1 Particle 1 + /// \param mass1 Mass of particle 1 + /// \param part2 Particle 2 + /// \param mass2 Mass of particle 2 + template + static ROOT::Math::PxPyPzMVector boostPRF(const T& part1, const float mass1, const T& part2, const float mass2) + { + const ROOT::Math::PtEtaPhiMVector vecpart1(part1.pt(), part1.eta(), part1.phi(), mass1); + const ROOT::Math::PtEtaPhiMVector vecpart2(part2.pt(), part2.eta(), part2.phi(), mass2); + const ROOT::Math::PtEtaPhiMVector trackSum = vecpart1 + vecpart2; + + const float beta = trackSum.Beta(); + const float betax = beta * std::cos(trackSum.Phi()) * std::sin(trackSum.Theta()); + const float betay = beta * std::sin(trackSum.Phi()) * std::sin(trackSum.Theta()); + const float betaz = beta * std::cos(trackSum.Theta()); + + ROOT::Math::PxPyPzMVector partOneCMS(vecpart1); + ROOT::Math::PxPyPzMVector partTwoCMS(vecpart2); + + const ROOT::Math::Boost boostPRF = ROOT::Math::Boost(-betax, -betay, -betaz); + partOneCMS = boostPRF(partOneCMS); + partTwoCMS = boostPRF(partTwoCMS); + + return partOneCMS; + } + + /// Compute the qij of a pair of particles + /// \tparam T type of tracks + /// \param vecparti Particle i PxPyPzMVector + /// \param vecpartj Particle j PxPyPzMVector + // The q12 components can be calculated as: + // q^mu = (p1-p2)^mu /2 - ((p1-p2)*P/(2P^2))*P^mu + // where P = p1+p2 + // Reference: https://www.annualreviews.org/doi/pdf/10.1146/annurev.nucl.55.090704.151533 + // In the following code the above written equation will be expressed as: + // q = trackDifference/2 - scaling * trackSum + // where scaling is a float number: + // scaling = trackDifference*trackSum/(2*trackSum^2) = ((p1-p2)*P/(2P^2)) + // We don't use the reduced vector - no division by 2 + template + static ROOT::Math::PxPyPzEVector getqij(const T& vecparti, const T& vecpartj) + { + ROOT::Math::PxPyPzEVector trackSum = vecparti + vecpartj; + ROOT::Math::PxPyPzEVector trackDifference = vecparti - vecpartj; + float scaling = trackDifference.Dot(trackSum) / trackSum.Dot(trackSum); + return trackDifference - scaling * trackSum; + } + + /// Compute the Q3 of a triplet of particles + /// \tparam T type of tracks + /// \param part1 Particle 1 + /// \param mass1 Mass of particle 1 + /// \param part2 Particle 2 + /// \param mass2 Mass of particle 2 + /// \param part3 Particle 3 + /// \param mass3 Mass of particle 3 + template + static float getQ3(const T& part1, const float mass1, const T& part2, const float mass2, const T& part3, const float mass3) + { + float e1 = std::sqrt(std::pow(part1.px(), 2) + std::pow(part1.py(), 2) + std::pow(part1.pz(), 2) + std::pow(mass1, 2)); + float e2 = std::sqrt(std::pow(part2.px(), 2) + std::pow(part2.py(), 2) + std::pow(part2.pz(), 2) + std::pow(mass2, 2)); + float e3 = std::sqrt(std::pow(part3.px(), 2) + std::pow(part3.py(), 2) + std::pow(part3.pz(), 2) + std::pow(mass3, 2)); + + const ROOT::Math::PxPyPzEVector vecpart1(part1.px(), part1.py(), part1.pz(), e1); + const ROOT::Math::PxPyPzEVector vecpart2(part2.px(), part2.py(), part2.pz(), e2); + const ROOT::Math::PxPyPzEVector vecpart3(part3.px(), part3.py(), part3.pz(), e3); + + ROOT::Math::PxPyPzEVector q12 = getqij(vecpart1, vecpart2); + ROOT::Math::PxPyPzEVector q23 = getqij(vecpart2, vecpart3); + ROOT::Math::PxPyPzEVector q31 = getqij(vecpart3, vecpart1); + + float q32 = q12.M2() + q23.M2() + q31.M2(); + + return std::sqrt(-q32); + } + + /// Compute the transverse momentum of a pair of particles + /// \tparam T type of tracks + /// \param part1 Particle 1 + /// \param mass1 Mass of particle 1 + /// \param part2 Particle 2 + /// \param mass2 Mass of particle 2 + template + static float getkT(const T& part1, const float mass1, const T& part2, const float mass2) + { + const ROOT::Math::PtEtaPhiMVector vecpart1(part1.pt(), part1.eta(), part1.phi(), mass1); + const ROOT::Math::PtEtaPhiMVector vecpart2(part2.pt(), part2.eta(), part2.phi(), mass2); + const ROOT::Math::PtEtaPhiMVector trackSum = vecpart1 + vecpart2; + return 0.5 * trackSum.Pt(); + } + + /// Compute the transverse mass of a pair of particles + /// \tparam T type of tracks + /// \param part1 Particle 1 + /// \param mass1 Mass of particle 1 + /// \param part2 Particle 2 + /// \param mass2 Mass of particle 2 + template + static float getmT(const T& part1, const float mass1, const T& part2, const float mass2) + { + return std::sqrt(std::pow(getkT(part1, mass1, part2, mass2), 2.) + std::pow(0.5 * (mass1 + mass2), 2.)); + } + + /// Compute the 3d components of the pair momentum in LCMS and PRF + /// \tparam T type of tracks + /// \param part1 Particle 1 + /// \param mass1 Mass of particle 1 + /// \param part2 Particle 2 + /// \param mass2 Mass of particle 2 + /// \param isiden Identical or non-identical particle pair + template + static std::vector newpairfunc(const T& part1, const float mass1, const T& part2, const float mass2, bool isiden) + { + const double e1 = std::sqrt(std::pow(part1.px(), 2) + std::pow(part1.py(), 2) + std::pow(part1.pz(), 2) + std::pow(mass1, 2)); + const double e2 = std::sqrt(std::pow(part2.px(), 2) + std::pow(part2.py(), 2) + std::pow(part2.pz(), 2) + std::pow(mass2, 2)); + + const ROOT::Math::PxPyPzEVector vecpart1(part1.px(), part1.py(), part1.pz(), e1); + const ROOT::Math::PxPyPzEVector vecpart2(part2.px(), part2.py(), part2.pz(), e2); + const ROOT::Math::PxPyPzEVector trackSum = vecpart1 + vecpart2; + + std::vector vect; + + const double tPx = trackSum.px(); + const double tPy = trackSum.py(); + const double tPz = trackSum.pz(); + const double tE = trackSum.E(); + + const double tPtSq = (tPx * tPx + tPy * tPy); + const double tMtSq = (tE * tE - tPz * tPz); + const double tM = std::sqrt(tMtSq - tPtSq); + const double tMt = std::sqrt(tMtSq); + const double tPt = std::sqrt(tPtSq); + + // Boost to LCMS + + const double beta = tPz / tE; + const double gamma = tE / tMt; + + const double fDKOut = (part1.px() * tPx + part1.py() * tPy) / tPt; + const double fDKSide = (-part1.px() * tPy + part1.py() * tPx) / tPt; + const double fDKLong = gamma * (part1.pz() - beta * e1); + const double fDE = gamma * (e1 - beta * part1.pz()); + + const double px1LCMS = fDKOut; + const double py1LCMS = fDKSide; + const double pz1LCMS = fDKLong; + const double pE1LCMS = fDE; + + const double px2LCMS = (part2.px() * tPx + part2.py() * tPy) / tPt; + const double py2LCMS = (part2.py() * tPx - part2.px() * tPy) / tPt; + const double pz2LCMS = gamma * (part2.pz() - beta * e2); + const double pE2LCMS = gamma * (e2 - beta * part2.pz()); + + const double fDKOutLCMS = px1LCMS - px2LCMS; + const double fDKSideLCMS = py1LCMS - py2LCMS; + const double fDKLongLCMS = pz1LCMS - pz2LCMS; + + // Boost to PRF + + const double betaOut = tPt / tMt; + const double gammaOut = tMt / tM; + + const double fDKOutPRF = gammaOut * (fDKOutLCMS - betaOut * (pE1LCMS - pE2LCMS)); + const double fDKSidePRF = fDKSideLCMS; + const double fDKLongPRF = fDKLongLCMS; + const double fKOut = gammaOut * (fDKOut - betaOut * fDE); + + const double qlcms = std::sqrt(fDKOutLCMS * fDKOutLCMS + fDKSideLCMS * fDKSideLCMS + fDKLongLCMS * fDKLongLCMS); + const double qinv = std::sqrt(fDKOutPRF * fDKOutPRF + fDKSidePRF * fDKSidePRF + fDKLongPRF * fDKLongPRF); + const double kstar = std::sqrt(fKOut * fKOut + fDKSide * fDKSide + fDKLong * fDKLong); + + if (isiden) { + vect.push_back(qinv); + vect.push_back(fDKOutLCMS); + vect.push_back(fDKSideLCMS); + vect.push_back(fDKLongLCMS); + vect.push_back(qlcms); + } else { + vect.push_back(kstar); + vect.push_back(fDKOut); + vect.push_back(fDKSide); + vect.push_back(fDKLong); + } + return vect; + } +}; + +} // namespace o2::analysis::femto_flow + +#endif // PWGCF_FEMTO_CORE_FEMTOFLOWMATH_H_ diff --git a/PWGCF/Femto/Core/FemtoFlowObjectSelection.h b/PWGCF/Femto/Core/FemtoFlowObjectSelection.h new file mode 100644 index 00000000000..b6365bda900 --- /dev/null +++ b/PWGCF/Femto/Core/FemtoFlowObjectSelection.h @@ -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 FemtoFlowObjectSelection.h +/// \brief FemtoFlowObjectSelection - Parent class of all selections +/// \author Andi Mathis, TU München, andreas.mathis@ph.tum.de +/// \author Zuzanna Chochulska, WUT Warsaw & CTU Prague, zchochul@cern.ch + +#ifndef PWGCF_FEMTO_CORE_FEMTOFLOWOBJECTSELECTION_H_ +#define PWGCF_FEMTO_CORE_FEMTOFLOWOBJECTSELECTION_H_ + +#include "PWGCF/Femto/Core/FemtoFlowSelection.h" +#include "PWGCF/Femto/DataModel/FemtoDerived.h" + +#include "Framework/HistogramRegistry.h" +#include "ReconstructionDataFormats/PID.h" + +#include +#include +#include + +namespace o2::analysis +{ + +namespace femto_flow +{ + +/// \class FemtoFlowObjectSelection +/// \brief Cut class to contain and execute all cuts applied to tracks +/// \todo In principle all cuts that fulfill the getMinimalSelection are done implicitly and can be removed from the vector containing all cuts +/// \tparam selValDataType Data type used for the selection (float/int/bool/...) +/// \tparam selVariable Variable used for the selection +template +class FemtoFlowObjectSelection +{ + public: + /// Destructor + virtual ~FemtoFlowObjectSelection() = default; + + /// The selection criteria employed in the child class are written to a histogram + /// \tparam part Type of the particle, used as a prefix for the folder in the QAResults.root + template + void fillSelectionHistogram() + { + using namespace o2::framework; + + int nBins = mSelections.size(); + LOGF(info, "%s", (static_cast(o2::aod::femtoflowparticle::ParticleTypeName[part]) + "/cuthist").c_str()); + mHistogramRegistry->add((static_cast(o2::aod::femtoflowparticle::ParticleTypeName[part]) + "/cuthist").c_str(), "; Cut; Value", kTH1F, {{nBins, 0, static_cast(nBins)}}); + auto hist = mHistogramRegistry->get(HIST(o2::aod::femtoflowparticle::ParticleTypeName[part]) + HIST("/cuthist")); + for (size_t i = 0; i < mSelections.size(); ++i) { + hist->GetXaxis()->SetBinLabel(i + 1, Form("%u", mSelections.at(i).getSelectionVariable())); + hist->SetBinContent(i + 1, mSelections.at(i).getSelectionValue()); + } + } + + /// Pass the Configurable of selection values in the analysis task to the selection class + /// \tparam T Type of the configurable passed to the function + /// \param selVals o2 configurable containing the values employed for the selection + /// \param selVar Variable to be employed for the selection + /// \param selType Type of the selection to be employed + template + void setSelection(T& selVals, selVariable selVar, femto_flow_selection::SelectionType selType) + { + using namespace o2::framework; + + std::vector tmpSelVals = selVals; // necessary due to some features of the Configurable + std::vector> tempVec; + for (const selValDataType& selVal : tmpSelVals) { + tempVec.push_back(FemtoFlowSelection(selVal, selVar, selType)); + } + setSelection(tempVec); + } + + /// Pass an std::vector of selection values to the selection class + /// \param sels std::vector containing FemtoFlowSelections + void setSelection(std::vector>& sels) + { + using namespace o2::framework; + + /// First the selection is sorted so that the most open cuts are conducted first + switch (sels.at(0).getSelectionType()) { + case (femto_flow_selection::SelectionType::kUpperLimit): + case (femto_flow_selection::SelectionType::kAbsUpperLimit): + std::sort(sels.begin(), sels.end(), [](FemtoFlowSelection a, FemtoFlowSelection b) { + return a.getSelectionValue() > b.getSelectionValue(); + }); + break; + case (femto_flow_selection::SelectionType::kLowerLimit): + case (femto_flow_selection::SelectionType::kAbsLowerLimit): + case (femto_flow_selection::SelectionType::kEqual): + std::sort(sels.begin(), sels.end(), [](FemtoFlowSelection a, FemtoFlowSelection b) { + return a.getSelectionValue() < b.getSelectionValue(); + }); + break; + } + + /// Then, the sorted selections are added to the overall container of cuts + for (const auto& sel : sels) { + mSelections.push_back(sel); + } + } + + /// Retrieve the most open selection of a given selection variable + /// \param selVar Selection variable under consideration + /// \param selType Type of the selection variable + /// \return The most open selection of the selection variable given to the class + selValDataType getMinimalSelection(selVariable selVar, femto_flow_selection::SelectionType selType) + { + using namespace o2::framework; + + selValDataType minimalSel{}; + switch (selType) { + case (femto_flow_selection::SelectionType::kUpperLimit): + case (femto_flow_selection::SelectionType::kAbsUpperLimit): + minimalSel = -999.e9; + break; + case (femto_flow_selection::SelectionType::kLowerLimit): + case (femto_flow_selection::SelectionType::kAbsLowerLimit): + case (femto_flow_selection::SelectionType::kEqual): + minimalSel = 999.e9; + break; + } + + for (const auto& sel : mSelections) { + if (sel.getSelectionVariable() == selVar) { + switch (sel.getSelectionType()) { + case (femto_flow_selection::SelectionType::kUpperLimit): + case (femto_flow_selection::SelectionType::kAbsUpperLimit): + if (minimalSel < sel.getSelectionValue()) { + minimalSel = sel.getSelectionValue(); + } + break; + case (femto_flow_selection::SelectionType::kLowerLimit): + case (femto_flow_selection::SelectionType::kAbsLowerLimit): + case (femto_flow_selection::SelectionType::kEqual): + if (minimalSel > sel.getSelectionValue()) { + minimalSel = sel.getSelectionValue(); + } + break; + } + } + } + return minimalSel; + } + + /// The total number of different selections + /// \return Total number of selections + size_t getNSelections() + { + return mSelections.size(); + } + + /// The number of selection of an individual variable + /// \param selVar Selection variable under consideration + /// \return Number of selection of the individual variable + size_t getNSelections(selVariable selVar) + { + return getSelections(selVar).size(); + } + + /// Obtain the selections of an individual variable + /// \param selVar Selection variable under consideration + /// \return All selections of the individual variable + std::vector> getSelections(selVariable selVar) + { + std::vector> selValVec; + for (const auto& it : mSelections) { + if (it.getSelectionVariable() == selVar) { + selValVec.push_back(it); + } + } + return selValVec; + } + + /// Retrieve all the different selection variables + /// \return std::vector containing all the different selection variables + std::vector getSelectionVariables() + { + std::vector selVarVec; + for (const auto& it : mSelections) { + auto selVar = it.getSelectionVariable(); + if (std::none_of( + selVarVec.begin(), + selVarVec.end(), + [selVar](selVariable a) { + return a == selVar; + })) { + selVarVec.push_back(selVar); + } + } + return selVarVec; + } + + protected: + o2::framework::HistogramRegistry* mHistogramRegistry; ///< For QA output + std::vector> mSelections; ///< Vector containing all selections +}; + +} // namespace femto_flow +} // namespace o2::analysis + +#endif // PWGCF_FEMTO_CORE_FEMTOFLOWOBJECTSELECTION_H_ diff --git a/PWGCF/Femto/Core/FemtoFlowPairCleaner.h b/PWGCF/Femto/Core/FemtoFlowPairCleaner.h new file mode 100644 index 00000000000..e54abebe548 --- /dev/null +++ b/PWGCF/Femto/Core/FemtoFlowPairCleaner.h @@ -0,0 +1,196 @@ +// 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 FemtoFlowPairCleaner.h +/// \brief FemtoFlowPairCleaner - Makes sure only proper candidates are paired +/// \author Andi Mathis, TU München, andreas.mathis@ph.tum.de +/// \author Laura Serksnyte, TU München,laura.serksnyte@cern.ch +/// \author Zuzanna Chochulska, WUT Warsaw & CTU Prague, zchochul@cern.ch +/// \author Shirajum Monira, WUT Warsaw, shirajum.monira@cern.ch + +#ifndef PWGCF_FEMTO_CORE_FEMTOFLOWPAIRCLEANER_H_ +#define PWGCF_FEMTO_CORE_FEMTOFLOWPAIRCLEANER_H_ + +#include "PWGCF/Femto/DataModel/FemtoDerived.h" + +#include "Framework/HistogramRegistry.h" + +namespace o2::analysis::femto_flow +{ + +/// \class FemtoFlowPairCleaner +/// \brief Class taking care that no autocorrelations enter the same event distribution +/// \tparam partOne Type of particle 1 (Track/V0/Cascade/...) +/// \tparam partTwo Type of particle 2 (Track/V0/Cascade/...) +template +class FemtoFlowPairCleaner +{ + public: + /// Destructor + virtual ~FemtoFlowPairCleaner() = default; + + /// Initialization of the QA histograms + /// \param registry HistogramRegistry + void init(o2::framework::HistogramRegistry* registry) + { + if (registry) { + mHistogramRegistry = registry; + // \todo some QA histograms like in FemtoFlow + } + } + + /// Check whether a given pair has shared tracks + /// \tparam Part Data type of the particle + /// \tparam Parts Data type of the collection of all particles + /// \param part1 Particle 1 + /// \param part2 Particle 2 + /// \param particles Collection of all particles passed to the task + /// \return Whether the pair has shared tracks + template + bool isCleanPair(Part const& part1, Part const& part2, Parts const& particles) + { + using namespace o2::framework; + + if constexpr (kPartOneType == o2::aod::femtoflowparticle::ParticleType::kTrack && kPartTwoType == o2::aod::femtoflowparticle::ParticleType::kTrack) { + /// Track-Track combination + if (part1.partType() != o2::aod::femtoflowparticle::ParticleType::kTrack || part2.partType() != o2::aod::femtoflowparticle::ParticleType::kTrack) { + LOG(fatal) << "FemtoFlowPairCleaner: passed arguments don't agree with FemtoFlowPairCleaner instantiation! Please provide kTrack,kTrack candidates."; + return false; + } + return part1.globalIndex() != part2.globalIndex(); + } else if constexpr (kPartOneType == o2::aod::femtoflowparticle::ParticleType::kMCTruthTrack && kPartTwoType == o2::aod::femtoflowparticle::ParticleType::kMCTruthTrack) { + /// Track-Track combination + if (part1.partType() != o2::aod::femtoflowparticle::ParticleType::kMCTruthTrack || part2.partType() != o2::aod::femtoflowparticle::ParticleType::kMCTruthTrack) { + LOG(fatal) << "FemtoFlowPairCleaner: passed arguments don't agree with FemtoFlowPairCleaner instantiation! Please provide kMCTruthTrack,kMCTruthTrack candidates."; + return false; + } + return part1.globalIndex() != part2.globalIndex(); + } else if constexpr (kPartOneType == o2::aod::femtoflowparticle::ParticleType::kTrack && kPartTwoType == o2::aod::femtoflowparticle::ParticleType::kV0) { + /// Track-V0 combination part1 is hadron and part2 is v0 + if (part2.partType() != o2::aod::femtoflowparticle::ParticleType::kV0) { + LOG(fatal) << "FemtoFlowPairCleaner: passed arguments don't agree with FemtoFlowPairCleaner instantiation! Please provide second argument kV0 candidate."; + return false; + } + // Getting v0 (part2) children + const auto& posChild = particles.iteratorAt(part2.index() - 2); + const auto& negChild = particles.iteratorAt(part2.index() - 1); + if (part1.globalIndex() == posChild.globalIndex() || part1.globalIndex() == negChild.globalIndex()) { + return false; + } + return part1.globalIndex() != part2.globalIndex(); + } else if constexpr (kPartOneType == o2::aod::femtoflowparticle::ParticleType::kV0 && kPartTwoType == o2::aod::femtoflowparticle::ParticleType::kV0) { + /// V0-V0 combination both part1 and part2 are v0 + if (part1.partType() != o2::aod::femtoflowparticle::ParticleType::kV0 || part2.partType() != o2::aod::femtoflowparticle::ParticleType::kV0) { + LOG(fatal) << "FemtoFlowPairCleaner: passed arguments don't agree with FemtoFlowPairCleaner instantiation! Please provide first and second arguments kV0 candidate."; + return false; + } + // Getting v0 children for part1 + const auto& posChild1 = particles.iteratorAt(part1.index() - 2); + const auto& negChild1 = particles.iteratorAt(part1.index() - 1); + // Getting v0 children for part2 + const auto& posChild2 = particles.iteratorAt(part2.index() - 2); + const auto& negChild2 = particles.iteratorAt(part2.index() - 1); + if (posChild1.globalIndex() == posChild2.globalIndex() || negChild1.globalIndex() == negChild2.globalIndex()) { + return false; + } + return part1.globalIndex() != part2.globalIndex(); + } else if constexpr (kPartOneType == o2::aod::femtoflowparticle::ParticleType::kTrack && kPartTwoType == o2::aod::femtoflowparticle::ParticleType::kCascade) { + /// Track-Cascade combination part1 is hadron and part2 is cascade + if (part1.partType() != o2::aod::femtoflowparticle::ParticleType::kTrack || part2.partType() != o2::aod::femtoflowparticle::ParticleType::kCascade) { + LOG(fatal) << "FemtoFlowPairCleaner: passed arguments don't agree with FemtoFlowPairCleaner instantiation! Please provide first argument kTrack candidate and second argument kCascade candidate."; + return false; + } + // Getting cascade children for part2 + const auto& posChild = particles.iteratorAt(part2.index() - 3); + const auto& negChild = particles.iteratorAt(part2.index() - 2); + const auto& bachelor = particles.iteratorAt(part2.index() - 1); + if (part1.globalIndex() == posChild.globalIndex() || part1.globalIndex() == negChild.globalIndex() || part1.globalIndex() == bachelor.globalIndex()) { + return false; + } + return part1.globalIndex() != part2.globalIndex(); + } else if constexpr (kPartOneType == o2::aod::femtoflowparticle::ParticleType::kCascade && kPartTwoType == o2::aod::femtoflowparticle::ParticleType::kCascade) { + /// Cascade-Cascade combination both part1 and part2 are cascades + if (part1.partType() != o2::aod::femtoflowparticle::ParticleType::kCascade || part2.partType() != o2::aod::femtoflowparticle::ParticleType::kCascade) { + LOG(fatal) << "FemtoFlowPairCleaner: passed arguments don't agree with FemtoFlowPairCleaner instantiation! Please provide first and second arguments kCascade candidate."; + return false; + } + // Getting cascade children for part1 + const auto& posChild1 = particles.iteratorAt(part1.index() - 3); + const auto& negChild1 = particles.iteratorAt(part1.index() - 2); + const auto& bachelor1 = particles.iteratorAt(part1.index() - 1); + // Getting cascade children for part2 + const auto& posChild2 = particles.iteratorAt(part2.index() - 3); + const auto& negChild2 = particles.iteratorAt(part2.index() - 2); + const auto& bachelor2 = particles.iteratorAt(part2.index() - 1); + if (posChild1.globalIndex() == posChild2.globalIndex() || negChild1.globalIndex() == negChild2.globalIndex() || bachelor1.globalIndex() == bachelor2.globalIndex()) { + return false; + } + return part1.globalIndex() != part2.globalIndex(); + } else if constexpr (kPartOneType == o2::aod::femtoflowparticle::ParticleType::kV0 && kPartTwoType == o2::aod::femtoflowparticle::ParticleType::kCascade) { + /// V0-Cascade combination where part1 is a V0 and part2 is a cascade + if (part1.partType() != o2::aod::femtoflowparticle::ParticleType::kV0 || part2.partType() != o2::aod::femtoflowparticle::ParticleType::kCascade) { + LOG(fatal) << "FemtoFlowPairCleaner: passed arguments don't agree with FemtoFlowPairCleaner instantiation! Please provide first argument kV0 candidate and second argument kCascade candidate."; + return false; + } + // part1 v0 children + const auto& posChild1 = particles.iteratorAt(part1.index() - 2); + const auto& negChild1 = particles.iteratorAt(part1.index() - 1); + // part2 cascade children + const auto& posChild2 = particles.iteratorAt(part2.index() - 3); + const auto& negChild2 = particles.iteratorAt(part2.index() - 2); + const auto& bachelor2 = particles.iteratorAt(part2.index() - 1); + if (posChild1.globalIndex() == posChild2.globalIndex() || negChild1.globalIndex() == negChild2.globalIndex() || posChild1.globalIndex() == bachelor2.globalIndex() || negChild1.globalIndex() == bachelor2.globalIndex()) { + return false; + } + return part1.globalIndex() != part2.globalIndex(); + } else if constexpr (kPartOneType == o2::aod::femtoflowparticle::ParticleType::kTrack && kPartTwoType == o2::aod::femtoflowparticle::ParticleType::kD0) { + /// Track-D0 combination part1 is hadron and part2 is D0 + if (part2.partType() != o2::aod::femtoflowparticle::ParticleType::kD0) { + LOG(fatal) << "FemtoFlowPairCleaner: passed arguments don't agree with FemtoFlowPairCleaner instantiation! Please provide second argument kD0 candidate."; + return false; + } + // Getting D0 (part2) children + const auto& posChild = particles.iteratorAt(part2.index() - 2); + const auto& negChild = particles.iteratorAt(part2.index() - 1); + + if (part1.globalIndex() != posChild.globalIndex() && part1.globalIndex() != negChild.globalIndex()) { + return true; + } + return false; + } else if constexpr (kPartOneType == o2::aod::femtoflowparticle::ParticleType::kTrack && kPartTwoType == o2::aod::femtoflowparticle::ParticleType::kPhi) { + /// Track-Phi combination part1 is Phi and part 2 is hadron + if (part1.partType() != o2::aod::femtoflowparticle::ParticleType::kTrack || part2.partType() != o2::aod::femtoflowparticle::ParticleType::kPhi) { + LOG(fatal) << "FemtoFlowPairCleaner: passed arguments don't agree with FemtoFlowPairCleaner instantiation! Please provide second argument kPhi candidate."; + return false; + } + + // getting Phi (part1) children + const auto& posChild = particles.iteratorAt(part2.index() - 2); + const auto& negChild = particles.iteratorAt(part2.index() - 1); + + if (part1.globalIndex() != posChild.globalIndex() && part1.globalIndex() != negChild.globalIndex()) { + return true; + } + return false; + } else { + LOG(fatal) << "FemtoFlowPairCleaner: Combination of objects not defined - quitting!"; + return false; + } + } + + private: + o2::framework::HistogramRegistry* mHistogramRegistry; ///< For QA output + static constexpr o2::aod::femtoflowparticle::ParticleType kPartOneType = partOne; ///< Type of particle 1 + static constexpr o2::aod::femtoflowparticle::ParticleType kPartTwoType = partTwo; ///< Type of particle 2 +}; +} // namespace o2::analysis::femto_flow + +#endif // PWGCF_FEMTO_CORE_FEMTOFLOWPAIRCLEANER_H_ diff --git a/PWGCF/Femto/Core/FemtoFlowParticleHisto.h b/PWGCF/Femto/Core/FemtoFlowParticleHisto.h new file mode 100644 index 00000000000..16fcd472135 --- /dev/null +++ b/PWGCF/Femto/Core/FemtoFlowParticleHisto.h @@ -0,0 +1,558 @@ +// 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 FemtoFlowParticleHisto.h +/// \brief FemtoFlowParticleHisto - Histogram class for tracks, V0s and cascades +/// \author Andi Mathis, TU München, andreas.mathis@ph.tum.de +/// \author Georgios Mantzaridis, TU München, georgios.mantzaridis@tum.de +/// \author Anton Riedel, TU München, anton.riedel@tum.de +/// \author Zuzanna Chochulska, WUT Warsaw & CTU Prague, zchochul@cern.ch +/// \author Shirajum Monira, WUT Warsaw, shirajum.monira.dokt@pw.edu.pl + +#ifndef PWGCF_FEMTO_CORE_FEMTOFLOWPARTICLEHISTO_H_ +#define PWGCF_FEMTO_CORE_FEMTOFLOWPARTICLEHISTO_H_ + +#include "PWGCF/Femto/DataModel/FemtoDerived.h" + +#include "CommonConstants/MathConstants.h" +#include "Framework/HistogramRegistry.h" + +#include + +#include +#include + +namespace o2::analysis::femto_flow +{ + +/// \class FemtoFlowParticleHisto +/// \brief Class for histogramming particle properties +/// \tparam particleType Type of the particle (Track/V0/Cascade/Phi/...) +/// \tparam suffixType (optional) Takes care of the suffix for the folder name in case of analyses of pairs of the same kind (T-T, V-V, C-C) +template +class FemtoFlowParticleHisto +{ + public: + /// Destructor + virtual ~FemtoFlowParticleHisto() = default; + + /// Initializes particle histograms + /// Called by init both in case of reconstructed data/ Monte Carlo, and for Monte Carlo Truth + /// \tparam T type of the axis Object + /// \tparam mc enum object to get the suffix ("" for data/ Monte Cartlo reconstructed, "_MC" for Monte Carlo truth) for the folder in the output file + /// \param folderName base path of the directory in the output file, in which to store the histograms + /// \param tempFitVarAxisTitle Title of the axis of the tempFitVar (DCA_xy in case of tracks, CPA in case of V0s, etc.) + /// \param tempFitVarpTAxis axis object for the pT axis in the pT vs. tempFitVar plots + /// \param tempFitVarAxis axis object for the tempFitVar axis + template + void initBase(std::string folderName, std::string tempFitVarAxisTitle, T& tempFitVarpTAxis, T& tempFitVarAxis) // o2-linter: disable=name/function-variable + { + using namespace o2::framework; + + std::string folderSuffix = static_cast(o2::aod::femtoflow_mc_particle::MCTypeName[mc]).c_str(); + /// Histograms of the kinematic properties + mHistogramRegistry->add((folderName + folderSuffix + "/hPt").c_str(), "; #it{p}_{T} (GeV/#it{c}); Entries", kTH1F, {tempFitVarpTAxis}); + mHistogramRegistry->add((folderName + folderSuffix + "/hEta").c_str(), "; #eta; Entries", kTH1F, {{200, -1.5, 1.5}}); + mHistogramRegistry->add((folderName + folderSuffix + "/hPhi").c_str(), "; #phi; Entries", kTH1F, {{200, 0, o2::constants::math::TwoPI}}); + mHistogramRegistry->add((folderName + folderSuffix + "/hPhiEta").c_str(), "; #phi; #eta", kTH2F, {{200, 0, o2::constants::math::TwoPI}, {200, -1.5, 1.5}}); + + /// particle specific histogramms for the TempFitVar column in FemtoFlowParticles + if constexpr (o2::aod::femtoflow_mc_particle::MCType::kRecon == mc) { + mHistogramRegistry->add((folderName + folderSuffix + static_cast(o2::aod::femtoflowparticle::TempFitVarName[kParticleType])).c_str(), ("; #it{p}_{T} (GeV/#it{c}); " + tempFitVarAxisTitle).c_str(), kTH2F, {{tempFitVarpTAxis}, {tempFitVarAxis}}); + } + } + + // comment + template + void initDebug(std::string folderName) // o2-linter: disable=name/function-variable + { + using namespace o2::framework; + + std::string folderSuffix = static_cast(o2::aod::femtoflow_mc_particle::MCTypeName[mc]).c_str(); + if constexpr (kParticleType == o2::aod::femtoflowparticle::ParticleType::kTrack || kParticleType == o2::aod::femtoflowparticle::ParticleType::kV0Child || kParticleType == o2::aod::femtoflowparticle::ParticleType::kCascadeBachelor || kParticleType == o2::aod::femtoflowparticle::ParticleType::kMCTruthTrack) { + mHistogramRegistry->add((folderName + folderSuffix + "/hCharge").c_str(), "; Charge; Entries", kTH1F, {{5, -2.5, 2.5}}); + mHistogramRegistry->add((folderName + folderSuffix + "/hTPCfindable").c_str(), "; TPC findable clusters; Entries", kTH1F, {{163, -0.5, 162.5}}); + mHistogramRegistry->add((folderName + folderSuffix + "/hTPCfound").c_str(), "; TPC found clusters; Entries", kTH1F, {{163, -0.5, 162.5}}); + mHistogramRegistry->add((folderName + folderSuffix + "/hTPCcrossedOverFindable").c_str(), "; TPC ratio findable; Entries", kTH1F, {{100, 0.5, 1.5}}); + mHistogramRegistry->add((folderName + folderSuffix + "/hTPCcrossedRows").c_str(), "; TPC crossed rows; Entries", kTH1F, {{163, -0.5, 162.5}}); + mHistogramRegistry->add((folderName + folderSuffix + "/hTPCfindableVsCrossed").c_str(), ";TPC findable clusters ; TPC crossed rows;", kTH2F, {{163, -0.5, 162.5}, {163, -0.5, 162.5}}); + mHistogramRegistry->add((folderName + folderSuffix + "/hTPCshared").c_str(), "; TPC shared clusters; Entries", kTH1F, {{163, -0.5, 162.5}}); + mHistogramRegistry->add((folderName + folderSuffix + "/hTPCfractionSharedCls").c_str(), "; TPC fraction of shared clusters; Entries", kTH1F, {{100, 0.0, 100.0}}); + mHistogramRegistry->add((folderName + folderSuffix + "/hITSclusters").c_str(), "; ITS clusters; Entries", kTH1F, {{10, -0.5, 9.5}}); + mHistogramRegistry->add((folderName + folderSuffix + "/hITSclustersIB").c_str(), "; ITS clusters in IB; Entries", kTH1F, {{10, -0.5, 9.5}}); + mHistogramRegistry->add((folderName + folderSuffix + "/hDCAz").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{z} (cm)", kTH2F, {{100, 0, 10}, {500, -5, 5}}); + mHistogramRegistry->add((folderName + folderSuffix + "/hDCA").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA (cm)", kTH2F, {{100, 0, 10}, {301, 0., 1.5}}); + mHistogramRegistry->add((folderName + folderSuffix + "/hTPCdEdX").c_str(), "; #it{p} (GeV/#it{c}); TPC Signal", kTH2F, {{100, 0, 10}, {1000, 0, 1000}}); + mHistogramRegistry->add((folderName + folderSuffix + "/hTOFBeta").c_str(), "; #it{p} (GeV/#it{c}); TOF Beta", kTH2F, {{100, 0, 5}, {100, 0.1, 1.1}}); + mHistogramRegistry->add((folderName + folderSuffix + "/nSigmaTPC_el").c_str(), "; #it{p} (GeV/#it{c}); n#sigma_{TPC}^{e}", kTH2F, {{100, 0, 10}, {200, -4.975, 5.025}}); + mHistogramRegistry->add((folderName + folderSuffix + "/nSigmaTPC_pi").c_str(), "; #it{p} (GeV/#it{c}); n#sigma_{TPC}^{#pi}", kTH2F, {{100, 0, 10}, {200, -4.975, 5.025}}); + mHistogramRegistry->add((folderName + folderSuffix + "/nSigmaTPC_K").c_str(), "; #it{p} (GeV/#it{c}); n#sigma_{TPC}^{K}", kTH2F, {{100, 0, 10}, {200, -4.975, 5.025}}); + mHistogramRegistry->add((folderName + folderSuffix + "/nSigmaTPC_p").c_str(), "; #it{p} (GeV/#it{c}); n#sigma_{TPC}^{p}", kTH2F, {{100, 0, 10}, {200, -4.975, 5.025}}); + mHistogramRegistry->add((folderName + folderSuffix + "/nSigmaTPC_d").c_str(), "; #it{p} (GeV/#it{c}); n#sigma_{TPC}^{d}", kTH2F, {{100, 0, 10}, {200, -4.975, 5.025}}); + mHistogramRegistry->add((folderName + folderSuffix + "/nSigmaTOF_el").c_str(), "; #it{p} (GeV/#it{c}); n#sigma_{TOF}^{e}", kTH2F, {{100, 0, 10}, {200, -4.975, 5.025}}); + mHistogramRegistry->add((folderName + folderSuffix + "/nSigmaTOF_pi").c_str(), "; #it{p} (GeV/#it{c}); n#sigma_{TOF}^{#pi}", kTH2F, {{100, 0, 10}, {200, -4.975, 5.025}}); + mHistogramRegistry->add((folderName + folderSuffix + "/nSigmaTOF_K").c_str(), "; #it{p} (GeV/#it{c}); n#sigma_{TOF}^{K}", kTH2F, {{100, 0, 10}, {200, -4.975, 5.025}}); + mHistogramRegistry->add((folderName + folderSuffix + "/nSigmaTOF_p").c_str(), "; #it{p} (GeV/#it{c}); n#sigma_{TOF}^{p}", kTH2F, {{100, 0, 10}, {200, -4.975, 5.025}}); + mHistogramRegistry->add((folderName + folderSuffix + "/nSigmaTOF_d").c_str(), "; #it{p} (GeV/#it{c}); n#sigma_{TOF}^{d}", kTH2F, {{100, 0, 10}, {200, -4.975, 5.025}}); + mHistogramRegistry->add((folderName + folderSuffix + "/nSigmaComb_el").c_str(), "; #it{p} (GeV/#it{c}); n#sigma_{comb}^{e}", kTH2F, {{100, 0, 10}, {100, 0, 5}}); + mHistogramRegistry->add((folderName + folderSuffix + "/nSigmaComb_pi").c_str(), "; #it{p} (GeV/#it{c}); n#sigma_{comb}^{#pi}", kTH2F, {{100, 0, 10}, {100, 0, 5}}); + mHistogramRegistry->add((folderName + folderSuffix + "/nSigmaComb_K").c_str(), "; #it{p} (GeV/#it{c}); n#sigma_{comb}^{K}", kTH2F, {{100, 0, 10}, {100, 0, 5}}); + mHistogramRegistry->add((folderName + folderSuffix + "/nSigmaComb_p").c_str(), "; #it{p} (GeV/#it{c}); n#sigma_{comb}^{p}", kTH2F, {{100, 0, 10}, {100, 0, 5}}); + mHistogramRegistry->add((folderName + folderSuffix + "/nSigmaComb_d").c_str(), "; #it{p} (GeV/#it{c}); n#sigma_{comb}^{d}", kTH2F, {{100, 0, 10}, {100, 0, 5}}); + } else if constexpr (kParticleType == o2::aod::femtoflowparticle::ParticleType::kV0) { + mHistogramRegistry->add((folderName + folderSuffix + "/hDaughDCA").c_str(), "; DCA^{daugh} (cm); Entries", kTH1F, {{1000, 0, 10}}); + mHistogramRegistry->add((folderName + folderSuffix + "/hTransRadius").c_str(), "; #it{r}_{xy} (cm); Entries", kTH1F, {{1500, 0, 150}}); + mHistogramRegistry->add((folderName + folderSuffix + "/hDecayVtxX").c_str(), "; #it{Vtx}_{x} (cm); Entries", kTH1F, {{2000, 0, 200}}); + mHistogramRegistry->add((folderName + folderSuffix + "/hDecayVtxY").c_str(), "; #it{Vtx}_{y} (cm)); Entries", kTH1F, {{2000, 0, 200}}); + mHistogramRegistry->add((folderName + folderSuffix + "/hDecayVtxZ").c_str(), "; #it{Vtx}_{z} (cm); Entries", kTH1F, {{2000, 0, 200}}); + mHistogramRegistry->add((folderName + folderSuffix + "/hInvMassLambda").c_str(), "; M_{#Lambda}; Entries", kTH1F, {{2000, 1.f, 3.f}}); + mHistogramRegistry->add((folderName + folderSuffix + "/hInvMassLambdaVsPt").c_str(), "; #it{p}_{T} (GeV/#it{c}); M_{#Lambda}; Entries", kTH2F, {{240, 0, 6}, {2000, 1.f, 3.f}}); + mHistogramRegistry->add((folderName + folderSuffix + "/hInvMassAntiLambda").c_str(), "; M_{#bar{#Lambda}}; Entries", kTH1F, {{2000, 1.f, 3.f}}); + mHistogramRegistry->add((folderName + folderSuffix + "/hInvMassAntiLambdaVsPt").c_str(), "; #it{p}_{T} (GeV/#it{c}); M_{#Lambda}; Entries", kTH2F, {{240, 0, 6}, {2000, 1.f, 3.f}}); + mHistogramRegistry->add((folderName + folderSuffix + "/hInvMassLambdaAntiLambda").c_str(), "; M_{#Lambda}; M_{#bar{#Lambda}}", kTH2F, {{2000, 1.f, 3.f}, {2000, 1.f, 3.f}}); + } else if constexpr (kParticleType == o2::aod::femtoflowparticle::ParticleType::kCascade) { + mHistogramRegistry->add((folderName + folderSuffix + "/hDaughDCA").c_str(), "; DCA^{daugh} (cm); Entries", kTH1F, {{1000, 0, 10}}); + mHistogramRegistry->add((folderName + folderSuffix + "/hTransRadius").c_str(), "; #it{r}_{xy} (cm); Entries", kTH1F, {{1500, 0, 150}}); + mHistogramRegistry->add((folderName + folderSuffix + "/hDecayVtxX").c_str(), "; #it{Vtx}_{x} (cm); Entries", kTH1F, {{2000, 0, 200}}); + mHistogramRegistry->add((folderName + folderSuffix + "/hDecayVtxY").c_str(), "; #it{Vtx}_{y} (cm)); Entries", kTH1F, {{2000, 0, 200}}); + mHistogramRegistry->add((folderName + folderSuffix + "/hDecayVtxZ").c_str(), "; #it{Vtx}_{z} (cm); Entries", kTH1F, {{2000, 0, 200}}); + mHistogramRegistry->add((folderName + folderSuffix + "/hInvMassXi").c_str(), "; M_{Xi}; Entries", kTH1F, {{2000, 1.f, 1.8f}}); + mHistogramRegistry->add((folderName + folderSuffix + "/hInvMassOmega").c_str(), "; M_{Omega}; Entries", kTH1F, {{2000, 1.f, 1.8f}}); + } + } + + /// Initializes specialized Monte Carlo particle histograms + /// internal function called by init only in case of Monte Carlo truth + /// \tparam T type of the axis Object + /// \param folderName base path of the directory in the output file, in which to store the histograms + /// \param tempFitVarAxisTitle Title of the axis of the tempFitVar (DCA_xy in case of tracks, CPA in case of V0s, etc.) + /// \param tempFitVarpTAxis axis object for the pT axis in the pT vs. tempFitVar plots + /// \param tempFitVarAxis axis object for the tempFitVar axis + template + void initMC(std::string folderName, std::string /*tempFitVarAxisTitle*/, T& tempFitVarpTAxis, T& tempFitVarAxis, bool isDebug) // o2-linter: disable=name/function-variable + { + using namespace o2::framework; + + /// Particle-type specific histograms + std::string folderSuffix = static_cast(o2::aod::femtoflow_mc_particle::MCTypeName[o2::aod::femtoflow_mc_particle::MCType::kTruth]).c_str(); + + mHistogramRegistry->add((folderName + folderSuffix + "/hPt_ReconNoFake").c_str(), "; #it{p}_{T} (GeV/#it{c}); Entries", kTH1F, {tempFitVarpTAxis}); + mHistogramRegistry->add((folderName + folderSuffix + "/hPDG").c_str(), "; PDG; Entries", kTH1I, {{6001, -3000, 3000}}); + mHistogramRegistry->add((folderName + folderSuffix + "/hOrigin_MC").c_str(), "; Origin; Entries", kTH1I, {{100, 0, 100}}); + mHistogramRegistry->add((folderName + folderSuffix + "/hNoMCtruthCounter").c_str(), "; Counter; Entries", kTH1I, {{1, 0, 1}}); + + if constexpr (kParticleType == o2::aod::femtoflowparticle::ParticleType::kTrack || kParticleType == o2::aod::femtoflowparticle::ParticleType::kV0Child || kParticleType == o2::aod::femtoflowparticle::ParticleType::kCascadeBachelor || kParticleType == o2::aod::femtoflowparticle::ParticleType::kMCTruthTrack) { + /// Track histograms + if (isDebug) { + mHistogramRegistry->add((folderName + folderSuffix + "/Debug/hPDGmother_Primary").c_str(), "; PDG mother; Entries", kTH1I, {{6001, -3000.5, 3000.5}}); + mHistogramRegistry->add((folderName + folderSuffix + "/Debug/hPDGmother_Daughter").c_str(), "; PDG mother; Entries", kTH1I, {{6001, -3000.5, 3000.5}}); + mHistogramRegistry->add((folderName + folderSuffix + "/Debug/hPDGmother_Material").c_str(), "; PDG mother; Entries", kTH1I, {{6001, -3000.5, 3000.5}}); + mHistogramRegistry->add((folderName + folderSuffix + "/Debug/hPDGmother_WrongCollision").c_str(), "; PDG mother; Entries", kTH1I, {{6001, -3000.5, 3000.5}}); + mHistogramRegistry->add((folderName + folderSuffix + "/Debug/hPDGmother_Fake").c_str(), "; PDG mother; Entries", kTH1I, {{6001, -3000.5, 3000.5}}); + mHistogramRegistry->add((folderName + folderSuffix + "/Debug/hPDGmother_Else").c_str(), "; PDG mother; Entries", kTH1I, {{6001, -3000.5, 3000.5}}); + mHistogramRegistry->add((folderName + folderSuffix + "/Debug/hPDGmother_DaughterLambda").c_str(), "; PDG mother; Entries", kTH1I, {{12001, -6000.5, 6000.5}}); + mHistogramRegistry->add((folderName + folderSuffix + "/Debug/hPDGmother_DaughterSigmaplus").c_str(), "; PDG mother; Entries", kTH1I, {{12001, -6000.5, 6000.5}}); + + mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_Primary").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", kTH2F, {tempFitVarpTAxis, tempFitVarAxis}); + mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_Daughter").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", kTH2F, {tempFitVarpTAxis, tempFitVarAxis}); + mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_Material").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", kTH2F, {tempFitVarpTAxis, tempFitVarAxis}); + mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_WrongCollision").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", kTH2F, {tempFitVarpTAxis, tempFitVarAxis}); + mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_Fake").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", kTH2F, {tempFitVarpTAxis, tempFitVarAxis}); + mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_Else").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", kTH2F, {tempFitVarpTAxis, tempFitVarAxis}); + mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_DaughterLambda").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", kTH2F, {tempFitVarpTAxis, tempFitVarAxis}); + mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_DaughterSigmaplus").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", kTH2F, {tempFitVarpTAxis, tempFitVarAxis}); + mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_NoMCTruthOrigin").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", kTH2F, {tempFitVarpTAxis, tempFitVarAxis}); + mHistogramRegistry->add((folderName + folderSuffix + "/hMisidentification").c_str(), "; #it{p}_{T} (GeV/#it{c}); Particle; Particle", kTH3F, {{4, 0, 4}, {4, 0, 4}, tempFitVarpTAxis}); + + } else { + mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_Primary").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", kTH2F, {tempFitVarpTAxis, tempFitVarAxis}); + mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_Daughter").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", kTH2F, {tempFitVarpTAxis, tempFitVarAxis}); + mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_Material").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", kTH2F, {tempFitVarpTAxis, tempFitVarAxis}); + mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_WrongCollision").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", kTH2F, {tempFitVarpTAxis, tempFitVarAxis}); + mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_Fake").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", kTH2F, {tempFitVarpTAxis, tempFitVarAxis}); + mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_Else").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", kTH2F, {tempFitVarpTAxis, tempFitVarAxis}); + mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_NoMCTruthOrigin").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", kTH2F, {tempFitVarpTAxis, tempFitVarAxis}); + mHistogramRegistry->add((folderName + folderSuffix + "/hMisidentification").c_str(), "; #it{p}_{T} (GeV/#it{c}); Particle; Particle", kTH3F, {{4, 0, 4}, {4, 0, 4}, tempFitVarpTAxis}); + + mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_DaughterLambda").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", kTH2F, {tempFitVarpTAxis, tempFitVarAxis}); + mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_DaughterSigmaplus").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", kTH2F, {tempFitVarpTAxis, tempFitVarAxis}); + } + + } else if constexpr (kParticleType == o2::aod::femtoflowparticle::ParticleType::kV0) { + /// V0 histograms + /// to be implemented + } else if constexpr (kParticleType == o2::aod::femtoflowparticle::ParticleType::kCascade) { + /// Cascade histograms + /// to be implemented + } else if constexpr (kParticleType == o2::aod::femtoflowparticle::ParticleType::kPhi) { + // Phi histograms + + } else if constexpr (kParticleType == o2::aod::femtoflowparticle::ParticleType::kD0) { + // D0/D0bar histograms + } else { + LOG(fatal) << "FemtoFlowParticleHisto: Histogramming for requested object not defined - quitting!"; + } + } + + /// Templated function for the initialization of the QA histograms + /// Always calls initBase to initialize the histograms with data/ Monte Carlo reconstructed + /// In case of Monte Carlo, calls initBase again for Monte Carlo truth and the specialized function initMC for additional Monte Carlo histogramms + /// \tparam T type of the axis binning + /// \param registry Histogram registry to be passed + /// \param tempFitVarpTBins binning of the pT axis in the pT vs. tempFitVar + /// \param tempFitVarBins binning of the tempFitVar (DCA_xy in case of tracks, CPA in case of V0s, etc.) + /// \param isMC add Monte Carlo truth histograms to the output file + template + void init(o2::framework::HistogramRegistry* registry, T& tempFitVarpTBins, T& tempFitVarBins, bool isMC, int pdgCode, bool isDebug = false, std::optional flexibleFolder = std::nullopt) + { + using namespace o2::framework; + + mPDG = pdgCode; + if (registry) { + mHistogramRegistry = registry; + /// The folder names are defined by the type of the object and the suffix (if applicable) + std::string tempFitVarAxisTitle; + if constexpr (kParticleType == o2::aod::femtoflowparticle::ParticleType::kTrack || kParticleType == o2::aod::femtoflowparticle::ParticleType::kV0Child || kParticleType == o2::aod::femtoflowparticle::ParticleType::kCascadeBachelor) { + /// Track histograms + tempFitVarAxisTitle = "DCA_{xy} (cm)"; + } else if constexpr (kParticleType == o2::aod::femtoflowparticle::ParticleType::kMCTruthTrack) { + /// MC Truth Track histograms + tempFitVarAxisTitle = "PDG code"; + } else if constexpr (kParticleType == o2::aod::femtoflowparticle::ParticleType::kV0) { + /// V0 histograms + tempFitVarAxisTitle = "cos#alpha"; + } else if constexpr (kParticleType == o2::aod::femtoflowparticle::ParticleType::kCascade) { + /// Cascade histograms + tempFitVarAxisTitle = "cos#alpha"; + } else if constexpr (kParticleType == o2::aod::femtoflowparticle::ParticleType::kPhi) { + // Phi histograms + tempFitVarAxisTitle = "#Phi invariant mass"; + } else if constexpr (kParticleType == o2::aod::femtoflowparticle::ParticleType::kD0) { + // D0/D0bar histograms + tempFitVarAxisTitle = "D^{0}/#bar{D^{0}} invariant mass"; + } else { + LOG(fatal) << "FemtoFlowParticleHisto: Histogramming for requested object not defined - quitting!"; + } + + o2::framework::AxisSpec tempFitVarpTAxis = {tempFitVarpTBins, "#it{p}_{T} (GeV/#it{c})"}; // the pT binning may vary + o2::framework::AxisSpec tempFitVarAxis = {tempFitVarBins, tempFitVarAxisTitle}; + + // std::string folderName = (static_cast(o2::aod::femtoflowparticle::ParticleTypeName[kParticleType]).c_str() + static_cast(kFolderSuffix[kFolderSuffixType])).c_str(); + std::string folderName = flexibleFolder.value_or((static_cast(o2::aod::femtoflowparticle::ParticleTypeName[kParticleType]) + static_cast(kFolderSuffix[kFolderSuffixType]))); + + // Fill here the actual histogramms by calling initBase and initMC + initBase(folderName, tempFitVarAxisTitle, tempFitVarpTAxis, tempFitVarAxis); + if (isDebug) { + initDebug(folderName); + } + if (isMC) { + initBase(folderName, tempFitVarAxisTitle, tempFitVarpTAxis, tempFitVarAxis); + initMC(folderName, tempFitVarAxisTitle, tempFitVarpTAxis, tempFitVarAxis, isDebug); + } + } + } + + /// Filling of the histograms + /// Called by init both in case of reconstructed data/ Monte Carlo, and for Monte Carlo Truth + /// \tparam T Data type of the particle + /// \param part Particle + template + void fillQABase(T const& part, H const& histFolder) // o2-linter: disable=name/function-variable + { + using namespace o2::framework; + + /// Histograms of the kinematic properties + mHistogramRegistry->fill(histFolder + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("/hPt"), part.pt()); + mHistogramRegistry->fill(histFolder + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("/hEta"), part.eta()); + mHistogramRegistry->fill(histFolder + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("/hPhi"), part.phi()); + mHistogramRegistry->fill(histFolder + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("/hPhiEta"), part.phi(), part.eta()); + + /// particle specific histogramms for the TempFitVar column in FemtoFlowParticles + if constexpr (mc == o2::aod::femtoflow_mc_particle::MCType::kRecon) { + mHistogramRegistry->fill(histFolder + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST(o2::aod::femtoflowparticle::TempFitVarName[kParticleType]), part.pt(), part.tempFitVar()); + } + } + + template + void fillQADebug(T const& part, H const& histFolder) // o2-linter: disable=name/function-variable + { + using namespace o2::framework; + + // Histograms holding further debug information + if constexpr (kParticleType == o2::aod::femtoflowparticle::ParticleType::kTrack || kParticleType == o2::aod::femtoflowparticle::ParticleType::kV0Child || kParticleType == o2::aod::femtoflowparticle::ParticleType::kCascadeBachelor || kParticleType == o2::aod::femtoflowparticle::ParticleType::kMCTruthTrack) { + mHistogramRegistry->fill(histFolder + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("/hCharge"), part.sign()); + mHistogramRegistry->fill(histFolder + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("/hTPCfindable"), part.tpcNClsFindable()); + mHistogramRegistry->fill(histFolder + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("/hTPCfound"), part.tpcNClsFound()); + mHistogramRegistry->fill(histFolder + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("/hTPCcrossedOverFindable"), part.tpcCrossedRowsOverFindableCls()); + mHistogramRegistry->fill(histFolder + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("/hTPCcrossedRows"), part.tpcNClsCrossedRows()); + mHistogramRegistry->fill(histFolder + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("/hTPCfindableVsCrossed"), part.tpcNClsFindable(), part.tpcNClsCrossedRows()); + mHistogramRegistry->fill(histFolder + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("/hTPCshared"), part.tpcNClsShared()); + mHistogramRegistry->fill(histFolder + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("/hTPCfractionSharedCls"), part.tpcFractionSharedCls()); + mHistogramRegistry->fill(histFolder + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("/hITSclusters"), part.itsNCls()); + mHistogramRegistry->fill(histFolder + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("/hITSclustersIB"), part.itsNClsInnerBarrel()); + mHistogramRegistry->fill(histFolder + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("/hDCAz"), part.pt(), part.dcaZ()); + mHistogramRegistry->fill(histFolder + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("/hDCA"), part.pt(), std::sqrt(std::pow(part.dcaXY(), 2.) + std::pow(part.dcaZ(), 2.))); + mHistogramRegistry->fill(histFolder + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("/hTPCdEdX"), part.p(), part.tpcSignal()); + mHistogramRegistry->fill(histFolder + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("/hTOFBeta"), part.p(), part.beta()); + mHistogramRegistry->fill(histFolder + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("/nSigmaTPC_el"), part.p(), part.tpcNSigmaEl()); + mHistogramRegistry->fill(histFolder + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("/nSigmaTPC_pi"), part.p(), part.tpcNSigmaPi()); + mHistogramRegistry->fill(histFolder + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("/nSigmaTPC_K"), part.p(), part.tpcNSigmaKa()); + mHistogramRegistry->fill(histFolder + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("/nSigmaTPC_p"), part.p(), part.tpcNSigmaPr()); + mHistogramRegistry->fill(histFolder + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("/nSigmaTPC_d"), part.p(), part.tpcNSigmaDe()); + mHistogramRegistry->fill(histFolder + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("/nSigmaTOF_el"), part.p(), part.tofNSigmaEl()); + mHistogramRegistry->fill(histFolder + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("/nSigmaTOF_pi"), part.p(), part.tofNSigmaPi()); + mHistogramRegistry->fill(histFolder + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("/nSigmaTOF_K"), part.p(), part.tofNSigmaKa()); + mHistogramRegistry->fill(histFolder + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("/nSigmaTOF_p"), part.p(), part.tofNSigmaPr()); + mHistogramRegistry->fill(histFolder + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("/nSigmaTOF_d"), part.p(), part.tofNSigmaDe()); + mHistogramRegistry->fill(histFolder + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("/nSigmaComb_el"), part.p(), std::sqrt(part.tpcNSigmaEl() * part.tpcNSigmaEl() + part.tofNSigmaEl() * part.tofNSigmaEl())); + mHistogramRegistry->fill(histFolder + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("/nSigmaComb_pi"), part.p(), std::sqrt(part.tpcNSigmaPi() * part.tpcNSigmaPi() + part.tofNSigmaPi() * part.tofNSigmaPi())); + mHistogramRegistry->fill(histFolder + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("/nSigmaComb_K"), part.p(), std::sqrt(part.tpcNSigmaKa() * part.tpcNSigmaKa() + part.tofNSigmaKa() * part.tofNSigmaKa())); + mHistogramRegistry->fill(histFolder + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("/nSigmaComb_p"), part.p(), std::sqrt(part.tpcNSigmaPr() * part.tpcNSigmaPr() + part.tofNSigmaPr() * part.tofNSigmaPr())); + mHistogramRegistry->fill(histFolder + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("/nSigmaComb_d"), part.p(), std::sqrt(part.tpcNSigmaDe() * part.tpcNSigmaDe() + part.tofNSigmaDe() * part.tofNSigmaDe())); + } else if constexpr (kParticleType == o2::aod::femtoflowparticle::ParticleType::kV0) { + mHistogramRegistry->fill(histFolder + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("/hDaughDCA"), part.daughDCA()); + mHistogramRegistry->fill(histFolder + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("/hTransRadius"), part.transRadius()); + mHistogramRegistry->fill(histFolder + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("/hDecayVtxX"), part.decayVtxX()); + mHistogramRegistry->fill(histFolder + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("/hDecayVtxY"), part.decayVtxY()); + mHistogramRegistry->fill(histFolder + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("/hDecayVtxZ"), part.decayVtxZ()); + mHistogramRegistry->fill(histFolder + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("/hInvMassLambda"), part.mLambda()); + mHistogramRegistry->fill(histFolder + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("/hInvMassLambdaVsPt"), part.pt(), part.mLambda()); + mHistogramRegistry->fill(histFolder + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("/hInvMassAntiLambda"), part.mAntiLambda()); + mHistogramRegistry->fill(histFolder + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("/hInvMassAntiLambdaVsPt"), part.pt(), part.mAntiLambda()); + mHistogramRegistry->fill(histFolder + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("/hInvMassLambdaAntiLambda"), part.mLambda(), part.mAntiLambda()); + } else if constexpr (kParticleType == o2::aod::femtoflowparticle::ParticleType::kCascade) { + mHistogramRegistry->fill(histFolder + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("/hDaughDCA"), part.daughDCA()); + mHistogramRegistry->fill(histFolder + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("/hTransRadius"), part.transRadius()); + mHistogramRegistry->fill(histFolder + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("/hDecayVtxX"), part.decayVtxX()); + mHistogramRegistry->fill(histFolder + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("/hDecayVtxY"), part.decayVtxY()); + mHistogramRegistry->fill(histFolder + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("/hDecayVtxZ"), part.decayVtxZ()); + mHistogramRegistry->fill(histFolder + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("/hInvMassXi"), part.mLambda()); + mHistogramRegistry->fill(histFolder + HIST(o2::aod::femtoflow_mc_particle::MCTypeName[mc]) + HIST("/hInvMassOmega"), part.mAntiLambda()); + } + } + + /// Filling specialized histograms for Monte Carlo truth + /// internal function called by init only in case of Monte Carlo truth + /// \tparam T Data type of the particle + /// \tparam TMC Data typ of the Monte Carlo Particle + /// \param part Particle + /// \param mctruthorigin Origin of the associated mc Truth particle + /// \param pdgcode PDG of the associated mc Truth particle associated to the reconstructed particle part + template + void fillQAMC(T const& part, int mctruthorigin, int pdgcode, H const& histFolder) // o2-linter: disable=name/function-variable + { + using namespace o2::framework; + + if (mHistogramRegistry) { + mHistogramRegistry->fill(histFolder + HIST("_MC/hPDG"), pdgcode); + mHistogramRegistry->fill(histFolder + HIST("_MC/hOrigin_MC"), mctruthorigin); + + if (std::abs(pdgcode) == mPDG) { // fill this histogramm only for TRUE protons (independently of their origin) for the track purity estimation + mHistogramRegistry->fill(histFolder + HIST("_MC/hPt_ReconNoFake"), part.pt()); + } + + if constexpr (kParticleType == o2::aod::femtoflowparticle::ParticleType::kTrack || kParticleType == o2::aod::femtoflowparticle::ParticleType::kV0Child || kParticleType == o2::aod::femtoflowparticle::ParticleType::kCascadeBachelor || kParticleType == o2::aod::femtoflowparticle::ParticleType::kMCTruthTrack) { + if constexpr (isDebug) { + switch (mctruthorigin) { + case (o2::aod::femtoflow_mc_particle::kPrimary): + // mHistogramRegistry->fill(histFolder + HIST("_MC/Debug/hPDGmother_Primary"), part.fDMCParticle().motherPDG()); + mHistogramRegistry->fill(histFolder + HIST("_MC/hDCAxy_Primary"), part.pt(), part.tempFitVar()); + break; + case (o2::aod::femtoflow_mc_particle::kDaughter): + // mHistogramRegistry->fill(histFolder + HIST("_MC/Debug/hPDGmother_Daughter"), part.fDMCParticle().motherPDG()); + mHistogramRegistry->fill(histFolder + HIST("_MC/hDCAxy_Daughter"), part.pt(), part.tempFitVar()); + break; + case (o2::aod::femtoflow_mc_particle::kMaterial): + // mHistogramRegistry->fill(histFolder + HIST("_MC/Debug/hPDGmother_Material"), part.fDMCParticle().motherPDG()); + mHistogramRegistry->fill(histFolder + HIST("_MC/hDCAxy_Material"), part.pt(), part.tempFitVar()); + break; + case (o2::aod::femtoflow_mc_particle::kWrongCollision): + // mHistogramRegistry->fill(histFolder + HIST("_MC/Debug/hPDGmother_WrongCollision"), part.fDMCParticle().motherPDG()); + mHistogramRegistry->fill(histFolder + HIST("_MC/hDCAxy_WrongCollision"), part.pt(), part.tempFitVar()); + break; + case (o2::aod::femtoflow_mc_particle::kFake): + // mHistogramRegistry->fill(histFolder + HIST("_MC/Debug/hPDGmother_Fake"), part.fDMCParticle().motherPDG()); + mHistogramRegistry->fill(histFolder + HIST("_MC/hDCAxy_Fake"), part.pt(), part.tempFitVar()); + break; + case (o2::aod::femtoflow_mc_particle::kDaughterLambda): + // mHistogramRegistry->fill(histFolder + HIST("_MC/Debug/hPDGmother_DaughterLambda"), part.fDMCParticle().motherPDG()); + mHistogramRegistry->fill(histFolder + HIST("_MC/hDCAxy_DaughterLambda"), part.pt(), part.tempFitVar()); + break; + case (o2::aod::femtoflow_mc_particle::kDaughterSigmaplus): + // mHistogramRegistry->fill(histFolder + HIST("_MC/Debug/hPDGmother_DaughterSigmaplus"), part.fDMCParticle().motherPDG()); + mHistogramRegistry->fill(histFolder + HIST("_MC/hDCAxy_DaughterSigmaplus"), part.pt(), part.tempFitVar()); + break; + case (o2::aod::femtoflow_mc_particle::kElse): + // mHistogramRegistry->fill(histFolder + HIST("_MC/Debug/hPDGmother_Else"), part.fDMCParticle().motherPDG()); + mHistogramRegistry->fill(histFolder + HIST("_MC/hDCAxy_Else"), part.pt(), part.tempFitVar()); + break; + default: + LOGF(info, "femtodreamparticleMC: not known value for ParticleOriginMCTruth --- %d - please check. Quitting!", mctruthorigin); + mHistogramRegistry->fill(histFolder + HIST("_MC/hDCAxy_NoMCTruthOrigin"), part.pt(), part.tempFitVar()); + } + } else { + switch (mctruthorigin) { + case (o2::aod::femtoflow_mc_particle::kPrimary): + mHistogramRegistry->fill(histFolder + HIST("_MC/hDCAxy_Primary"), part.pt(), part.tempFitVar()); + break; + case (o2::aod::femtoflow_mc_particle::kDaughter): + mHistogramRegistry->fill(histFolder + HIST("_MC/hDCAxy_Daughter"), part.pt(), part.tempFitVar()); + break; + case (o2::aod::femtoflow_mc_particle::kMaterial): + mHistogramRegistry->fill(histFolder + HIST("_MC/hDCAxy_Material"), part.pt(), part.tempFitVar()); + break; + case (o2::aod::femtoflow_mc_particle::kWrongCollision): + mHistogramRegistry->fill(histFolder + HIST("_MC/hDCAxy_WrongCollision"), part.pt(), part.tempFitVar()); + break; + case (o2::aod::femtoflow_mc_particle::kFake): + mHistogramRegistry->fill(histFolder + HIST("_MC/hDCAxy_Fake"), part.pt(), part.tempFitVar()); + break; + case (o2::aod::femtoflow_mc_particle::kDaughterLambda): + mHistogramRegistry->fill(histFolder + HIST("_MC/hDCAxy_DaughterLambda"), part.pt(), part.tempFitVar()); + break; + case (o2::aod::femtoflow_mc_particle::kDaughterSigmaplus): + mHistogramRegistry->fill(histFolder + HIST("_MC/hDCAxy_DaughterSigmaplus"), part.pt(), part.tempFitVar()); + break; + case (o2::aod::femtoflow_mc_particle::kElse): + mHistogramRegistry->fill(histFolder + HIST("_MC/hDCAxy_Else"), part.pt(), part.tempFitVar()); + break; + + default: + LOGF(info, "femtodreamparticleMC: not known value for ParticleOriginMCTruth --- %d - please check. Quitting!", mctruthorigin); + mHistogramRegistry->fill(histFolder + HIST("_MC/hDCAxy_NoMCTruthOrigin"), part.pt(), part.tempFitVar()); + } + } + } else if constexpr (kParticleType == o2::aod::femtoflowparticle::ParticleType::kV0) { + /// V0 histograms + } else if constexpr (kParticleType == o2::aod::femtoflowparticle::ParticleType::kCascade) { + /// Cascade histograms + } else if constexpr (kParticleType == o2::aod::femtoflowparticle::ParticleType::kPhi) { + // Phi histograms + } else if constexpr (kParticleType == o2::aod::femtoflowparticle::ParticleType::kD0) { + // D0/D0bar histograms + } else { + LOG(fatal) << "FemtoFlowParticleHisto: Histogramming for requested object not defined - quitting!"; + } + } + } + + template + void fillQAMCMisIden(T const& part, int pdgcode, int confPDG, H const& histFolder) // o2-linter: disable=name/function-variable + { + using namespace o2::framework; + + if (mHistogramRegistry) { + if constexpr (kParticleType == o2::aod::femtoflowparticle::ParticleType::kTrack) { + if (confPDG == mConfPDGCodePart[0]) { + binPart = 0; + } else if (confPDG == mConfPDGCodePart[1]) { + binPart = 1; + } else if (confPDG == mConfPDGCodePart[2]) { + binPart = 2; + } else { + binPart = 3; + } + if (std::abs(pdgcode) == PDG_t::kPiPlus) { + mHistogramRegistry->fill(histFolder + HIST("_MC/hMisidentification"), + binPart, 0, part.pt()); + } else if (std::abs(pdgcode) == PDG_t::kKPlus) { + mHistogramRegistry->fill(histFolder + HIST("_MC/hMisidentification"), + binPart, 1, part.pt()); + } else if (std::abs(pdgcode) == PDG_t::kProton) { + mHistogramRegistry->fill(histFolder + HIST("_MC/hMisidentification"), + binPart, 2, part.pt()); + } else { + mHistogramRegistry->fill(histFolder + HIST("_MC/hMisidentification"), + binPart, 3, part.pt()); + } + } + } else { + LOG(fatal) << "FemtoFlowParticleHisto: Histogramming for requested object not defined - quitting!"; + } + } + + /// Templated function to fill particle histograms for data/ Monte Carlo reconstructed and Monte Carlo truth + /// Always calls fillQABase fill histogramms with data/ Monte Carlo reconstructed + /// In case of Monte Carlo, calls fillQABase with Monte Carlo truth info and specialized function fillQAMC for additional histogramms + /// \tparam T particle type + /// \tparam isMC fills the additional histograms for Monte Carlo truth + /// \param part particle for which the histograms should be filled + template + void fillQA(T const& part) + { + using namespace o2::framework; + + fillQABase(part, HIST(o2::aod::femtoflowparticle::ParticleTypeName[kParticleType]) + HIST(kFolderSuffix[kFolderSuffixType])); + } + + template + void fillQABase(T const& part, H const& histFolder) + { + using namespace o2::framework; + + std::string tempFitVarName; + if (mHistogramRegistry) { + fillQABase(part, histFolder); + if constexpr (isDebug) { + fillQADebug(part, histFolder); + } + if constexpr (isMC) { + if (part.has_fDMCParticle()) { + fillQABase(part.fDMCParticle(), histFolder); + fillQAMC(part, (part.fDMCParticle()).partOriginMCTruth(), (part.fDMCParticle()).pdgMCTruth(), histFolder); + } else { + mHistogramRegistry->fill(histFolder + HIST("_MC/hNoMCtruthCounter"), 0); + } + } + } + } + + /// Templated function to fill particle histograms for data/ Monte Carlo reconstructed and Monte Carlo truth + /// Always calls fillQABase fill histogramms with data/ Monte Carlo reconstructed + /// In case of Monte Carlo, calls fillQABase with Monte Carlo truth info and specialized function fillQAMC for additional histogramms + /// \tparam T particle type + /// \tparam isMC fills the additional histograms for Monte Carlo truth + /// \param part particle for which the histograms should be filled + template + void fillQAMisIden(T const& part, int confPDG) + { + using namespace o2::framework; + + fillQABaseMisiden(part, HIST(o2::aod::femtoflowparticle::ParticleTypeName[kParticleType]) + HIST(kFolderSuffix[kFolderSuffixType]), confPDG); + } + + template + void fillQABaseMisiden(T const& part, H const& histFolder, int confPDG) + { + using namespace o2::framework; + + std::string tempFitVarName; + if (mHistogramRegistry) { + if constexpr (isMC) { + if (part.has_fDMCParticle()) { + fillQAMCMisIden(part, (part.fDMCParticle()).pdgMCTruth(), confPDG, histFolder); + } + } + } + } + + private: + o2::framework::HistogramRegistry* mHistogramRegistry; ///< For QA output + static constexpr o2::aod::femtoflowparticle::ParticleType kParticleType = particleType; ///< Type of the particle under analysis // o2-linter: disable=name/constexpr-constant + static constexpr int kFolderSuffixType = suffixType; ///< Counter for the folder suffix specified below // o2-linter: disable=name/constexpr-constant + static constexpr std::string_view kFolderSuffix[5] = {"_debug", "_one", "_two", "_pos", "_neg"}; ///< Suffix for the folder name in case of analyses of pairs of the same kind (T-T, V-V, C-C) // o2-linter: disable=name/constexpr-constant + int mConfPDGCodePart[4] = {211, 321, 2212, 9999}; ///< PDG code as per analysis + int mPDG = 0; ///< PDG code of the selected particle + int binPart = 0; +}; +} // namespace o2::analysis::femto_flow + +#endif // PWGCF_FEMTO_CORE_FEMTOFLOWPARTICLEHISTO_H_ diff --git a/PWGCF/Femto/Core/FemtoFlowSelection.h b/PWGCF/Femto/Core/FemtoFlowSelection.h new file mode 100644 index 00000000000..0d2885cd79c --- /dev/null +++ b/PWGCF/Femto/Core/FemtoFlowSelection.h @@ -0,0 +1,133 @@ +// 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 FemtoFlowSelection.h +/// \brief FemtoFlowSelection - small generic class to do selections +/// \author Andi Mathis, TU München, andreas.mathis@ph.tum.de +/// \author Zuzanna Chochulska, WUT Warsaw & CTU Prague, zchochul@cern.ch + +#ifndef PWGCF_FEMTO_CORE_FEMTOFLOWSELECTION_H_ +#define PWGCF_FEMTO_CORE_FEMTOFLOWSELECTION_H_ + +#include + +namespace o2::analysis::femto_flow +{ + +namespace femto_flow_selection +{ +/// Type of selection to be employed +enum SelectionType { kUpperLimit, ///< simple upper limit for the value, e.g. p_T < 1 GeV/c + kAbsUpperLimit, ///< upper limit of the absolute value, e.g. |eta| < 0.8 + kLowerLimit, ///< simple lower limit for the value, e.g. p_T > 0.2 GeV/c + kAbsLowerLimit, ///< lower limit of the absolute value, e.g. |DCA_xyz| > 0.05 cm + kEqual ///< values need to be equal, e.g. sign = 1 +}; + +} // namespace femto_flow_selection + +/// Simple class taking care of individual selections +/// \todo In principle all cuts that fulfill the getMinimalSelection are done implicitly and can be removed from the vector containing all cuts +/// \tparam selValDataType Data type used for the selection (float/int/bool/...) +/// \tparam selVariableDataType Data type used for the variable of the selection +template +class FemtoFlowSelection +{ + public: + /// Default constructor + FemtoFlowSelection(); + + /// Constructor + /// \param selVal Value used for the selection + /// \param selVar Variable used for the selection + /// \param selType Type of selection to be employed + FemtoFlowSelection(selValDataType selVal, selVariableDataType selVar, femto_flow_selection::SelectionType selType) + : mSelVal(selVal), + mSelVar(selVar), + mSelType(selType) + { + } + + /// Destructor + virtual ~FemtoFlowSelection() = default; + + /// Get the value used for the selection + /// \return Value used for the selection + selValDataType getSelectionValue() const { return mSelVal; } + + /// Get the variable used for the selection + /// \return variable used for the selection + selVariableDataType getSelectionVariable() const { return mSelVar; } + + /// Get the type of selection to be employed + /// \return Type of selection to be employed + femto_flow_selection::SelectionType getSelectionType() const { return mSelType; } + + /// Check whether the selection is fulfilled or not + /// \param observable Value of the variable to be checked + /// \return Whether the selection is fulfilled or not + bool isSelected(selValDataType observable) const + { + switch (mSelType) { + case (femto_flow_selection::SelectionType::kUpperLimit): + return (observable < mSelVal); + case (femto_flow_selection::SelectionType::kAbsUpperLimit): + return (std::abs(observable) < mSelVal); + break; + case (femto_flow_selection::SelectionType::kLowerLimit): + return (observable > mSelVal); + case (femto_flow_selection::SelectionType::kAbsLowerLimit): + return (std::abs(observable) > mSelVal); + break; + case (femto_flow_selection::SelectionType::kEqual): + /// \todo can the comparison be done a bit nicer? + return (std::abs(observable - mSelVal) < std::abs(mSelVal * 1e-6)); + break; + } + return false; + } + + /// Check whether the selection is fulfilled or not and put together the bit-wise container for the systematic variations + /// \tparam T Data type of the bit-wise container for the systematic variations + /// \param observable Value of the variable to be checked + /// \param cutContainer Bit-wise container for the systematic variations + /// \param counter Position in the bit-wise container for the systematic variations to be modified + template + void checkSelectionSetBit(selValDataType observable, T& cutContainer, size_t& counter) const + { + /// If the selection is fulfilled the bit at the specified position (counter) within the bit-wise container is set to 1 + if (isSelected(observable)) { + cutContainer |= 1UL << counter; + } + ++counter; + } + + template + void checkSelectionSetBitPID(selValDataType observable, T& cutContainer) const + { + /// If the selection is fulfilled the bit at the specified position (counter) within the bit-wise container is set to 1 + if (isSelected(observable)) { + cutContainer |= 1UL; + } else { + cutContainer |= 0UL; + } + cutContainer <<= 1; + } + + private: + selValDataType mSelVal{0.f}; ///< Value used for the selection + selVariableDataType mSelVar; ///< Variable used for the selection + femto_flow_selection::SelectionType mSelType; ///< Type of selection employed +}; + +} // namespace o2::analysis::femto_flow + +#endif // PWGCF_FEMTO_CORE_FEMTOFLOWSELECTION_H_ diff --git a/PWGCF/Femto/Core/FemtoFlowTrackSelection.h b/PWGCF/Femto/Core/FemtoFlowTrackSelection.h new file mode 100644 index 00000000000..823a23f341b --- /dev/null +++ b/PWGCF/Femto/Core/FemtoFlowTrackSelection.h @@ -0,0 +1,613 @@ +// 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 FemtoFlowTrackSelection.h +/// \brief Definition of the FemtoFlowTrackSelection +/// \author Andi Mathis, TU München, andreas.mathis@ph.tum.de +/// \author Luca Barioglio, TU München, luca.barioglio@cern.ch +/// \author Zuzanna Chochulska, WUT Warsaw & CTU Prague, zchochul@cern.ch + +#ifndef PWGCF_FEMTO_CORE_FEMTOFLOWTRACKSELECTION_H_ +#define PWGCF_FEMTO_CORE_FEMTOFLOWTRACKSELECTION_H_ + +#include "PWGCF/Femto/Core/FemtoFlowObjectSelection.h" +#include "PWGCF/Femto/DataModel/FemtoDerived.h" + +#include "Common/Core/TrackSelection.h" +#include "Common/Core/TrackSelectionDefaults.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include "Framework/HistogramRegistry.h" +#include "ReconstructionDataFormats/PID.h" + +#include +#include +#include + +namespace o2::analysis::femto_flow +{ + +namespace femto_flow_track_selection +{ +/// The different selections this task is capable of doing +enum TrackSel { kSign, ///< Sign of the track + kpTMin, ///< Min. p_T (GeV/c) + kpTMax, ///< Max. p_T (GeV/c) + kEtaMax, ///< Max. |eta| + kTPCnClsMin, ///< Min. number of TPC clusters + kTPCfClsMin, ///< Min. fraction of crossed rows/findable TPC clusters + kTPCcRowsMin, ///< Min. number of crossed TPC rows + kTPCsClsMax, ///< Max. number of shared TPC clusters + kTPCfracsClsMax, ///< Max. number of fraction of shared TPC clusters + kITSnClsMin, ///< Min. number of ITS clusters + kITSnClsIbMin, ///< Min. number of ITS clusters in the inner barrel + kDCAxyMax, ///< Max. DCA_xy (cm) + kDCAzMax, ///< Max. DCA_z (cm) + kDCAMin, ///< Min. DCA_xyz (cm) + kPIDnSigmaMax ///< Max. |n_sigma| for PID +}; + +enum TrackContainerPosition { + kCuts, + kPID +}; /// Position in the full track cut container + +} // namespace femto_flow_track_selection + +/// \class FemtoFlowTrackCuts +/// \brief Cut class to contain and execute all cuts applied to tracks +class FemtoFlowTrackSelection : public FemtoFlowObjectSelection +{ + public: + FemtoFlowTrackSelection() : nRejectNotPropagatedTracks(false), + nPtMinSel(0), + nPtMaxSel(0), + nEtaSel(0), + nTPCnMinSel(0), + nTPCfMinSel(0), + nTPCcMinSel(0), + nTPCsMaxSel(0), + nITScMinSel(0), + nITScIbMinSel(0), + nDCAxyMaxSel(0), + nDCAzMaxSel(0), + nDCAMinSel(0), + nPIDnSigmaSel(0), + pTMin(9999999.), + pTMax(-9999999.), + etaMax(-9999999.), + nClsMin(9999999.), + fClsMin(9999999.), + cTPCMin(9999999.), + sTPCMax(-9999999.), + fracsTPCMax(-9999999.), + dcaXYMax(-9999999.), + dcaZMax(-9999999.), + dcaMin(9999999.), + nSigmaPIDMax(9999999.), + nSigmaPIDOffsetTPC(0.), + nSigmaPIDOffsetTOF(0.) {} + + /// Initializes histograms for the task + /// \tparam part Type of the particle for proper naming of the folders for QA + /// \tparam tracktype Type of track (track, positive child, negative child) for proper naming of the folders for QA + /// \tparam cutContainerType Data type of the bit-wise container for the selections + /// \param registry HistogramRegistry for QA output + template + void init(o2::framework::HistogramRegistry* registry); + + /// Passes the species to the task for which PID needs to be stored + /// \tparam T Data type of the configurable passed to the functions + /// \param pids Configurable with the species + template + void setPIDSpecies(T& pids) + { + std::vector tmpPids = pids; /// necessary due to some features of the configurable + for (const auto& pid : tmpPids) { + kPIDspecies.push_back(pid); + } + } + + /// Computes the n_sigma for a track and a particle-type hypothesis in the TPC + /// \tparam T Data type of the track + /// \param track Track for which PID is evaluated + /// \param pid Particle species for which PID is evaluated + /// \return Value of n_{sigma, TPC} + template + auto getNsigmaTPC(T const& track, o2::track::PID pid); + + /// Computes the n_sigma for a track and a particle-type hypothesis in the TOF + /// \tparam T Data type of the track + /// \param track Track for which PID is evaluated + /// \param pid Particle species for which PID is evaluated + /// \return Value of n_{sigma, TOF} + template + auto getNsigmaTOF(T const& track, o2::track::PID pid); + + /// Checks whether the most open combination of all selection criteria is fulfilled + /// \tparam T Data type of the track + /// \param track Track + /// \return Whether the most open combination of all selection criteria is fulfilled + template + bool isSelectedMinimal(T const& track); + + /// Obtain the bit-wise container for the selections + /// \todo For the moment, PID is separated from the other selections, hence instead of a single value an std::array of size two is returned + /// \tparam cutContainerType Data type of the bit-wise container for the selections + /// \tparam T Data type of the track + /// \param track Track + /// \return The bit-wise container for the selections, separately with all selection criteria, and the PID + template + std::array getCutContainer(T const& track); + + /// Some basic QA histograms + /// \tparam part Type of the particle for proper naming of the folders for QA + /// \tparam tracktype Type of track (track, positive child, negative child) for proper naming of the folders for QA + /// \tparam T Data type of the track + /// \param track Track + template + void fillQA(T const& track); + + /// Helper function to obtain the name of a given selection criterion for consistent naming of the configurables + /// \param iSel Track selection variable to be examined + /// \param prefix Additional prefix for the name of the configurable + /// \param suffix Additional suffix for the name of the configurable + static std::string getSelectionName(femto_flow_track_selection::TrackSel iSel, std::string_view prefix = "", std::string_view suffix = "") + { + std::string outString = static_cast(prefix); + outString += static_cast(kSelectionNames[iSel]); + outString += suffix; + return outString; + } + + /// Helper function to obtain the index of a given selection variable for consistent naming of the configurables + /// \param obs Track selection variable (together with prefix) got from file + /// \param prefix Additional prefix for the output of the configurable + static int findSelectionIndex(const std::string_view& obs, std::string_view prefix = "") + { + for (int index = 0; index < kNtrackSelection; index++) { + std::string comp = static_cast(prefix) + static_cast(kSelectionNames[index]); + std::string_view cmp{comp}; + if (obs.compare(cmp) == 0) + return index; + } + return -1; + } + + /// Helper function to obtain the type of a given selection variable for consistent naming of the configurables + /// \param iSel Track selection variable whose type is returned + static femto_flow_selection::SelectionType getSelectionType(femto_flow_track_selection::TrackSel iSel) + { + return kSelectionTypes[iSel]; + } + + /// Helper function to obtain the helper string of a given selection criterion for consistent description of the configurables + /// \param iSel Track selection variable to be examined + /// \param prefix Additional prefix for the output of the configurable + static std::string getSelectionHelper(femto_flow_track_selection::TrackSel iSel, std::string_view prefix = "") + { + std::string outString = static_cast(prefix); + outString += static_cast(kSelectionHelper[iSel]); + return outString; + } + + float getSigmaPIDMax() + { + return nSigmaPIDMax; + } + + void setRejectNotPropagatedTracks(bool reject) + { + nRejectNotPropagatedTracks = reject; + } + void setnSigmaPIDOffset(float offsetTPC, float offsetTOF) + { + nSigmaPIDOffsetTPC = offsetTPC; + nSigmaPIDOffsetTOF = offsetTOF; + } + + private: + bool nRejectNotPropagatedTracks; + int nPtMinSel; + int nPtMaxSel; + int nEtaSel; + int nTPCnMinSel; + int nTPCfMinSel; + int nTPCcMinSel; + int nTPCsMaxSel; + int nTPCsFracMaxSel; + int nITScMinSel; + int nITScIbMinSel; + int nDCAxyMaxSel; + int nDCAzMaxSel; + int nDCAMinSel; + int nPIDnSigmaSel; + float pTMin; + float pTMax; + float etaMax; + float nClsMin; + float fClsMin; + float cTPCMin; + float sTPCMax; + float fracsTPCMax; + float nITSclsMin; + float nITSclsIbMin; + float dcaXYMax; + float dcaZMax; + float dcaMin; + float nSigmaPIDMax; + float nSigmaPIDOffsetTPC; + float nSigmaPIDOffsetTOF; + std::vector kPIDspecies; ///< All the particle species for which the n_sigma values need to be stored + static constexpr int kNtrackSelection = 15; + static constexpr std::string_view kSelectionNames[kNtrackSelection] = {"Sign", + "PtMin", + "PtMax", + "EtaMax", + "TPCnClsMin", + "TPCfClsMin", + "TPCcRowsMin", + "TPCsClsMax", + "TPCfracsClsMax", + "ITSnClsMin", + "ITSnClsIbMin", + "DCAxyMax", + "DCAzMax", + "DCAMin", + "PIDnSigmaMax"}; ///< Name of the different selections + + static constexpr femto_flow_selection::SelectionType kSelectionTypes[kNtrackSelection]{femto_flow_selection::kEqual, + femto_flow_selection::kLowerLimit, + femto_flow_selection::kUpperLimit, + femto_flow_selection::kAbsUpperLimit, + femto_flow_selection::kLowerLimit, + femto_flow_selection::kLowerLimit, + femto_flow_selection::kLowerLimit, + femto_flow_selection::kUpperLimit, + femto_flow_selection::kUpperLimit, + femto_flow_selection::kLowerLimit, + femto_flow_selection::kLowerLimit, + femto_flow_selection::kAbsUpperLimit, + femto_flow_selection::kAbsUpperLimit, + femto_flow_selection::kAbsUpperLimit, + femto_flow_selection::kAbsUpperLimit}; ///< Map to match a variable with its type + + static constexpr std::string_view kSelectionHelper[kNtrackSelection] = {"Sign of the track", + "Minimal pT (GeV/c)", + "Maximal pT (GeV/c)", + "Maximal eta", + "Minimum number of TPC clusters", + "Minimum fraction of crossed rows/findable clusters", + "Minimum number of crossed TPC rows", + "Maximal number of shared TPC cluster", + "Maximal number of fraction of shared TPC cluster", + "Minimum number of ITS clusters", + "Minimum number of ITS clusters in the inner barrel", + "Maximal DCA_xy (cm)", + "Maximal DCA_z (cm)", + "Minimal DCA (cm)", + "Maximal PID (nSigma)"}; ///< Helper information for the different selections +}; // namespace femto_flow + +template +void FemtoFlowTrackSelection::init(o2::framework::HistogramRegistry* registry) +{ + using namespace o2::framework; + if (registry) { + mHistogramRegistry = registry; + std::string folderName = static_cast(o2::aod::femtoflowparticle::ParticleTypeName[part]) + "/" + static_cast(o2::aod::femtoflowparticle::TrackTypeName[tracktype]); + + /// check whether the number of selection exceeds the bitmap size + unsigned int nSelections = getNSelections() - getNSelections(femto_flow_track_selection::kPIDnSigmaMax); + const int multiple = 8; + if (nSelections > multiple * sizeof(cutContainerType)) { + LOG(fatal) << "FemtoFlowTrackCuts: Number of selections too large for your container - quitting!"; + } + + mHistogramRegistry->add((folderName + "/hPt").c_str(), "; #it{p}_{T} (GeV/#it{c}); Entries", kTH1F, {{240, 0, 6}}); + mHistogramRegistry->add((folderName + "/hEta").c_str(), "; #eta; Entries", kTH1F, {{200, -1.5, 1.5}}); + mHistogramRegistry->add((folderName + "/hPhi").c_str(), "; #phi; Entries", kTH1F, {{200, 0, o2::constants::math::TwoPI}}); + mHistogramRegistry->add((folderName + "/hTPCfindable").c_str(), "; TPC findable clusters; Entries", kTH1F, {{163, -0.5, 162.5}}); + mHistogramRegistry->add((folderName + "/hTPCfound").c_str(), "; TPC found clusters; Entries", kTH1F, {{163, -0.5, 162.5}}); + mHistogramRegistry->add((folderName + "/hTPCcrossedOverFindalbe").c_str(), "; TPC ratio findable; Entries", kTH1F, {{100, 0.5, 1.5}}); + mHistogramRegistry->add((folderName + "/hTPCcrossedRows").c_str(), "; TPC crossed rows; Entries", kTH1F, {{163, 0, 163}}); + mHistogramRegistry->add((folderName + "/hTPCfindableVsCrossed").c_str(), ";TPC findable clusters ; TPC crossed rows;", kTH2F, {{163, 0, 163}, {163, 0, 163}}); + mHistogramRegistry->add((folderName + "/hTPCshared").c_str(), "; TPC shared clusters; Entries", kTH1F, {{163, -0.5, 162.5}}); + mHistogramRegistry->add((folderName + "/hTPCfractionSharedCls").c_str(), "; TPC fraction of shared clusters; Entries", kTH1F, {{100, 0.0, 100.0}}); + mHistogramRegistry->add((folderName + "/hITSclusters").c_str(), "; ITS clusters; Entries", kTH1F, {{10, -0.5, 9.5}}); + mHistogramRegistry->add((folderName + "/hITSclustersIB").c_str(), "; ITS clusters in IB; Entries", kTH1F, {{10, -0.5, 9.5}}); + mHistogramRegistry->add((folderName + "/hDCAxy").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", kTH2F, {{100, 0, 10}, {500, -5, 5}}); + mHistogramRegistry->add((folderName + "/hDCAz").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{z} (cm)", kTH2F, {{100, 0, 10}, {500, -5, 5}}); + mHistogramRegistry->add((folderName + "/hDCA").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA (cm)", kTH2F, {{100, 0, 10}, {301, 0., 1.5}}); + mHistogramRegistry->add((folderName + "/hTPCdEdX").c_str(), "; #it{p} (GeV/#it{c}); TPC Signal", kTH2F, {{100, 0, 10}, {1000, 0, 1000}}); + mHistogramRegistry->add((folderName + "/nSigmaTPC_el").c_str(), "; #it{p} (GeV/#it{c}); n#sigma_{TPC}^{e}", kTH2F, {{100, 0, 10}, {200, -4.975, 5.025}}); + mHistogramRegistry->add((folderName + "/nSigmaTPC_pi").c_str(), "; #it{p} (GeV/#it{c}); n#sigma_{TPC}^{#pi}", kTH2F, {{100, 0, 10}, {200, -4.975, 5.025}}); + mHistogramRegistry->add((folderName + "/nSigmaTPC_K").c_str(), "; #it{p} (GeV/#it{c}); n#sigma_{TPC}^{K}", kTH2F, {{100, 0, 10}, {200, -4.975, 5.025}}); + mHistogramRegistry->add((folderName + "/nSigmaTPC_p").c_str(), "; #it{p} (GeV/#it{c}); n#sigma_{TPC}^{p}", kTH2F, {{100, 0, 10}, {200, -4.975, 5.025}}); + mHistogramRegistry->add((folderName + "/nSigmaTPC_d").c_str(), "; #it{p} (GeV/#it{c}); n#sigma_{TPC}^{d}", kTH2F, {{100, 0, 10}, {200, -4.975, 5.025}}); + mHistogramRegistry->add((folderName + "/nSigmaTOF_el").c_str(), "; #it{p} (GeV/#it{c}); n#sigma_{TOF}^{e}", kTH2F, {{100, 0, 10}, {200, -4.975, 5.025}}); + mHistogramRegistry->add((folderName + "/nSigmaTOF_pi").c_str(), "; #it{p} (GeV/#it{c}); n#sigma_{TOF}^{#pi}", kTH2F, {{100, 0, 10}, {200, -4.975, 5.025}}); + mHistogramRegistry->add((folderName + "/nSigmaTOF_K").c_str(), "; #it{p} (GeV/#it{c}); n#sigma_{TOF}^{K}", kTH2F, {{100, 0, 10}, {200, -4.975, 5.025}}); + mHistogramRegistry->add((folderName + "/nSigmaTOF_p").c_str(), "; #it{p} (GeV/#it{c}); n#sigma_{TOF}^{p}", kTH2F, {{100, 0, 10}, {200, -4.975, 5.025}}); + mHistogramRegistry->add((folderName + "/nSigmaTOF_d").c_str(), "; #it{p} (GeV/#it{c}); n#sigma_{TOF}^{d}", kTH2F, {{100, 0, 10}, {200, -4.975, 5.025}}); + mHistogramRegistry->add((folderName + "/nSigmaComb_el").c_str(), "; #it{p} (GeV/#it{c}); n#sigma_{comb}^{e}", kTH2F, {{100, 0, 10}, {200, -4.975, 5.025}}); + mHistogramRegistry->add((folderName + "/nSigmaComb_pi").c_str(), "; #it{p} (GeV/#it{c}); n#sigma_{comb}^{#pi}", kTH2F, {{100, 0, 10}, {200, -4.975, 5.025}}); + mHistogramRegistry->add((folderName + "/nSigmaComb_K").c_str(), "; #it{p} (GeV/#it{c}); n#sigma_{comb}^{K}", kTH2F, {{100, 0, 10}, {200, -4.975, 5.0255}}); + mHistogramRegistry->add((folderName + "/nSigmaComb_p").c_str(), "; #it{p} (GeV/#it{c}); n#sigma_{comb}^{p}", kTH2F, {{100, 0, 10}, {200, -4.975, 5.025}}); + mHistogramRegistry->add((folderName + "/nSigmaComb_d").c_str(), "; #it{p} (GeV/#it{c}); n#sigma_{comb}^{d}", kTH2F, {{100, 0, 10}, {200, -4.975, 5.025}}); + mHistogramRegistry->add((folderName + "/Chi2ITSTPCperCluster").c_str(), "; ITS_Chi2; TPC_Chi2", kTH2F, {{100, 0, 50}, {100, 0, 20}}); + mHistogramRegistry->add((folderName + "/RefitITSTPC").c_str(), "; ITS_Refit; TPC_Refit", kTH2F, {{2, 0, 2}, {2, 0, 2}}); + } + /// set cuts + nPtMinSel = getNSelections(femto_flow_track_selection::kpTMin); + nPtMaxSel = getNSelections(femto_flow_track_selection::kpTMax); + nEtaSel = getNSelections(femto_flow_track_selection::kEtaMax); + nTPCnMinSel = getNSelections(femto_flow_track_selection::kTPCnClsMin); + nTPCfMinSel = getNSelections(femto_flow_track_selection::kTPCfClsMin); + nTPCcMinSel = getNSelections(femto_flow_track_selection::kTPCcRowsMin); + nTPCsMaxSel = getNSelections(femto_flow_track_selection::kTPCsClsMax); + nTPCsFracMaxSel = getNSelections(femto_flow_track_selection::kTPCfracsClsMax); + nITScMinSel = getNSelections(femto_flow_track_selection::kITSnClsMin); + nITScIbMinSel = getNSelections(femto_flow_track_selection::kITSnClsIbMin); + nDCAxyMaxSel = getNSelections(femto_flow_track_selection::kDCAxyMax); + nDCAzMaxSel = getNSelections(femto_flow_track_selection::kDCAzMax); + nDCAMinSel = getNSelections(femto_flow_track_selection::kDCAMin); + nPIDnSigmaSel = getNSelections(femto_flow_track_selection::kPIDnSigmaMax); + + pTMin = getMinimalSelection(femto_flow_track_selection::kpTMin, femto_flow_selection::kLowerLimit); + pTMax = getMinimalSelection(femto_flow_track_selection::kpTMax, femto_flow_selection::kUpperLimit); + etaMax = getMinimalSelection(femto_flow_track_selection::kEtaMax, femto_flow_selection::kAbsUpperLimit); + nClsMin = getMinimalSelection(femto_flow_track_selection::kTPCnClsMin, femto_flow_selection::kLowerLimit); + fClsMin = getMinimalSelection(femto_flow_track_selection::kTPCfClsMin, femto_flow_selection::kLowerLimit); + cTPCMin = getMinimalSelection(femto_flow_track_selection::kTPCcRowsMin, femto_flow_selection::kLowerLimit); + sTPCMax = getMinimalSelection(femto_flow_track_selection::kTPCsClsMax, femto_flow_selection::kUpperLimit); + fracsTPCMax = getMinimalSelection(femto_flow_track_selection::kTPCfracsClsMax, femto_flow_selection::kUpperLimit); + nITSclsMin = getMinimalSelection(femto_flow_track_selection::kITSnClsMin, femto_flow_selection::kLowerLimit); + nITSclsIbMin = getMinimalSelection(femto_flow_track_selection::kITSnClsIbMin, femto_flow_selection::kLowerLimit); + dcaXYMax = getMinimalSelection(femto_flow_track_selection::kDCAxyMax, femto_flow_selection::kAbsUpperLimit); + dcaZMax = getMinimalSelection(femto_flow_track_selection::kDCAzMax, femto_flow_selection::kAbsUpperLimit); + dcaMin = getMinimalSelection(femto_flow_track_selection::kDCAMin, femto_flow_selection::kAbsLowerLimit); + nSigmaPIDMax = getMinimalSelection(femto_flow_track_selection::kPIDnSigmaMax, femto_flow_selection::kAbsUpperLimit); +} + +template +auto FemtoFlowTrackSelection::getNsigmaTPC(T const& track, o2::track::PID pid) +{ + return o2::aod::pidutils::tpcNSigma(pid, track); +} + +template +auto FemtoFlowTrackSelection::getNsigmaTOF(T const& track, o2::track::PID pid) +{ + /// skip tracks without TOF signal + // if (!track.hasTOF()) { + // return 999.f; + // } + + return o2::aod::pidutils::tofNSigma(pid, track); +} + +template +bool FemtoFlowTrackSelection::isSelectedMinimal(T const& track) +{ + const auto pT = track.pt(); + const auto eta = track.eta(); + const auto tpcNClsF = track.tpcNClsFound(); + const auto tpcRClsC = track.tpcCrossedRowsOverFindableCls(); + const auto tpcNClsC = track.tpcNClsCrossedRows(); + const auto tpcNClsS = track.tpcNClsShared(); + const auto tpcNClsFracS = track.tpcFractionSharedCls(); + const auto itsNCls = track.itsNCls(); + const auto itsNClsIB = track.itsNClsInnerBarrel(); + const auto dcaXY = track.dcaXY(); + const auto dcaZ = track.dcaZ(); + const auto dca = track.dcaXY(); // Accordingly to FemtoUniverse in AliPhysics as well as LF analysis, + // only dcaXY should be checked; NOT std::sqrt(pow(dcaXY, 2.) + pow(dcaZ, 2.)) + std::vector pidTPC, pidTOF; + const double dcaNan = 1e3; + + for (const auto& it : kPIDspecies) { + pidTPC.push_back(getNsigmaTPC(track, it)); + pidTOF.push_back(getNsigmaTOF(track, it)); + } + + if (nPtMinSel > 0 && pT < pTMin) { + return false; + } + if (nPtMaxSel > 0 && pT > pTMax) { + return false; + } + if (nEtaSel > 0 && std::abs(eta) > etaMax) { + return false; + } + if (nTPCnMinSel > 0 && tpcNClsF < nClsMin) { + return false; + } + if (nTPCfMinSel > 0 && tpcRClsC < fClsMin) { + return false; + } + if (nTPCcMinSel > 0 && tpcNClsC < cTPCMin) { + return false; + } + if (nTPCsMaxSel > 0 && tpcNClsS > sTPCMax) { + return false; + } + if (nTPCsFracMaxSel > 0 && tpcNClsFracS > fracsTPCMax) { + return false; + } + if (nITScMinSel > 0 && itsNCls < nITSclsMin) { + return false; + } + if (nITScIbMinSel > 0 && itsNClsIB < nITSclsIbMin) { + return false; + } + if (nDCAxyMaxSel > 0 && std::abs(dcaXY) > dcaXYMax) { + return false; + } + if (nDCAzMaxSel > 0 && std::abs(dcaZ) > dcaZMax) { + return false; + } + if (nDCAMinSel > 0 && std::abs(dca) < dcaMin) { + return false; + } + if (nRejectNotPropagatedTracks && std::abs(dca) > dcaNan) { + return false; + } + + if (nPIDnSigmaSel > 0) { + bool isFulfilled = false; + for (size_t i = 0; i < pidTPC.size(); ++i) { + auto pidTPCVal = pidTPC.at(i); + if (std::abs(pidTPCVal - nSigmaPIDOffsetTPC) < nSigmaPIDMax) { + isFulfilled = true; + } + } + if (!isFulfilled) { + return isFulfilled; + } + } + return true; +} + +template +std::array FemtoFlowTrackSelection::getCutContainer(T const& track) +{ + cutContainerType output = 0; + size_t counter = 0; + cutContainerType outputPID = 0; + const auto sign = track.sign(); + const auto pT = track.pt(); + const auto eta = track.eta(); + const auto tpcNClsF = track.tpcNClsFound(); + const auto tpcRClsC = track.tpcCrossedRowsOverFindableCls(); + const auto tpcNClsC = track.tpcNClsCrossedRows(); + const auto tpcNClsS = track.tpcNClsShared(); + const auto tpcNClsFracS = track.tpcFractionSharedCls(); + const auto itsNCls = track.itsNCls(); + const auto itsNClsIB = track.itsNClsInnerBarrel(); + const auto dcaXY = track.dcaXY(); + const auto dcaZ = track.dcaZ(); + const auto dca = std::sqrt(std::pow(dcaXY, 2.) + std::pow(dcaZ, 2.)); + + std::vector pidTPC, pidTOF; + for (const auto& it : kPIDspecies) { + pidTPC.push_back(getNsigmaTPC(track, it)); + pidTOF.push_back(getNsigmaTOF(track, it)); + } + + float observable = 0.; + for (const auto& sel : mSelections) { + const auto selVariable = sel.getSelectionVariable(); + if (selVariable == femto_flow_track_selection::kPIDnSigmaMax) { + /// PID needs to be handled a bit differently since we may need more than one species + for (size_t i = 0; i < kPIDspecies.size(); ++i) { + auto pidTPCVal = pidTPC.at(i) - nSigmaPIDOffsetTPC; + auto pidTOFVal = pidTOF.at(i) - nSigmaPIDOffsetTOF; + auto pidComb = std::sqrt(pidTPCVal * pidTPCVal + pidTOFVal * pidTOFVal); + sel.checkSelectionSetBitPID(pidTPCVal, outputPID); + sel.checkSelectionSetBitPID(pidComb, outputPID); + } + + } else { + /// for the rest it's all the same + switch (selVariable) { + case (femto_flow_track_selection::kSign): + observable = sign; + break; + case (femto_flow_track_selection::kpTMin): + case (femto_flow_track_selection::kpTMax): + observable = pT; + break; + case (femto_flow_track_selection::kEtaMax): + observable = eta; + break; + case (femto_flow_track_selection::kTPCnClsMin): + observable = tpcNClsF; + break; + case (femto_flow_track_selection::kTPCfClsMin): + observable = tpcRClsC; + break; + case (femto_flow_track_selection::kTPCcRowsMin): + observable = tpcNClsC; + break; + case (femto_flow_track_selection::kTPCsClsMax): + observable = tpcNClsS; + break; + case (femto_flow_track_selection::kTPCfracsClsMax): + observable = tpcNClsFracS; + break; + case (femto_flow_track_selection::kITSnClsMin): + observable = itsNCls; + break; + case (femto_flow_track_selection::kITSnClsIbMin): + observable = itsNClsIB; + break; + case (femto_flow_track_selection::kDCAxyMax): + observable = dcaXY; + break; + case (femto_flow_track_selection::kDCAzMax): + observable = dcaZ; + break; + case (femto_flow_track_selection::kDCAMin): + observable = dca; + break; + case (femto_flow_track_selection::kPIDnSigmaMax): + break; + } + sel.checkSelectionSetBit(observable, output, counter); + } + } + return {output, outputPID}; +} + +template +void FemtoFlowTrackSelection::fillQA(T const& track) +{ + if (mHistogramRegistry) { + mHistogramRegistry->fill(HIST(o2::aod::femtoflowparticle::ParticleTypeName[part]) + HIST("/") + HIST(o2::aod::femtoflowparticle::TrackTypeName[tracktype]) + HIST("/hPt"), track.pt()); + mHistogramRegistry->fill(HIST(o2::aod::femtoflowparticle::ParticleTypeName[part]) + HIST("/") + HIST(o2::aod::femtoflowparticle::TrackTypeName[tracktype]) + HIST("/hEta"), track.eta()); + mHistogramRegistry->fill(HIST(o2::aod::femtoflowparticle::ParticleTypeName[part]) + HIST("/") + HIST(o2::aod::femtoflowparticle::TrackTypeName[tracktype]) + HIST("/hPhi"), track.phi()); + mHistogramRegistry->fill(HIST(o2::aod::femtoflowparticle::ParticleTypeName[part]) + HIST("/") + HIST(o2::aod::femtoflowparticle::TrackTypeName[tracktype]) + HIST("/hTPCfindable"), track.tpcNClsFindable()); + mHistogramRegistry->fill(HIST(o2::aod::femtoflowparticle::ParticleTypeName[part]) + HIST("/") + HIST(o2::aod::femtoflowparticle::TrackTypeName[tracktype]) + HIST("/hTPCfound"), track.tpcNClsFound()); + mHistogramRegistry->fill(HIST(o2::aod::femtoflowparticle::ParticleTypeName[part]) + HIST("/") + HIST(o2::aod::femtoflowparticle::TrackTypeName[tracktype]) + HIST("/hTPCcrossedOverFindalbe"), track.tpcCrossedRowsOverFindableCls()); + mHistogramRegistry->fill(HIST(o2::aod::femtoflowparticle::ParticleTypeName[part]) + HIST("/") + HIST(o2::aod::femtoflowparticle::TrackTypeName[tracktype]) + HIST("/hTPCcrossedRows"), track.tpcNClsCrossedRows()); + mHistogramRegistry->fill(HIST(o2::aod::femtoflowparticle::ParticleTypeName[part]) + HIST("/") + HIST(o2::aod::femtoflowparticle::TrackTypeName[tracktype]) + HIST("/hTPCfindableVsCrossed"), track.tpcNClsFindable(), track.tpcNClsCrossedRows()); + mHistogramRegistry->fill(HIST(o2::aod::femtoflowparticle::ParticleTypeName[part]) + HIST("/") + HIST(o2::aod::femtoflowparticle::TrackTypeName[tracktype]) + HIST("/hTPCshared"), track.tpcNClsShared()); + mHistogramRegistry->fill(HIST(o2::aod::femtoflowparticle::ParticleTypeName[part]) + HIST("/") + HIST(o2::aod::femtoflowparticle::TrackTypeName[tracktype]) + HIST("/hTPCfractionSharedCls"), track.tpcFractionSharedCls()); + mHistogramRegistry->fill(HIST(o2::aod::femtoflowparticle::ParticleTypeName[part]) + HIST("/") + HIST(o2::aod::femtoflowparticle::TrackTypeName[tracktype]) + HIST("/hITSclusters"), track.itsNCls()); + mHistogramRegistry->fill(HIST(o2::aod::femtoflowparticle::ParticleTypeName[part]) + HIST("/") + HIST(o2::aod::femtoflowparticle::TrackTypeName[tracktype]) + HIST("/hITSclustersIB"), track.itsNClsInnerBarrel()); + mHistogramRegistry->fill(HIST(o2::aod::femtoflowparticle::ParticleTypeName[part]) + HIST("/") + HIST(o2::aod::femtoflowparticle::TrackTypeName[tracktype]) + HIST("/hDCAxy"), track.pt(), track.dcaXY()); + mHistogramRegistry->fill(HIST(o2::aod::femtoflowparticle::ParticleTypeName[part]) + HIST("/") + HIST(o2::aod::femtoflowparticle::TrackTypeName[tracktype]) + HIST("/hDCAz"), track.pt(), track.dcaZ()); + mHistogramRegistry->fill(HIST(o2::aod::femtoflowparticle::ParticleTypeName[part]) + HIST("/") + HIST(o2::aod::femtoflowparticle::TrackTypeName[tracktype]) + HIST("/hDCA"), track.pt(), std::sqrt(std::pow(track.dcaXY(), 2.) + std::pow(track.dcaZ(), 2.))); + mHistogramRegistry->fill(HIST(o2::aod::femtoflowparticle::ParticleTypeName[part]) + HIST("/") + HIST(o2::aod::femtoflowparticle::TrackTypeName[tracktype]) + HIST("/hTPCdEdX"), track.p(), track.tpcSignal()); + mHistogramRegistry->fill(HIST(o2::aod::femtoflowparticle::ParticleTypeName[part]) + HIST("/") + HIST(o2::aod::femtoflowparticle::TrackTypeName[tracktype]) + HIST("/nSigmaTPC_el"), track.p(), track.tpcNSigmaEl()); + mHistogramRegistry->fill(HIST(o2::aod::femtoflowparticle::ParticleTypeName[part]) + HIST("/") + HIST(o2::aod::femtoflowparticle::TrackTypeName[tracktype]) + HIST("/nSigmaTPC_pi"), track.p(), track.tpcNSigmaPi()); + mHistogramRegistry->fill(HIST(o2::aod::femtoflowparticle::ParticleTypeName[part]) + HIST("/") + HIST(o2::aod::femtoflowparticle::TrackTypeName[tracktype]) + HIST("/nSigmaTPC_K"), track.p(), track.tpcNSigmaKa()); + mHistogramRegistry->fill(HIST(o2::aod::femtoflowparticle::ParticleTypeName[part]) + HIST("/") + HIST(o2::aod::femtoflowparticle::TrackTypeName[tracktype]) + HIST("/nSigmaTPC_p"), track.p(), track.tpcNSigmaPr()); + mHistogramRegistry->fill(HIST(o2::aod::femtoflowparticle::ParticleTypeName[part]) + HIST("/") + HIST(o2::aod::femtoflowparticle::TrackTypeName[tracktype]) + HIST("/nSigmaTPC_d"), track.p(), track.tpcNSigmaDe()); + mHistogramRegistry->fill(HIST(o2::aod::femtoflowparticle::ParticleTypeName[part]) + HIST("/") + HIST(o2::aod::femtoflowparticle::TrackTypeName[tracktype]) + HIST("/nSigmaTOF_el"), track.p(), track.tofNSigmaEl()); + mHistogramRegistry->fill(HIST(o2::aod::femtoflowparticle::ParticleTypeName[part]) + HIST("/") + HIST(o2::aod::femtoflowparticle::TrackTypeName[tracktype]) + HIST("/nSigmaTOF_pi"), track.p(), track.tofNSigmaPi()); + mHistogramRegistry->fill(HIST(o2::aod::femtoflowparticle::ParticleTypeName[part]) + HIST("/") + HIST(o2::aod::femtoflowparticle::TrackTypeName[tracktype]) + HIST("/nSigmaTOF_K"), track.p(), track.tofNSigmaKa()); + mHistogramRegistry->fill(HIST(o2::aod::femtoflowparticle::ParticleTypeName[part]) + HIST("/") + HIST(o2::aod::femtoflowparticle::TrackTypeName[tracktype]) + HIST("/nSigmaTOF_p"), track.p(), track.tofNSigmaPr()); + mHistogramRegistry->fill(HIST(o2::aod::femtoflowparticle::ParticleTypeName[part]) + HIST("/") + HIST(o2::aod::femtoflowparticle::TrackTypeName[tracktype]) + HIST("/nSigmaTOF_d"), track.p(), track.tofNSigmaDe()); + mHistogramRegistry->fill(HIST(o2::aod::femtoflowparticle::ParticleTypeName[part]) + HIST("/") + HIST(o2::aod::femtoflowparticle::TrackTypeName[tracktype]) + HIST("/nSigmaComb_el"), track.p(), std::sqrt(track.tpcNSigmaEl() * track.tpcNSigmaEl() + track.tofNSigmaEl() * track.tofNSigmaEl())); + mHistogramRegistry->fill(HIST(o2::aod::femtoflowparticle::ParticleTypeName[part]) + HIST("/") + HIST(o2::aod::femtoflowparticle::TrackTypeName[tracktype]) + HIST("/nSigmaComb_pi"), track.p(), std::sqrt(track.tpcNSigmaPi() * track.tpcNSigmaPi() + track.tofNSigmaPi() * track.tofNSigmaPi())); + mHistogramRegistry->fill(HIST(o2::aod::femtoflowparticle::ParticleTypeName[part]) + HIST("/") + HIST(o2::aod::femtoflowparticle::TrackTypeName[tracktype]) + HIST("/nSigmaComb_K"), track.p(), std::sqrt(track.tpcNSigmaKa() * track.tpcNSigmaKa() + track.tofNSigmaKa() * track.tofNSigmaKa())); + mHistogramRegistry->fill(HIST(o2::aod::femtoflowparticle::ParticleTypeName[part]) + HIST("/") + HIST(o2::aod::femtoflowparticle::TrackTypeName[tracktype]) + HIST("/nSigmaComb_p"), track.p(), std::sqrt(track.tpcNSigmaPr() * track.tpcNSigmaPr() + track.tofNSigmaPr() * track.tofNSigmaPr())); + mHistogramRegistry->fill(HIST(o2::aod::femtoflowparticle::ParticleTypeName[part]) + HIST("/") + HIST(o2::aod::femtoflowparticle::TrackTypeName[tracktype]) + HIST("/nSigmaComb_d"), track.p(), std::sqrt(track.tpcNSigmaDe() * track.tpcNSigmaDe() + track.tofNSigmaDe() * track.tofNSigmaDe())); + mHistogramRegistry->fill(HIST(o2::aod::femtoflowparticle::ParticleTypeName[part]) + HIST("/") + HIST(o2::aod::femtoflowparticle::TrackTypeName[tracktype]) + HIST("/Chi2ITSTPCperCluster"), track.itsChi2NCl(), track.tpcChi2NCl()); + mHistogramRegistry->fill(HIST(o2::aod::femtoflowparticle::ParticleTypeName[part]) + HIST("/") + HIST(o2::aod::femtoflowparticle::TrackTypeName[tracktype]) + HIST("/RefitITSTPC"), track.hasITS(), track.hasTPC()); + } +} + +} // namespace o2::analysis::femto_flow + +#endif // PWGCF_FEMTO_CORE_FEMTOFLOWTRACKSELECTION_H_ diff --git a/PWGCF/Femto/Core/femtoUtils.h b/PWGCF/Femto/Core/femtoUtils.h new file mode 100644 index 00000000000..d3903ec1985 --- /dev/null +++ b/PWGCF/Femto/Core/femtoUtils.h @@ -0,0 +1,133 @@ +// 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 femtoUtils.h +/// \brief Utilities for the FemtoFlow framework +/// \author Luca Barioglio, TU München, luca.barioglio@cern.ch +/// \author Zuzanna Chochulska, WUT Warsaw & CTU Prague, zchochul@cern.ch + +#ifndef PWGCF_FEMTO_CORE_FEMTOUTILS_H_ +#define PWGCF_FEMTO_CORE_FEMTOUTILS_H_ + +#include "PWGCF/Femto/DataModel/FemtoDerived.h" + +#include "Framework/ASoAHelpers.h" + +#include +#include +#include + +namespace o2::analysis::femto_flow +{ + +enum KDetector { kTPC, + kTPCTOF, + kNdetectors }; + +/// internal function that returns the kPIDselection element corresponding to a +/// specifica n-sigma value \param nSigma number of sigmas for PID +/// \param vNsigma vector with the number of sigmas of interest +/// \return kPIDselection corresponding to n-sigma +int getPIDselection(float nSigma, std::vector vNsigma) +{ + std::sort(vNsigma.begin(), vNsigma.end(), std::greater<>()); + auto it = std::find(vNsigma.begin(), vNsigma.end(), nSigma); + if (it == vNsigma.end()) { + LOG(warn) << "Invalid value of nSigma: " << nSigma << ". Return the largest value of the vector: " << *it; + } + return std::distance(vNsigma.begin(), it); +} + +/// function that checks whether the PID selection specified in the vectors is +/// fulfilled +/// \param pidcut Bit-wise container for the PID +/// \param vSpecies number (ID) of the species of interest (as returned by the CutCulator), e.g. 0 / 1 / 2, usually 0 if only one particle species in the skimmed data +/// \param nSpecies number of available selected species (output from cutculator), i.e. how many particle types were saved in the skimmed data +/// \param nSigma Nsigma selection for PID (e.g. 3, for NsigmaTPC < 3 or NsigmaTPCTOF < 3) +/// \param vNsigma vector with available n-sigma selections for PID (to check if chosen nSigma value is avialable + size to get the bit number) +/// \param KDetector enum corresponding to the PID technique +/// \return Whether the PID selection specified in the vectors is fulfilled +bool isPIDSelected(aod::femtoflowparticle::CutContainerType pidcut, + int vSpecies, + int nSpecies, + float nSigma, + std::vector vNsigma, + KDetector iDet) +{ + int iNsigma = getPIDselection(nSigma, vNsigma); + int nDet = static_cast(KDetector::kNdetectors); + int bitToCheck = 1 + (vNsigma.size() - (iNsigma + 1)) * nDet * nSpecies + (nSpecies - (vSpecies + 1)) * nSpecies + (nDet - 1 - iDet); + return ((pidcut >> (bitToCheck)) & 1) == 1; +}; + +/// function that checks whether the PID selection specified in the vectors is fulfilled, depending on the momentum TPC or TPC+TOF PID is conducted +/// \param pidcut Bit-wise container for the PID +/// \param momentum Momentum of the track +/// \param pidThresh Momentum threshold that separates between TPC and TPC+TOF PID +/// \param vSpecies number (ID) of the species of interest (as returned by the CutCulator), e.g. 0 / 1 / 2, usually 0 if only one particle specie in the skimmed data +/// \param nSpecies number of available selected species (output from cutculator), i.e. how many particle types were saved in the skimmed data +/// \param vNsigma vector with available n-sigma selections for PID +/// \param nSigmaTPC Number of TPC sigmas for selection +/// \param nSigmaTPCTOF Number of TPC+TOF sigmas for selection (circular selection) +/// \return Whether the PID selection is fulfilled +bool isFullPIDSelected(aod::femtoflowparticle::CutContainerType const& pidCut, + float momentum, + float pidThresh, + int vSpecies, + int nSpecies, + std::vector vNsigma, + float nSigmaTPC, + float nSigmaTPCTOF) +{ + bool pidSelection = true; + if (momentum < pidThresh) { + /// TPC PID only + pidSelection = isPIDSelected(pidCut, vSpecies, nSpecies, nSigmaTPC, vNsigma, KDetector::kTPC); + } else { + /// TPC + TOF PID + pidSelection = isPIDSelected(pidCut, vSpecies, nSpecies, nSigmaTPCTOF, vNsigma, KDetector::kTPCTOF); + } + return pidSelection; +}; + +int checkDaughterType(o2::aod::femtoflowparticle::ParticleType partType, int motherPDG) +{ + int partOrigin = 0; + if (partType == o2::aod::femtoflowparticle::ParticleType::kTrack) { + + switch (std::abs(motherPDG)) { + case 3122: + partOrigin = aod::femtoflow_mc_particle::ParticleOriginMCTruth::kDaughterLambda; + break; + case 3222: + partOrigin = aod::femtoflow_mc_particle::ParticleOriginMCTruth::kDaughterSigmaplus; + break; + default: + partOrigin = aod::femtoflow_mc_particle::ParticleOriginMCTruth::kDaughter; + } // switch + + } else if (partType == o2::aod::femtoflowparticle::ParticleType::kV0) { + partOrigin = aod::femtoflow_mc_particle::ParticleOriginMCTruth::kDaughter; + + } else if (partType == o2::aod::femtoflowparticle::ParticleType::kV0Child) { + partOrigin = aod::femtoflow_mc_particle::ParticleOriginMCTruth::kDaughter; + + } else if (partType == o2::aod::femtoflowparticle::ParticleType::kCascade) { + partOrigin = aod::femtoflow_mc_particle::ParticleOriginMCTruth::kDaughter; + + } else if (partType == o2::aod::femtoflowparticle::ParticleType::kCascadeBachelor) { + partOrigin = aod::femtoflow_mc_particle::ParticleOriginMCTruth::kDaughter; + } + return partOrigin; +}; + +} // namespace o2::analysis::femto_flow +#endif // PWGCF_FEMTO_CORE_FEMTOUTILS_H_ diff --git a/PWGCF/Femto/DataModel/FemtoDerived.h b/PWGCF/Femto/DataModel/FemtoDerived.h new file mode 100644 index 00000000000..6736210f55e --- /dev/null +++ b/PWGCF/Femto/DataModel/FemtoDerived.h @@ -0,0 +1,324 @@ +// 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 FemtoDerived.h +/// \brief Declaration of FemtoFlow tables +/// \author Zuzanna Chochulska, WUT Warsaw & CTU Prague, zchochul@cern.ch + +#ifndef PWGCF_FEMTO_DATAMODEL_FEMTODERIVED_H_ +#define PWGCF_FEMTO_DATAMODEL_FEMTODERIVED_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/DataTypes.h" +#include "Framework/Expressions.h" +#include "MathUtils/Utils.h" + +#include + +namespace o2::aod +{ +/// FemtoFlowCollision +namespace femtoflowcollision +{ +DECLARE_SOA_COLUMN(MultV0M, multV0M, float); //! V0M multiplicity +DECLARE_SOA_COLUMN(MultNtr, multNtr, int); //! multiplicity of charged tracks as defined in the producer +DECLARE_SOA_COLUMN(Sphericity, sphericity, float); //! Sphericity of the event +DECLARE_SOA_COLUMN(QnBin, qnBin, int); //! Bin of qn-vector of the event +DECLARE_SOA_COLUMN(MagField, magField, float); //! Magnetic field of the event +DECLARE_SOA_COLUMN(InteractionRate, interactionRate, float); //! Interaction rate +DECLARE_SOA_COLUMN(Occupancy, occupancy, int); //! TPC occupancy + +} // namespace femtoflowcollision + +DECLARE_SOA_TABLE(FDCollisions, "AOD", "FDCOLLISION", + o2::soa::Index<>, + o2::aod::collision::PosZ, + femtoflowcollision::MultV0M, + femtoflowcollision::MultNtr, + femtoflowcollision::Sphericity, + femtoflowcollision::QnBin, + femtoflowcollision::MagField); +using FDCollision = FDCollisions::iterator; + +DECLARE_SOA_TABLE(FDExtCollisions, "AOD", "FDEXTCOLLISION", + femtoflowcollision::InteractionRate, + femtoflowcollision::Occupancy); +using FDExtCollision = FDExtCollisions::iterator; + +/// FemtoFlowTrack +namespace femtoflowparticle +{ +/// Distinuishes the different particle types +enum ParticleType { + kTrack, //! Track + kMCTruthTrack, //! MC Truth Track + kV0, //! V0 + kV0Child, //! Child track of a V0 + kCascade, //! Cascade + kCascadeBachelor, //! Bachelor track of a cascade + kPhi, //! Phi meson + kPhiChild, //! Child track of a Phi meson + kD0, //! D0/D0bar meson + kD0Child, //! Child track of a D0/D0bar meson + kNParticleTypes //! Number of particle types +}; + +static constexpr std::string_view ParticleTypeName[kNParticleTypes] = {"Tracks", "MCTruthTracks", "V0", "V0Child", "Cascade", "CascadeBachelor", "Phi", "PhiChild", "D0", "D0Child"}; //! Naming of the different particle types +static constexpr std::string_view TempFitVarName[kNParticleTypes] = {"/hDCAxy", "/hPDGvspT", "/hCPA", "/hDCAxy", "/hCPA", "/hDCAxy", "/hInvMass", "/hDCAxy", "/hInvMass", "/hDCAxy"}; + +using CutContainerType = uint32_t; //! Definition of the data type for the bit-wise container for the different selection criteria + +enum TrackType { + kNoChild, //! Not a V0 child + kPosChild, //! Positive V0 child + kNegChild, //! Negative V0 child + kBachelor, //! Cascade bachelor + kNTrackTypes //! Number of child types +}; + +static constexpr std::string_view TrackTypeName[kNTrackTypes] = {"Trk", "Pos", "Neg", "Bach"}; //! Naming of the different particle types + +DECLARE_SOA_INDEX_COLUMN(FDCollision, fDCollision); +DECLARE_SOA_COLUMN(Pt, pt, float); //! p_T (GeV/c) +DECLARE_SOA_COLUMN(Eta, eta, float); //! Eta +DECLARE_SOA_COLUMN(Phi, phi, float); //! Phi +DECLARE_SOA_COLUMN(PartType, partType, uint8_t); //! Type of the particle, according to femtoflowparticle::ParticleType +DECLARE_SOA_COLUMN(Cut, cut, CutContainerType); //! Bit-wise container for the different selection criteria +DECLARE_SOA_COLUMN(PidCut, pidCut, CutContainerType); //! Bit-wise container for the different PID selection criteria \todo since bit-masking cannot be done yet with filters we use a second field for the PID +DECLARE_SOA_COLUMN(TempFitVar, tempFitVar, float); //! Observable for the template fitting (Track: DCA_xy, V0: CPA) +DECLARE_SOA_SELF_ARRAY_INDEX_COLUMN(Children, children); //! Field for the track indices to remove auto-correlations +DECLARE_SOA_COLUMN(MLambda, mLambda, float); //! The invariant mass of V0 candidate, assuming lambda +DECLARE_SOA_COLUMN(MAntiLambda, mAntiLambda, float); //! The invariant mass of V0 candidate, assuming antilambda + +DECLARE_SOA_DYNAMIC_COLUMN(Theta, theta, //! Compute the theta of the track + [](float eta) -> float { + return 2.f * std::atan(std::exp(-eta)); + }); +DECLARE_SOA_DYNAMIC_COLUMN(Px, px, //! Compute the momentum in x in GeV/c + [](float pt, float phi) -> float { + return pt * std::cos(phi); + }); +DECLARE_SOA_DYNAMIC_COLUMN(Py, py, //! Compute the momentum in y in GeV/c + [](float pt, float phi) -> float { + return pt * std::sin(phi); + }); +DECLARE_SOA_DYNAMIC_COLUMN(Pz, pz, //! Compute the momentum in z in GeV/c + [](float pt, float eta) -> float { + return pt * std::sinh(eta); + }); +DECLARE_SOA_DYNAMIC_COLUMN(P, p, //! Compute the overall momentum in GeV/c + [](float pt, float eta) -> float { + return pt * std::cosh(eta); + }); +// debug variables +DECLARE_SOA_COLUMN(Sign, sign, int8_t); //! Sign of the track charge +DECLARE_SOA_COLUMN(TpcNClsFound, tpcNClsFound, uint8_t); //! Number of TPC clusters +DECLARE_SOA_COLUMN(TpcNClsCrossedRows, tpcNClsCrossedRows, uint8_t); //! Number of TPC crossed rows +DECLARE_SOA_COLUMN(TpcFractionSharedCls, tpcFractionSharedCls, float); //! Number of TPC crossed rows +DECLARE_SOA_COLUMN(ItsNCls, itsNCls, uint8_t); //! Number of ITS clusters +DECLARE_SOA_COLUMN(ItsNClsInnerBarrel, itsNClsInnerBarrel, uint8_t); //! Number of ITS clusters in the inner barrel //! TPC signal +DECLARE_SOA_DYNAMIC_COLUMN(TpcCrossedRowsOverFindableCls, tpcCrossedRowsOverFindableCls, //! Compute the number of crossed rows over findable TPC clusters + [](uint8_t tpcNClsFindable, uint8_t tpcNClsCrossedRows) -> float { + return (float)tpcNClsCrossedRows / (float)tpcNClsFindable; + }); +DECLARE_SOA_COLUMN(DaughDCA, daughDCA, float); //! DCA between daughters +DECLARE_SOA_COLUMN(TransRadius, transRadius, float); //! Transverse radius of the decay vertex +DECLARE_SOA_COLUMN(DecayVtxX, decayVtxX, float); //! X position of the decay vertex +DECLARE_SOA_COLUMN(DecayVtxY, decayVtxY, float); //! Y position of the decay vertex +DECLARE_SOA_COLUMN(DecayVtxZ, decayVtxZ, float); //! Z position of the decay vertex +DECLARE_SOA_COLUMN(MKaon, mKaon, float); //! The invariant mass of V0 candidate, assuming kaon + +} // namespace femtoflowparticle + +DECLARE_SOA_TABLE(FDParticles, "AOD", "FDPARTICLE", + o2::soa::Index<>, + femtoflowparticle::FDCollisionId, + femtoflowparticle::Pt, + femtoflowparticle::Eta, + femtoflowparticle::Phi, + femtoflowparticle::PartType, + femtoflowparticle::Cut, + femtoflowparticle::PidCut, + femtoflowparticle::TempFitVar, + femtoflowparticle::ChildrenIds, + femtoflowparticle::MLambda, + femtoflowparticle::MAntiLambda, + femtoflowparticle::Theta, + femtoflowparticle::Px, + femtoflowparticle::Py, + femtoflowparticle::Pz, + femtoflowparticle::P); +using FDParticle = FDParticles::iterator; + +/// FemtoFlowCascadeTrack +namespace femtoflowcascparticle +{ +DECLARE_SOA_INDEX_COLUMN(FDParticle, fDParticle); +DECLARE_SOA_COLUMN(DcaV0daughters, dcaV0daughters, float); //! DCA between V0 daughters +DECLARE_SOA_COLUMN(Cpav0, cpav0, float); //! V0 cos of pointing angle +DECLARE_SOA_COLUMN(V0radius, v0radius, float); //! V0 transverse radius*/ +DECLARE_SOA_COLUMN(CpaCasc, cpaCasc, float); //! cascade cosinus of pointing angle +DECLARE_SOA_COLUMN(Dcacascdaughters, dcacascdaughters, float); //! DCA between cascade daughters +DECLARE_SOA_COLUMN(Cascradius, cascradius, float); //! cascade transverse radius +DECLARE_SOA_COLUMN(Dcapostopv, dcapostopv, float); //! DCA of positive daughter to PV +DECLARE_SOA_COLUMN(Dcanegtopv, dcanegtopv, float); //! DCA of negative daughter to PV +DECLARE_SOA_COLUMN(Dcabachtopv, dcabachtopv, float); //! DCA of bachelor track to PV +DECLARE_SOA_COLUMN(Dcav0topv, dcav0topv, float); //! DCA of V0 to PV + +} // namespace femtoflowcascparticle + +DECLARE_SOA_TABLE(FDExtParticles, "AOD", "FDEXTPARTICLE", + femtoflowparticle::Sign, + femtoflowparticle::TpcNClsFound, + track::TPCNClsFindable, + femtoflowparticle::TpcNClsCrossedRows, + track::TPCNClsShared, + femtoflowparticle::TpcFractionSharedCls, + track::TPCInnerParam, + femtoflowparticle::ItsNCls, + femtoflowparticle::ItsNClsInnerBarrel, + track::DcaXY, + track::DcaZ, + track::TPCSignal, + pidtofbeta::Beta, + pidtpc_tiny::TPCNSigmaStoreEl, + pidtpc_tiny::TPCNSigmaStorePi, + pidtpc_tiny::TPCNSigmaStoreKa, + pidtpc_tiny::TPCNSigmaStorePr, + pidtpc_tiny::TPCNSigmaStoreDe, + pidtof_tiny::TOFNSigmaStoreEl, + pidtof_tiny::TOFNSigmaStorePi, + pidtof_tiny::TOFNSigmaStoreKa, + pidtof_tiny::TOFNSigmaStorePr, + pidtof_tiny::TOFNSigmaStoreDe, + femtoflowparticle::DaughDCA, + femtoflowparticle::TransRadius, + femtoflowparticle::DecayVtxX, + femtoflowparticle::DecayVtxY, + femtoflowparticle::DecayVtxZ, + femtoflowparticle::MKaon, + femtoflowparticle::TpcCrossedRowsOverFindableCls, + pidtpc_tiny::TPCNSigmaEl, + pidtpc_tiny::TPCNSigmaPi, + pidtpc_tiny::TPCNSigmaKa, + pidtpc_tiny::TPCNSigmaPr, + pidtpc_tiny::TPCNSigmaDe, + pidtof_tiny::TOFNSigmaEl, + pidtof_tiny::TOFNSigmaPi, + pidtof_tiny::TOFNSigmaKa, + pidtof_tiny::TOFNSigmaPr, + pidtof_tiny::TOFNSigmaDe); +using FDFullParticle = FDExtParticles::iterator; + +DECLARE_SOA_TABLE(FDCascParticles, "AOD", "FDCASCPARTICLE", + o2::soa::Index<>, + femtoflowparticle::FDCollisionId, + femtoflowcascparticle::FDParticleId, + femtoflowparticle::Theta, + femtoflowparticle::Px, + femtoflowparticle::Py, + femtoflowparticle::Pz, + femtoflowparticle::P, + femtoflowcascparticle::DcaV0daughters, + femtoflowcascparticle::Cpav0, + femtoflowcascparticle::V0radius, + femtoflowcascparticle::CpaCasc, + femtoflowcascparticle::Dcacascdaughters, + femtoflowcascparticle::Cascradius, + femtoflowcascparticle::Dcapostopv, + femtoflowcascparticle::Dcanegtopv, + femtoflowcascparticle::Dcabachtopv, + femtoflowcascparticle::Dcav0topv); +using FDCascParticle = FDCascParticles::iterator; + +/// FemtoFlowTrackMC +namespace femtoflow_mc_particle +{ +/// Distinuishes the different particle origins +enum ParticleOriginMCTruth { + kPrimary, //! Primary track or V0 + kDaughter, //! Particle from a decay + kMaterial, //! Particle from a material + kNotPrimary, //! Not primary particles (kept for compatibility reasons with the FullProducer task. will be removed, since we look at "non primaries" more differentially now) + kFake, //! Particle, that has NOT the PDG code of the current analysed particle + kDaughterLambda, //! Daughter from a Lambda decay + kDaughterSigmaplus, //! Daughter from a Sigma^plus decay + kPrompt, //! Origin for D0/D0bar mesons + kNonPrompt, //! Origin for D0/D0bar mesons + kNOriginMCTruthTypes, + kElse, + kWrongCollision //! Origin for the wrong collision +}; + +//! Naming of the different OriginMCTruth types +static constexpr std::string_view ParticleOriginMCTruthName[kNOriginMCTruthTypes] = { + "_Primary", + "_Daughter", + "_Material", + "_NotPrimary", + "_Fake", + "_DaughterLambda", + "DaughterSigmaPlus", + "_Prompt", + "_NonPrompt"}; + +/// Distinguished between reconstructed and truth +enum MCType { + kRecon, //! Reconstructed in case of MC and used as default in case of data + kTruth, //! MC truth + kNMCTypes +}; + +static constexpr std::string_view MCTypeName[kNMCTypes] = {"", "_MC"}; + +DECLARE_SOA_COLUMN(PartOriginMCTruth, partOriginMCTruth, uint8_t); //! Origin of the particle, according to femtoflowparticle::ParticleOriginMCTruth +DECLARE_SOA_COLUMN(PdgMCTruth, pdgMCTruth, int); //! Particle PDG + +// debug variables +DECLARE_SOA_COLUMN(MotherPDG, motherPDG, int); //! Checks mother PDG, where mother is the primary particle for that decay chain +} // namespace femtoflow_mc_particle + +DECLARE_SOA_TABLE(FDMCParticles, "AOD", "FDMCPARTICLE", + o2::soa::Index<>, + femtoflow_mc_particle::PartOriginMCTruth, + femtoflow_mc_particle::PdgMCTruth, + femtoflowparticle::Pt, + femtoflowparticle::Eta, + femtoflowparticle::Phi); +using FDMCParticle = FDMCParticles::iterator; + +DECLARE_SOA_TABLE(FDExtMCParticles, "AOD", "FDEXTMCPARTICLE", + femtoflow_mc_particle::MotherPDG); +using FDExtMCParticle = FDExtMCParticles::iterator; + +namespace mcfdlabel +{ +DECLARE_SOA_INDEX_COLUMN(FDMCParticle, fDMCParticle); //! MC particle for femtoflowparticle +} // namespace mcfdlabel +DECLARE_SOA_TABLE(FDMCLabels, "AOD", "FDMCLabel", //! Table joinable to FemtoFlowParticle containing the MC labels + mcfdlabel::FDMCParticleId); + +/// Hash +namespace hash +{ +DECLARE_SOA_COLUMN(Bin, bin, int); //! Hash for the event mixing +} // namespace hash +DECLARE_SOA_TABLE(MixingHashes, "AOD", "HASH", hash::Bin); +using MixingHash = MixingHashes::iterator; + +} // namespace o2::aod + +#endif // PWGCF_FEMTO_DATAMODEL_FEMTODERIVED_H_ diff --git a/PWGCF/Femto/TableProducer/CMakeLists.txt b/PWGCF/Femto/TableProducer/CMakeLists.txt index 10a75557174..7f24cea451c 100644 --- a/PWGCF/Femto/TableProducer/CMakeLists.txt +++ b/PWGCF/Femto/TableProducer/CMakeLists.txt @@ -13,3 +13,7 @@ o2physics_add_dpl_workflow(pideuteronfemto SOURCES PiDeuteronFemto.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(femtoflow-producer + SOURCES femtoFlowProducerTask.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::EventFilteringUtils + COMPONENT_NAME Analysis) diff --git a/PWGCF/Femto/TableProducer/femtoFlowProducerTask.cxx b/PWGCF/Femto/TableProducer/femtoFlowProducerTask.cxx new file mode 100644 index 00000000000..1ca87b273d8 --- /dev/null +++ b/PWGCF/Femto/TableProducer/femtoFlowProducerTask.cxx @@ -0,0 +1,439 @@ +// 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 femtoFlowProducerTask.cxx +/// \brief Tasks that produces the track tables used for the pairing +/// \author Laura Serksnyte, TU München, laura.serksnyte@tum.de +/// \author Zuzanna Chochulska, WUT Warsaw & CTU Prague, zchochul@cern.ch +/// \author Malgorzata Janik, WUT Warsaw, majanik@cern.ch +/// \author Pritam Chakraborty, WUT Warsaw, pritam.chakraborty@cern.ch +/// \author Shirajum Monira, WUT Warsaw, shirajum.monira@cern.ch +/// \author Wenya Wu, TUM, wenya.wu@cern.ch +/// \NOTE: The femtoflow framework borrows and copies the framework of femtouniverse and femtodream + +#include "PWGCF/Femto/Core/FemtoFlowCollisionSelection.h" +// #include "PWGCF/Femto/Core/FemtoFlowPhiSelection.h" +#include "PWGCF/Femto/Core/FemtoFlowTrackSelection.h" +// #include "PWGCF/Femto/Core/FemtoFlowV0Selection.h" +#include "PWGCF/Femto/Core/femtoUtils.h" +#include "PWGCF/Femto/DataModel/FemtoDerived.h" +#include "PWGHF/Core/DecayChannels.h" +#include "PWGHF/Core/HfHelper.h" +#include "PWGHF/DataModel/CandidateReconstructionTables.h" +#include "PWGHF/DataModel/CandidateSelectionTables.h" +#include "PWGLF/DataModel/LFStrangenessTables.h" + +#include "Common/CCDB/ctpRateFetcher.h" +#include "Common/Core/RecoDecay.h" +#include "Common/Core/trackUtilities.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponse.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include "CommonConstants/PhysicsConstants.h" +#include "DataFormatsParameters/GRPMagField.h" +#include "DataFormatsParameters/GRPObject.h" +#include "Framework/ASoAHelpers.h" +#include "Framework/AnalysisDataModel.h" +#include "Framework/AnalysisTask.h" +#include "Framework/HistogramRegistry.h" +#include "Framework/O2DatabasePDGPlugin.h" +#include "Framework/runDataProcessing.h" +#include "ReconstructionDataFormats/Track.h" +#include + +#include "Math/Vector4D.h" +#include "TMath.h" +#include + +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::analysis::femto_flow; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::constants::physics; + +namespace o2::aod +{ + +using FemtoFullCollision = + soa::Join::iterator; +using FemtoFullCollision_CentPbPb = + soa::Join::iterator; +using FemtoFullTracks = + soa::Join; +} // namespace o2::aod + +namespace software_triggers +{ +static const int nTriggers = 6; +static const std::vector triggerNames{"fPPP", "fPPL", "fPLL", "fLLL", "fPD", "fLD"}; +static const float triggerSwitches[1][nTriggers]{ + {0, 0, 0, 0, 0, 0}}; +} // namespace software_triggers + +/// \todo fix how to pass array to setSelection, getRow() passing a different +/// type! +// static constexpr float arrayV0Sel[3][3] = {{100.f, 100.f, 100.f}, {0.2f, +// 0.2f, 0.2f}, {100.f, 100.f, 100.f}}; unsigned int rows = sizeof(arrayV0Sel) / +// sizeof(arrayV0Sel[0]); unsigned int columns = sizeof(arrayV0Sel[0]) / +// sizeof(arrayV0Sel[0][0]); + +struct FemtoFlowProducerTask { + + Produces outputCollision; + Produces outputParts; + Produces outputDebugParts; + + Configurable confIsDebug{"confIsDebug", true, "Enable Debug tables"}; + Configurable confIsRun3{"confIsRun3", true, "Running on Run3 or pilot"}; + Configurable confDoSpher{"confDoSpher", true, "Calculate sphericity. If false sphericity will take value of 2."}; + Configurable confDoqnVec{"confDoqnVec", true, "Calculate qn vector. If false sphericity will take value of 10."}; + Configurable confIsForceGRP{"confIsForceGRP", false, "Set true if the magnetic field configuration is not available in the usual CCDB directory (e.g. for Run 2 converted data or unanchorad Monte Carlo)"}; + + /// Event cuts + FemtoFlowCollisionSelection colCuts; + // Event cuts - Triggers + Configurable confEnableTriggerSelection{"confEnableTriggerSelection", false, "Should the trigger selection be enabled for collisions?"}; + Configurable> confTriggerSwitches{"confTriggerSwitches", {software_triggers::triggerSwitches[0], 1, software_triggers::nTriggers, std::vector{"Switch"}, software_triggers::triggerNames}, "Turn on which trigger should be checked for recorded events to pass selection"}; + Configurable confBaseCCDBPathForTriggers{"confBaseCCDBPathForTriggers", "Users/m/mpuccio/EventFiltering/OTS/Chunked/", "Provide ccdb path for trigger table; default - trigger coordination"}; + + // Event cuts - usual selection criteria + Configurable confEvtUseTPCmult{"confEvtUseTPCmult", false, "Use multiplicity based on the number of tracks with TPC information"}; + Configurable confEvtZvtx{"confEvtZvtx", 10.f, "Evt sel: Max. z-Vertex (cm)"}; + Configurable confEvtTriggerCheck{"confEvtTriggerCheck", true, "Evt sel: check for trigger"}; + Configurable confEvtTriggerSel{"confEvtTriggerSel", kINT7, "Evt sel: trigger"}; + Configurable confEvtOfflineCheck{"confEvtOfflineCheck", false, "Evt sel: check for offline selection"}; + Configurable confCentFT0Min{"confCentFT0Min", 0.f, "Min CentFT0 value for centrality selection"}; + Configurable confCentFT0Max{"confCentFT0Max", 100.f, "Max CentFT0 value for centrality selection"}; + Configurable confIsUsePileUp{"confIsUsePileUp", true, "Required for choosing whether to run the pile-up cuts"}; + Configurable confEvNoSameBunchPileup{"confEvNoSameBunchPileup", true, "Require kNoSameBunchPileup selection on Events."}; + Configurable confEvIsGoodZvtxFT0vsPV{"confEvIsGoodZvtxFT0vsPV", true, "Require kIsGoodZvtxFT0vsPV selection on Events."}; + Configurable confEvIsGoodITSLayersAll{"confEvIsGoodITSLayersAll", true, "Require kIsGoodITSLayersAll selection on Events."}; + Configurable confEvNoCollInRofStandard{"confEvNoCollInRofStandard", true, "Require kNoCollInRofStandard selection on Events."}; + Configurable confEvNoHighMultCollInPrevRof{"confEvNoHighMultCollInPrevRof", true, "Require kNoHighMultCollInPrevRof selection on Events."}; + Configurable confEvNoCollInTimeRangeStandard{"confEvNoCollInTimeRangeStandard", true, "Require kNoCollInTimeRangeStandard selection on Events."}; + Configurable confEvIsVertexITSTPC{"confEvIsVertexITSTPC", true, "Require kIsVertexITSTPC selection on Events"}; + Configurable confTPCOccupancyMin{"confTPCOccupancyMin", 0, "Minimum value for TPC Occupancy selection"}; + Configurable confTPCOccupancyMax{"confTPCOccupancyMax", 1000, "Maximum value for TPC Occupancy selection"}; + + // Track cuts + FemtoFlowTrackSelection trackCuts; + struct : o2::framework::ConfigurableGroup { + Configurable> confTrkCharge{FemtoFlowTrackSelection::getSelectionName(femto_flow_track_selection::kSign, "ConfTrk"), std::vector{-1, 1}, FemtoFlowTrackSelection::getSelectionHelper(femto_flow_track_selection::kSign, "Track selection: ")}; + Configurable> confTrkPtmin{FemtoFlowTrackSelection::getSelectionName(femto_flow_track_selection::kpTMin, "ConfTrk"), std::vector{0.5f, 0.4f, 0.6f}, FemtoFlowTrackSelection::getSelectionHelper(femto_flow_track_selection::kpTMin, "Track selection: ")}; + Configurable> confTrkPtmax{FemtoFlowTrackSelection::getSelectionName(femto_flow_track_selection::kpTMax, "ConfTrk"), std::vector{5.4f, 5.6f, 5.5f}, FemtoFlowTrackSelection::getSelectionHelper(femto_flow_track_selection::kpTMax, "Track selection: ")}; + Configurable> confTrkEta{FemtoFlowTrackSelection::getSelectionName(femto_flow_track_selection::kEtaMax, "ConfTrk"), std::vector{0.8f, 0.7f, 0.9f}, FemtoFlowTrackSelection::getSelectionHelper(femto_flow_track_selection::kEtaMax, "Track selection: ")}; + Configurable> confTrkTPCnclsMin{FemtoFlowTrackSelection::getSelectionName(femto_flow_track_selection::kTPCnClsMin, "ConfTrk"), std::vector{70.f}, FemtoFlowTrackSelection::getSelectionHelper(femto_flow_track_selection::kTPCnClsMin, "Track selection: ")}; + Configurable> confTrkTPCfCls{FemtoFlowTrackSelection::getSelectionName(femto_flow_track_selection::kTPCfClsMin, "ConfTrk"), std::vector{0.83f}, FemtoFlowTrackSelection::getSelectionHelper(femto_flow_track_selection::kTPCfClsMin, "Track selection: ")}; + Configurable> confTrkTPCcRowsMin{FemtoFlowTrackSelection::getSelectionName(femto_flow_track_selection::kTPCcRowsMin, "ConfTrk"), std::vector{70.f, 60.f, 80.f}, FemtoFlowTrackSelection::getSelectionHelper(femto_flow_track_selection::kTPCcRowsMin, "Track selection: ")}; + Configurable> confTrkTPCsCls{FemtoFlowTrackSelection::getSelectionName(femto_flow_track_selection::kTPCsClsMax, "ConfTrk"), std::vector{0.1f, 160.f}, FemtoFlowTrackSelection::getSelectionHelper(femto_flow_track_selection::kTPCsClsMax, "Track selection: ")}; + Configurable> confTrkTPCfracsCls{FemtoFlowTrackSelection::getSelectionName(femto_flow_track_selection::kTPCfracsClsMax, "ConfTrk"), std::vector{0.1f, 160.f}, FemtoFlowTrackSelection::getSelectionHelper(femto_flow_track_selection::kTPCfracsClsMax, "Track selection: ")}; + Configurable> confTrkITSnclsMin{FemtoFlowTrackSelection::getSelectionName(femto_flow_track_selection::kITSnClsMin, "ConfTrk"), std::vector{-1.f, 2.f, 4.f}, FemtoFlowTrackSelection::getSelectionHelper(femto_flow_track_selection::kITSnClsMin, "Track selection: ")}; + Configurable> confTrkITSnclsIbMin{FemtoFlowTrackSelection::getSelectionName(femto_flow_track_selection::kITSnClsIbMin, "ConfTrk"), std::vector{-1.f, 1.f}, FemtoFlowTrackSelection::getSelectionHelper(femto_flow_track_selection::kITSnClsIbMin, "Track selection: ")}; + Configurable> confTrkDCAxyMax{FemtoFlowTrackSelection::getSelectionName(femto_flow_track_selection::kDCAxyMax, "ConfTrk"), std::vector{0.1f, 3.5f}, FemtoFlowTrackSelection::getSelectionHelper(femto_flow_track_selection::kDCAxyMax, "Track selection: ")}; + Configurable> confTrkDCAzMax{FemtoFlowTrackSelection::getSelectionName(femto_flow_track_selection::kDCAzMax, "ConfTrk"), std::vector{0.2f}, FemtoFlowTrackSelection::getSelectionHelper(femto_flow_track_selection::kDCAzMax, "Track selection: ")}; /// \todo Reintegrate PID to the general selection container + Configurable> confTrkPIDnSigmaMax{FemtoFlowTrackSelection::getSelectionName(femto_flow_track_selection::kPIDnSigmaMax, "ConfTrk"), std::vector{3.5f, 3.f, 2.5f}, FemtoFlowTrackSelection::getSelectionHelper(femto_flow_track_selection::kPIDnSigmaMax, "Track selection: ")}; + // Configurable> confTrkPIDspecies{"confTrkPIDspecies", std::vector{o2::track::PID::Pion, o2::track::PID::Kaon, o2::track::PID::Proton, o2::track::PID::Deuteron}, "Trk sel: Particles species for PID (Pion=2, Kaon=3, Proton=4, Deuteron=5)"}; + Configurable> confTrkPIDspecies{"confTrkPIDspecies", std::vector{o2::track::PID::Proton}, "Trk sel: Only select track Proton"}; + // Numbers from ~/alice/O2/DataFormats/Reconstruction/include/ReconstructionDataFormats/PID.h //static constexpr ID Pion = 2; static constexpr ID Kaon = 3; static constexpr ID Proton = 4; static constexpr ID Deuteron = 5; + } ConfTrkSelection; + + Configurable confTrkPIDnSigmaOffsetTPC{"confTrkPIDnSigmaOffsetTPC", 0., "Offset for TPC nSigma because of bad calibration"}; + Configurable confTrkPIDnSigmaOffsetTOF{"confTrkPIDnSigmaOffsetTOF", 0., "Offset for TOF nSigma because of bad calibration"}; + Configurable confTOFpTmin{"confTOFpTmin", 500, "TOF pT min"}; + + struct : o2::framework::ConfigurableGroup { + Configurable confDcaXYCustom0Cut{"confDcaXYCustom0Cut", false, "Enable Custom Dcaxy < [0] cut."}; + Configurable confDcaXYFilterCut{"confDcaXYFilterCut", 2.4, "Value for DCA_XY for the global track"}; // max dca to vertex XY + Configurable confDcaXYCustom1Cut{"confDcaXYCustom1Cut", true, "Enable Custom |DCAxy| < [1] + [2]/pt cut."}; + Configurable confDcaXYCustom11FilterCut{"confDcaXYCustom11FilterCut", 0.004, "Value for [1] custom DCAxy cut -> |DCAxy| < [1] + [2]/pT"}; + Configurable confDcaXYCustom12FilterCut{"confDcaXYCustom12FilterCut", 0.013, "Value for [2] custom DCAxy cut -> |DCAxy| < [1] + [2]/pT"}; + Configurable confDcaZFilterCut{"confDcaZFilterCut", 3.2, "Value for DCA_Z for the global track"}; // max dca to vertex Z + Configurable confTrkMinChi2PerClusterTPC{"confTrkMinChi2PerClusterTPC", 0.f, "Lower limit for chi2 of TPC; currently for testing only"}; + Configurable confTrkMaxChi2PerClusterTPC{"confTrkMaxChi2PerClusterTPC", 4.f, "Upper limit for chi2 of TPC; currently for testing only"}; + Configurable confTrkMaxChi2PerClusterITS{"confTrkMaxChi2PerClusterITS", 10.0f, "Minimal track selection: max allowed chi2 per ITS cluster"}; // 36.0 is default + Configurable confTrkTPCRefit{"confTrkTPCRefit", false, "True: require TPC refit"}; + Configurable confTrkITSRefit{"confTrkITSRefit", false, "True: require ITS refit"}; + } ConfTrackSpecialFilters; + + HistogramRegistry qaRegistry{"QAHistos", {}, OutputObjHandlingPolicy::QAObject}; + HistogramRegistry trackRegistry{"Tracks", {}, OutputObjHandlingPolicy::QAObject}; + + int mRunNumber = 0; + float mMagField; + std::string zorroTriggerNames = ""; + Service ccdb; /// Accessing the CCDB + + void init(InitContext&) + { + colCuts.setCuts(confEvtZvtx, confEvtTriggerCheck, confEvtTriggerSel, confEvtOfflineCheck, confIsRun3, confCentFT0Min, confCentFT0Max); + colCuts.init(&qaRegistry); + + trackCuts.setSelection(ConfTrkSelection.confTrkCharge, femto_flow_track_selection::kSign, femto_flow_selection::kEqual); + trackCuts.setSelection(ConfTrkSelection.confTrkPtmin, femto_flow_track_selection::kpTMin, femto_flow_selection::kLowerLimit); + trackCuts.setSelection(ConfTrkSelection.confTrkPtmax, femto_flow_track_selection::kpTMax, femto_flow_selection::kUpperLimit); + trackCuts.setSelection(ConfTrkSelection.confTrkEta, femto_flow_track_selection::kEtaMax, femto_flow_selection::kAbsUpperLimit); + trackCuts.setSelection(ConfTrkSelection.confTrkTPCnclsMin, femto_flow_track_selection::kTPCnClsMin, femto_flow_selection::kLowerLimit); + trackCuts.setSelection(ConfTrkSelection.confTrkTPCfCls, femto_flow_track_selection::kTPCfClsMin, femto_flow_selection::kLowerLimit); + trackCuts.setSelection(ConfTrkSelection.confTrkTPCcRowsMin, femto_flow_track_selection::kTPCcRowsMin, femto_flow_selection::kLowerLimit); + trackCuts.setSelection(ConfTrkSelection.confTrkTPCsCls, femto_flow_track_selection::kTPCsClsMax, femto_flow_selection::kUpperLimit); + trackCuts.setSelection(ConfTrkSelection.confTrkTPCfracsCls, femto_flow_track_selection::kTPCfracsClsMax, femto_flow_selection::kUpperLimit); + trackCuts.setSelection(ConfTrkSelection.confTrkITSnclsMin, femto_flow_track_selection::kITSnClsMin, femto_flow_selection::kLowerLimit); + trackCuts.setSelection(ConfTrkSelection.confTrkITSnclsIbMin, femto_flow_track_selection::kITSnClsIbMin, femto_flow_selection::kLowerLimit); + trackCuts.setSelection(ConfTrkSelection.confTrkDCAxyMax, femto_flow_track_selection::kDCAxyMax, femto_flow_selection::kAbsUpperLimit); + trackCuts.setSelection(ConfTrkSelection.confTrkDCAzMax, femto_flow_track_selection::kDCAzMax, femto_flow_selection::kAbsUpperLimit); + trackCuts.setSelection(ConfTrkSelection.confTrkPIDnSigmaMax, femto_flow_track_selection::kPIDnSigmaMax, femto_flow_selection::kAbsUpperLimit); + trackCuts.setPIDSpecies(ConfTrkSelection.confTrkPIDspecies); + trackCuts.setnSigmaPIDOffset(confTrkPIDnSigmaOffsetTPC, confTrkPIDnSigmaOffsetTOF); + trackCuts.init(&trackRegistry); + + mRunNumber = 0; + mMagField = 0.0; + /// Initializing CCDB + ccdb->setURL("http://alice-ccdb.cern.ch"); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + + int64_t now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + ccdb->setCreatedNotAfter(now); + } + + /// Function to retrieve the nominal magnetic field in kG (0.1T) and convert it directly to T + void getMagneticFieldTesla(aod::BCsWithTimestamps::iterator bc) + { + // TODO done only once (and not per run). Will be replaced by CCDBConfigurable + // get magnetic field for run + if (mRunNumber == bc.runNumber()) + return; + auto timestamp = bc.timestamp(); + float output = -999; + + if (confIsRun3 && !confIsForceGRP) { + static o2::parameters::GRPMagField* grpo = nullptr; + grpo = ccdb->getForTimeStamp("GLO/Config/GRPMagField", timestamp); + if (grpo == nullptr) { + LOGF(fatal, "GRP object not found for timestamp %llu", timestamp); + return; + } + LOGF(info, "Retrieved GRP for timestamp %llu with L3 ", timestamp, grpo->getL3Current()); + // taken from GRP onject definition of getnominalL3Field; update later to something smarter (mnominalL3Field = std::lround(5.f * mL3Current / 30000.f);) + auto nominalL3Field = std::lround(5.f * grpo->getL3Current() / 30000.f); + output = 0.1 * (nominalL3Field); + + } else { + static o2::parameters::GRPObject* grpo = nullptr; + grpo = ccdb->getForTimeStamp("GLO/GRP/GRP", timestamp); + if (grpo == nullptr) { + LOGF(fatal, "GRP object not found for timestamp %llu", timestamp); + return; + } + LOGF(info, "Retrieved GRP for timestamp %llu with magnetic field of %d kG", timestamp, grpo->getNominalL3Field()); + output = 0.1 * (grpo->getNominalL3Field()); + } + mMagField = output; + mRunNumber = bc.runNumber(); + } + + template + void fillDebugParticle(ParticleType const& particle) + { + outputDebugParts(particle.sign(), (uint8_t)particle.tpcNClsFound(), + particle.tpcNClsFindable(), + (uint8_t)particle.tpcNClsCrossedRows(), + particle.tpcNClsShared(), particle.tpcFractionSharedCls(), particle.tpcInnerParam(), + particle.itsNCls(), particle.itsNClsInnerBarrel(), + particle.dcaXY(), particle.dcaZ(), particle.tpcSignal(), particle.beta(), + particle.tpcNSigmaStoreEl(), particle.tpcNSigmaStorePi(), + particle.tpcNSigmaStoreKa(), particle.tpcNSigmaStorePr(), + particle.tpcNSigmaStoreDe(), particle.tofNSigmaStoreEl(), + particle.tofNSigmaStorePi(), particle.tofNSigmaStoreKa(), + particle.tofNSigmaStorePr(), particle.tofNSigmaStoreDe(), + -999., -999., -999., -999., -999., -999.); + } + + template + bool fillCollisions(CollisionType const& col, TrackType const& tracks) + { + const auto vtxZ = col.posZ(); + float mult = 0; + int multNtr = 0; + if (confIsRun3) { + mult = col.multFV0M(); + multNtr = col.multNTracksPV(); + } else { + mult = 0.5 * (col.multFV0M()); /// For benchmarking on Run 2, V0M in + /// FemtoFlowRun2 is defined V0M/2 + multNtr = col.multTracklets(); + } + if (confEvtUseTPCmult) { + multNtr = col.multTPC(); + } + const auto occupancy = col.trackOccupancyInTimeRange(); + + // check whether the basic event selection criteria are fulfilled + // if the basic selection is NOT fulfilled: + // in case of skimming run - don't store such collisions + // in case of trigger run - store such collisions but don't store any + // particle candidates for such collisions + if (!colCuts.isSelected(col) || (occupancy < confTPCOccupancyMin || occupancy > confTPCOccupancyMax)) { + return false; + } + + float sphericity = colCuts.computeSphericity(col, tracks); + float sphrDefault = 2; + int qnbin = colCuts.myqnBin(col); + int qnBinBug = -999; + int qnBinMin = 0; + int qnBinMax = 10; + + if (!confIsUsePileUp) { + outputCollision(vtxZ, mult, multNtr, confDoSpher ? sphericity : sphrDefault, (confDoqnVec && qnbin >= qnBinMin && qnbin < qnBinMax) ? qnbin : qnBinBug, mMagField); + colCuts.fillQA(col); + return true; + } else if ((!confEvNoSameBunchPileup || col.selection_bit(aod::evsel::kNoSameBunchPileup)) && (!confEvIsGoodZvtxFT0vsPV || col.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) && (!confEvIsGoodITSLayersAll || col.selection_bit(aod::evsel::kIsGoodITSLayersAll)) && (!confEvNoCollInRofStandard || col.selection_bit(aod::evsel::kNoCollInRofStandard)) && (!confEvNoHighMultCollInPrevRof || col.selection_bit(aod::evsel::kNoHighMultCollInPrevRof)) && (!confEvNoCollInTimeRangeStandard || col.selection_bit(aod::evsel::kNoCollInTimeRangeStandard)) + // && (!confEvIsVertexITSTPC || col.selection_bit(aod::evsel::kIsVertexITSTPC)) + ) { + outputCollision(vtxZ, mult, multNtr, confDoSpher ? sphericity : sphrDefault, (confDoqnVec && qnbin >= qnBinMin && qnbin < qnBinMax) ? qnbin : qnBinBug, mMagField); + colCuts.fillQA(col); + return true; + } else { + return false; + } + } + + // template + // bool fillCollisionsCentRun3(CollisionType const& col) + // { + // const auto vtxZ = col.posZ(); + // const auto multNtr = col.multNTracksPV(); + // const auto cent = col.centFT0C(); + // const auto occupancy = col.trackOccupancyInTimeRange(); + + // // check whether the basic event selection criteria are fulfilled + // // if the basic selection is NOT fulfilled: + // // in case of skimming run - don't store such collisions + // // in case of trigger run - store such collisions but don't store any + // // particle candidates for such collisions + + // if (!colCuts.isSelectedRun3(col) || (occupancy < confTPCOccupancyMin || occupancy > confTPCOccupancyMax)) { + // return false; + // } else { + // if (col.selection_bit(aod::evsel::kNoSameBunchPileup) && + // col.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV) && + // col.selection_bit(aod::evsel::kIsGoodITSLayersAll) && + // col.selection_bit(aod::evsel::kNoCollInRofStandard) && + // col.selection_bit(aod::evsel::kNoHighMultCollInPrevRof) && + // col.selection_bit(aod::evsel::kNoCollInTimeRangeStandard)) { + // outputCollision(vtxZ, cent, multNtr, 2, mMagField); + // return true; + // } else { + // return false; + // } + // } + // } + + template + void fillTracks(TrackType const& tracks) + { + std::vector childIDs = {0, 0}; // these IDs are necessary to keep track of the children + std::vector tmpIDtrack; // this vector keeps track of the matching of the primary track table row <-> aod::track table global index + + for (const auto& track : tracks) { + /// if the most open selection criteria are not fulfilled there is no + /// point looking further at the track + + if (track.pt() > confTOFpTmin) { + if (!track.hasTOF()) { + continue; + } + } + if (ConfTrackSpecialFilters.confDcaXYCustom0Cut && std::fabs(track.dcaXY()) > ConfTrackSpecialFilters.confDcaXYFilterCut) { + continue; + } + if (ConfTrackSpecialFilters.confDcaXYCustom1Cut && std::fabs(track.dcaXY()) > ConfTrackSpecialFilters.confDcaXYCustom11FilterCut + ConfTrackSpecialFilters.confDcaXYCustom12FilterCut / track.pt()) { + continue; + } + if (std::fabs(track.dcaZ()) > ConfTrackSpecialFilters.confDcaZFilterCut) { + continue; + } + if (track.tpcChi2NCl() < ConfTrackSpecialFilters.confTrkMinChi2PerClusterTPC || track.tpcChi2NCl() > ConfTrackSpecialFilters.confTrkMaxChi2PerClusterTPC) { + continue; + } + if (track.itsChi2NCl() > ConfTrackSpecialFilters.confTrkMaxChi2PerClusterITS) { + continue; + } + if ((ConfTrackSpecialFilters.confTrkTPCRefit && !track.hasTPC()) || (ConfTrackSpecialFilters.confTrkITSRefit && !track.hasITS())) { + continue; + } + if (!trackCuts.isSelectedMinimal(track)) { + continue; + } + + trackCuts.fillQA(track); + + // the bit-wise container of the systematic variations is obtained + auto cutContainer = trackCuts.getCutContainer(track); + + // now the table is filled + outputParts(outputCollision.lastIndex(), track.pt(), track.eta(), + track.phi(), aod::femtoflowparticle::ParticleType::kTrack, + cutContainer.at( + femto_flow_track_selection::TrackContainerPosition::kCuts), + cutContainer.at( + femto_flow_track_selection::TrackContainerPosition::kPID), + track.dcaXY(), childIDs, 0, + track.sign()); // sign getter is mAntiLambda() + + if (confIsDebug) { + fillDebugParticle(track); + } + + tmpIDtrack.push_back(track.globalIndex()); + } + } + + template + void fillCollisionsAndTracks(CollisionType const& col, TrackType const& tracks) + { + const auto colcheck = fillCollisions(col, tracks); + if (colcheck) + fillTracks(tracks); + } + + void processFullData(aod::FemtoFullCollision_CentPbPb const& col, + aod::BCsWithTimestamps const&, + aod::FemtoFullTracks const& tracks) + { + // get magnetic field for run + getMagneticFieldTesla(col.bc_as()); + // fill the tables + fillCollisionsAndTracks(col, tracks); + } + PROCESS_SWITCH(FemtoFlowProducerTask, processFullData, "Provd experimental data", true); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + WorkflowSpec workflow{adaptAnalysisTask(cfgc)}; + return workflow; +} diff --git a/PWGCF/Femto/Tasks/CMakeLists.txt b/PWGCF/Femto/Tasks/CMakeLists.txt new file mode 100644 index 00000000000..8ef9074e9b9 --- /dev/null +++ b/PWGCF/Femto/Tasks/CMakeLists.txt @@ -0,0 +1,15 @@ +# 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. + +o2physics_add_dpl_workflow(femtoflow-pair-track-track + SOURCES femtoFlowPairTaskTrackTrack.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) \ No newline at end of file diff --git a/PWGCF/Femto/Tasks/femtoFlowPairTaskTrackTrack.cxx b/PWGCF/Femto/Tasks/femtoFlowPairTaskTrackTrack.cxx new file mode 100644 index 00000000000..b4466d3b57b --- /dev/null +++ b/PWGCF/Femto/Tasks/femtoFlowPairTaskTrackTrack.cxx @@ -0,0 +1,461 @@ +// 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 femtoFlowPairTaskTrackTrack.cxx +/// \brief Tasks that reads the track tables used for the pairing and builds pairs of two tracks +/// \author Andi Mathis, TU München, andreas.mathis@ph.tum.de +/// \author Georgios Mantzaridis, TU München, georgios.mantzaridis@tum.de +/// \author Anton Riedel, TU München, anton.riedel@tum.de +/// \author Zuzanna Chochulska, WUT Warsaw & CTU Prague, zchochul@cern.ch +/// \author Wenya Wu, TUM, wenya.wu@cern.ch +/// \NOTE: The femtoflow framework borrows and copies the framework of femtouniverse and femtodream + +#include "PWGCF/Femto/Core/FemtoFlowAngularContainer.h" +#include "PWGCF/Femto/Core/FemtoFlowDetaDphiStar.h" +#include "PWGCF/Femto/Core/FemtoFlowEventHisto.h" +#include "PWGCF/Femto/Core/FemtoFlowFemtoContainer.h" +#include "PWGCF/Femto/Core/FemtoFlowPairCleaner.h" +#include "PWGCF/Femto/Core/FemtoFlowParticleHisto.h" +#include "PWGCF/Femto/Core/FemtoFlowTrackSelection.h" +#include "PWGCF/Femto/Core/femtoUtils.h" +#include "PWGCF/Femto/DataModel/FemtoDerived.h" + +#include "Framework/ASoAHelpers.h" +#include "Framework/AnalysisTask.h" +#include "Framework/HistogramRegistry.h" +#include "Framework/O2DatabasePDGPlugin.h" +#include "Framework/RunningWorkflowInfo.h" +#include "Framework/StepTHn.h" +#include "Framework/runDataProcessing.h" + +#include +#include + +using namespace o2; +using namespace o2::analysis::femto_flow; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::soa; + +namespace +{ +static constexpr int NPart = 2; +static constexpr int NCuts = 5; +static const std::vector partNames{"PartOne", "PartTwo"}; +static const std::vector cutNames{"MaxPt", "PIDthr", "nSigmaTPC", "nSigmaTPCTOF", "MaxP"}; +static const float cutsTable[NPart][NCuts]{ + {4.05f, 1.f, 3.f, 3.f, 100.f}, + {4.05f, 1.f, 3.f, 3.f, 100.f}}; +} // namespace + +struct FemtoFlowPairTaskTrackTrack { + + Service pdg; + + SliceCache cache; + Preslice perCol = aod::femtoflowparticle::fDCollisionId; + + using FemtoFullParticles = soa::Join; + Preslice perColReco = aod::femtoflowparticle::fDCollisionId; + + /// Particle selection part + + /// Table for both particles + Configurable> confCutTable{"confCutTable", {cutsTable[0], NPart, NCuts, partNames, cutNames}, "Particle selections"}; + Configurable confNspecies{"confNspecies", 1, "Number of particle spieces with PID info"}; + Configurable confIsMC{"confIsMC", false, "Enable additional Histogramms in the case of a MonteCarlo Run"}; + Configurable> confTrkPIDnSigmaMax{"confTrkPIDnSigmaMax", std::vector{3.5f, 3.f, 2.5f}, "This configurable needs to be the same as the one used in the producer task"}; + Configurable confUse3D{"confUse3D", false, "Enable three dimensional histogramms (to be used only for analysis with high statistics): k* vs mT vs multiplicity"}; + Configurable confUseqnDivide{"confUseqnDivide", true, "Enable histogramms of k* vs mT in separated qn bins"}; + Configurable confIsDebug{"confIsDebug", true, "Enable Debug tables"}; + + /// Particle 1 + Configurable confPDGCodePartOne{"confPDGCodePartOne", 2212, "Particle 1 - PDG code"}; + Configurable confCutPartOne{"confCutPartOne", 170220070, "Particle 1 - Selection bit from cutCulator"}; + Configurable confPIDPartOne{"confPIDPartOne", 0, "Particle 1 - Read from cutCulator"}; // Should be bit-mask from cutCulator + // we also need the possibility to specify whether the bit is true/false ->std>>vector>int>> + + /// Partition for particle 1 + Partition partsOne = (aod::femtoflowparticle::partType == uint8_t(aod::femtoflowparticle::ParticleType::kTrack)) && ((aod::femtoflowparticle::cut & confCutPartOne) == confCutPartOne); + Partition> partsOneMC = (aod::femtoflowparticle::partType == uint8_t(aod::femtoflowparticle::ParticleType::kTrack)) && ((aod::femtoflowparticle::cut & confCutPartOne) == confCutPartOne); + + /// Histogramming for particle 1 + FemtoFlowParticleHisto trackHistoPartOne; + + /// Particle 2 + Configurable confIsSame{"confIsSame", false, "Pairs of the same particle"}; + Configurable confPDGCodePartTwo{"confPDGCodePartTwo", 2212, "Particle 2 - PDG code"}; + Configurable confCutPartTwo{"confCutPartTwo", 170220070, "Particle 2 - Selection bit"}; + Configurable confPIDPartTwo{"confPIDPartTwo", 0, "Particle 2 - Read from cutCulator"}; // we also need the possibility to specify whether the bit is true/false ->std>>vector> + + /// Partition for particle 2 + Partition partsTwo = (aod::femtoflowparticle::partType == uint8_t(aod::femtoflowparticle::ParticleType::kTrack)) && + // (aod::femtoflowparticle::pt < cfgCutTable->get("PartTwo", "MaxPt")) && + ((aod::femtoflowparticle::cut & confCutPartTwo) == confCutPartTwo); + Partition> partsTwoMC = (aod::femtoflowparticle::partType == uint8_t(aod::femtoflowparticle::ParticleType::kTrack)) && + ((aod::femtoflowparticle::cut & confCutPartTwo) == confCutPartTwo); + + /// Histogramming for particle 2 + FemtoFlowParticleHisto trackHistoPartTwo; + + // Particle for debug + Partition partsDebug = (aod::femtoflowparticle::partType == uint8_t(aod::femtoflowparticle::ParticleType::kTrack)) && + (aod::femtoflowparticle::sign == int8_t(1)) && + ((aod::femtoflowparticle::cut & confCutPartOne) == confCutPartOne); // Bit_mask for partOne or partTwo + + /// Histogramming for debug particle + FemtoFlowParticleHisto trackHistoPartDebug; // FolderSuffixType name set as 0 = "_debug" + + /// Histogramming for Event + FemtoFlowEventHisto eventHisto; + + /// The configurables need to be passed to an std::vector + int vPIDPartOne, vPIDPartTwo; + std::vector kNsigma; + + /// particle part + ConfigurableAxis confTempFitVarBins{"confTempFitVarBins", {300, -0.15, 0.15}, "binning of the TempFitVar in the pT vs. TempFitVar plot"}; + ConfigurableAxis confTempFitVarpTBins{"confTempFitVarpTBins", {20, 0.5, 4.05}, "pT binning of the pT vs. TempFitVar plot"}; + + /// Correlation part + ConfigurableAxis confMultBins{"confMultBins", {VARIABLE_WIDTH, 0.0f, 50.0f, 100.0f, 200.0f, 300.0f, 500.0f, 800.0f, 1000.0f, 1500.0f, 2000.0f, 2500.0f, 3000.0f, 4000.f}, "Mixing bins - multiplicity"}; // \todo to be obtained from the hash task + ConfigurableAxis confVtxBins{"confVtxBins", {VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; + + ConfigurableAxis confmTBins3D{"confmTBins3D", {VARIABLE_WIDTH, 1.02f, 1.14f, 1.20f, 1.26f, 1.38f, 1.56f, 1.86f, 4.50f}, "mT Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; + ConfigurableAxis confMultBins3D{"confMultBins3D", {VARIABLE_WIDTH, 0.0f, 20.0f, 30.0f, 40.0f, 99999.0f}, "multiplicity Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; + + ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}, true}; + + ConfigurableAxis confkstarBins{"confkstarBins", {1500, 0., 6.}, "binning kstar"}; + ConfigurableAxis confkTBins{"confkTBins", {150, 0., 9.}, "binning kT"}; + ConfigurableAxis confmTBins{"confmTBins", {225, 0., 7.5}, "binning mT"}; + Configurable confNEventsMix{"confNEventsMix", 5, "Number of events for mixing"}; + Configurable confIsCPR{"confIsCPR", false, "Close Pair Rejection"}; + Configurable confCPRPlotPerRadii{"confCPRPlotPerRadii", false, "Plot CPR per radii"}; + Configurable confCPRdeltaPhiCutMax{"confCPRdeltaPhiCutMax", 0.0, "Delta Phi max cut for Close Pair Rejection"}; + Configurable confCPRdeltaPhiCutMin{"confCPRdeltaPhiCutMin", 0.0, "Delta Phi min cut for Close Pair Rejection"}; + Configurable confCPRdeltaEtaCutMax{"confCPRdeltaEtaCutMax", 0.0, "Delta Eta max cut for Close Pair Rejection"}; + Configurable confCPRdeltaEtaCutMin{"confCPRdeltaEtaCutMin", 0.0, "Delta Eta min cut for Close Pair Rejection"}; + Configurable confCPRChosenRadii{"confCPRChosenRadii", 0.01, "Delta Eta cut for Close Pair Rejection"}; + Configurable confPhiBins{"confPhiBins", 29, "Number of phi bins in deta dphi"}; + Configurable confEtaBins{"confEtaBins", 29, "Number of eta bins in deta dphi"}; + + FemtoFlowFemtoContainer sameEventFemtoCont; + FemtoFlowFemtoContainer mixedEventFemtoCont; + FemtoFlowAngularContainer sameEventAngularCont; + FemtoFlowAngularContainer mixedEventAngularCont; + FemtoFlowPairCleaner pairCleaner; + FemtoFlowDetaDphiStar pairCloseRejection; + + /// Histogram output + HistogramRegistry qaRegistry{"TrackQA", {}, OutputObjHandlingPolicy::AnalysisObject}; + HistogramRegistry resultRegistry{"Correlations", {}, OutputObjHandlingPolicy::AnalysisObject}; + HistogramRegistry mixQaRegistry{"mixQaRegistry", {}, OutputObjHandlingPolicy::AnalysisObject}; + + void init(InitContext&) + { + + eventHisto.init(&qaRegistry); + trackHistoPartOne.init(&qaRegistry, confTempFitVarpTBins, confTempFitVarBins, confIsMC, confPDGCodePartOne, false); + if (!confIsSame) { + trackHistoPartTwo.init(&qaRegistry, confTempFitVarpTBins, confTempFitVarBins, confIsMC, confPDGCodePartTwo, false); + } + + if (confIsDebug) + trackHistoPartDebug.init(&qaRegistry, confTempFitVarpTBins, confTempFitVarBins, confIsMC, confPDGCodePartOne, true); + + mixQaRegistry.add("MixingQA/hSECollisionBins", ";bin;Entries", kTH1F, {{120, -0.5, 119.5}}); + mixQaRegistry.add("MixingQA/hMECollisionBins", ";bin;Entries", kTH1F, {{120, -0.5, 119.5}}); + + sameEventFemtoCont.init(&resultRegistry, confkstarBins, confMultBins, confkTBins, confmTBins, confMultBins3D, confmTBins3D, confIsMC, confUse3D, confUseqnDivide); + mixedEventFemtoCont.init(&resultRegistry, confkstarBins, confMultBins, confkTBins, confmTBins, confMultBins3D, confmTBins3D, confIsMC, confUse3D, false); + sameEventAngularCont.init(&resultRegistry, confkstarBins, confMultBins, confkTBins, confmTBins, confMultBins3D, confmTBins3D, confEtaBins, confPhiBins, confIsMC, confUse3D); + mixedEventAngularCont.init(&resultRegistry, confkstarBins, confMultBins, confkTBins, confmTBins, confMultBins3D, confmTBins3D, confEtaBins, confPhiBins, confIsMC, confUse3D); + + sameEventFemtoCont.setPDGCodesMass(confPDGCodePartOne, confPDGCodePartTwo, pdg->Mass(confPDGCodePartOne), pdg->Mass(confPDGCodePartTwo)); + mixedEventFemtoCont.setPDGCodesMass(confPDGCodePartOne, confPDGCodePartTwo, pdg->Mass(confPDGCodePartOne), pdg->Mass(confPDGCodePartTwo)); + sameEventAngularCont.setPDGCodesMass(confPDGCodePartOne, confPDGCodePartTwo, pdg->Mass(confPDGCodePartOne), pdg->Mass(confPDGCodePartTwo)); + mixedEventAngularCont.setPDGCodesMass(confPDGCodePartOne, confPDGCodePartTwo, pdg->Mass(confPDGCodePartOne), pdg->Mass(confPDGCodePartTwo)); + + pairCleaner.init(&qaRegistry); + if (confIsCPR.value) { + pairCloseRejection.init(&resultRegistry, &qaRegistry, confCPRdeltaPhiCutMin.value, confCPRdeltaPhiCutMax.value, confCPRdeltaEtaCutMin.value, confCPRdeltaEtaCutMax.value, confCPRChosenRadii.value, confCPRPlotPerRadii.value); + } + + vPIDPartOne = confPIDPartOne.value; + vPIDPartTwo = confPIDPartTwo.value; + kNsigma = confTrkPIDnSigmaMax.value; + } + + template + void fillCollision(CollisionType col) + { + mixQaRegistry.fill(HIST("MixingQA/hSECollisionBins"), colBinning.getBin({col.posZ(), col.multNtr()})); + eventHisto.fillQA(col); + } + + /// This function processes the same event and takes care of all the histogramming + /// \todo the trivial loops over the tracks should be factored out since they will be common to all combinations of T-T, T-V0, V0-V0, ... + /// @tparam PartitionType + /// @tparam PartType + /// @tparam isMC: enables Monte Carlo truth specific histograms + /// @param groupPartsOne partition for the first particle passed by the process function + /// @param groupPartsTwo partition for the second particle passed by the process function + /// @param parts femtoUniverseParticles table (in case of Monte Carlo joined with FemtoFlowMCLabels) + /// @param magFieldTesla magnetic field of the collision + /// @param multCol multiplicity of the collision + template + void doSameEvent(PartitionType groupPartsOne, PartitionType groupPartsTwo, PartType parts, float magFieldTesla, int multCol, int myqnBin) + { + + /// Histogramming same event + for (const auto& part : groupPartsOne) { + if (part.p() > confCutTable->get("PartOne", "MaxP") || part.pt() > confCutTable->get("PartOne", "MaxPt")) { + continue; + } + if (!isFullPIDSelected(part.pidCut(), + part.p(), + confCutTable->get("PartOne", "PIDthr"), + vPIDPartOne, + confNspecies, + kNsigma, + confCutTable->get("PartOne", "nSigmaTPC"), + confCutTable->get("PartOne", "nSigmaTPCTOF"))) { + continue; + } + + trackHistoPartOne.fillQA(part); + } + + if (!confIsSame) { + for (const auto& part : groupPartsTwo) { + if (part.p() > confCutTable->get("PartTwo", "MaxP") || part.pt() > confCutTable->get("PartTwo", "MaxPt")) { + continue; + } + if (!isFullPIDSelected(part.pidCut(), + part.p(), + confCutTable->get("PartTwo", "PIDthr"), + vPIDPartTwo, + confNspecies, + kNsigma, + confCutTable->get("PartTwo", "nSigmaTPC"), + confCutTable->get("PartTwo", "nSigmaTPCTOF"))) { + continue; + } + trackHistoPartTwo.fillQA(part); + } + } + /// Now build the combinations + for (const auto& [p1, p2] : combinations(CombinationsStrictlyUpperIndexPolicy(groupPartsOne, groupPartsTwo))) { + if (p1.p() > confCutTable->get("PartOne", "MaxP") || p1.pt() > confCutTable->get("PartOne", "MaxPt") || p2.p() > confCutTable->get("PartTwo", "MaxP") || p2.pt() > confCutTable->get("PartTwo", "MaxPt")) { + continue; + } + if (!isFullPIDSelected(p1.pidCut(), + p1.p(), + confCutTable->get("PartOne", "PIDthr"), + vPIDPartOne, + confNspecies, + kNsigma, + confCutTable->get("PartOne", "nSigmaTPC"), + confCutTable->get("PartOne", "nSigmaTPCTOF")) || + !isFullPIDSelected(p2.pidCut(), + p2.p(), + confCutTable->get("PartTwo", "PIDthr"), + vPIDPartTwo, + confNspecies, + kNsigma, + confCutTable->get("PartTwo", "nSigmaTPC"), + confCutTable->get("PartTwo", "nSigmaTPCTOF"))) { + continue; + } + + if (confIsCPR.value) { + if (pairCloseRejection.isClosePair(p1, p2, parts, magFieldTesla, femto_flow_femto_container::EventType::same)) { + continue; + } + } + + // track cleaning + if (!pairCleaner.isCleanPair(p1, p2, parts)) { + continue; + } + sameEventFemtoCont.setPair(p1, p2, multCol, confUse3D, confUseqnDivide, myqnBin); + sameEventAngularCont.setPair(p1, p2, multCol, confUse3D); + } + } + + /// process function for to call doSameEvent with Data + /// \param col subscribe to the collision table (Data) + /// \param parts subscribe to the femtoUniverseParticleTable + void processSameEvent(const o2::aod::FDCollision& col, + const o2::aod::FDParticles& parts, + const FemtoFullParticles&) + { + fillCollision(col); + + auto thegroupPartsOne = partsOne->sliceByCached(aod::femtoflowparticle::fDCollisionId, col.globalIndex(), cache); + auto thegroupPartsTwo = partsTwo->sliceByCached(aod::femtoflowparticle::fDCollisionId, col.globalIndex(), cache); + + doSameEvent(thegroupPartsOne, thegroupPartsTwo, parts, col.magField(), col.multNtr(), col.qnBin()); + + if (confIsDebug) { + auto thegroupPartsDebug = partsDebug->sliceByCached(aod::femtoflowparticle::fDCollisionId, col.globalIndex(), cache); + for (const auto& partDebug : thegroupPartsDebug) { + if (partDebug.p() > confCutTable->get("PartTwo", "MaxP") || partDebug.pt() > confCutTable->get("PartTwo", "MaxPt")) { + continue; + } + if (!isFullPIDSelected(partDebug.pidCut(), + partDebug.p(), + confCutTable->get("PartOne", "PIDthr"), + vPIDPartOne, + confNspecies, + kNsigma, + confCutTable->get("PartOne", "nSigmaTPC"), + confCutTable->get("PartOne", "nSigmaTPCTOF"))) { + continue; + } + trackHistoPartDebug.fillQA(partDebug); + } + } + } + PROCESS_SWITCH(FemtoFlowPairTaskTrackTrack, processSameEvent, "Enable processing same event", true); + + /// process function for to call doSameEvent with Monte Carlo + /// \param col subscribe to the collision table (Monte Carlo Reconstructed reconstructed) + /// \param parts subscribe to joined table FemtoFlowParticles and FemtoFlowMCLables to access Monte Carlo truth + /// \param FemtoFlowMCParticles subscribe to the Monte Carlo truth table + void processSameEventMC(const o2::aod::FDCollision& col, + const soa::Join& parts, + const o2::aod::FDMCParticles&) + { + fillCollision(col); + + auto thegroupPartsOne = partsOneMC->sliceByCached(aod::femtoflowparticle::fDCollisionId, col.globalIndex(), cache); + auto thegroupPartsTwo = partsTwoMC->sliceByCached(aod::femtoflowparticle::fDCollisionId, col.globalIndex(), cache); + + doSameEvent(thegroupPartsOne, thegroupPartsTwo, parts, col.magField(), col.multNtr(), col.qnBin()); + } + PROCESS_SWITCH(FemtoFlowPairTaskTrackTrack, processSameEventMC, "Enable processing same event for Monte Carlo", false); + + /// This function processes the mixed event + /// \todo the trivial loops over the collisions and tracks should be factored out since they will be common to all combinations of T-T, T-V0, V0-V0, ... + /// \tparam PartitionType + /// \tparam PartType + /// \tparam isMC: enables Monte Carlo truth specific histograms + /// \param groupPartsOne partition for the first particle passed by the process function + /// \param groupPartsTwo partition for the second particle passed by the process function + /// \param parts femtoUniverseParticles table (in case of Monte Carlo joined with FemtoFlowMCLabels) + /// \param magFieldTesla magnetic field of the collision + /// \param multCol multiplicity of the collision + template + void doMixedEvent(PartitionType groupPartsOne, PartitionType groupPartsTwo, PartType parts, float magFieldTesla, int multCol) + { + + for (const auto& [p1, p2] : combinations(CombinationsFullIndexPolicy(groupPartsOne, groupPartsTwo))) { + if (p1.p() > confCutTable->get("PartOne", "MaxP") || p1.pt() > confCutTable->get("PartOne", "MaxPt") || p2.p() > confCutTable->get("PartTwo", "MaxP") || p2.pt() > confCutTable->get("PartTwo", "MaxPt")) { + continue; + } + if (!isFullPIDSelected(p1.pidCut(), + p1.p(), + confCutTable->get("PartOne", "PIDthr"), + vPIDPartOne, + confNspecies, + kNsigma, + confCutTable->get("PartOne", "nSigmaTPC"), + confCutTable->get("PartOne", "nSigmaTPCTOF")) || + !isFullPIDSelected(p2.pidCut(), + p2.p(), + confCutTable->get("PartTwo", "PIDthr"), + vPIDPartTwo, + confNspecies, + kNsigma, + confCutTable->get("PartTwo", "nSigmaTPC"), + confCutTable->get("PartTwo", "nSigmaTPCTOF"))) { + continue; + } + + if (confIsCPR.value) { + if (pairCloseRejection.isClosePair(p1, p2, parts, magFieldTesla, femto_flow_femto_container::EventType::mixed)) { + continue; + } + } + + mixedEventFemtoCont.setPair(p1, p2, multCol, confUse3D, false, -999); + mixedEventAngularCont.setPair(p1, p2, multCol, confUse3D); + } + } + + /// process function for to call doMixedEvent with Data + /// @param cols subscribe to the collisions table (Data) + /// @param parts subscribe to the femtoFlowParticleTable + void processMixedEvent(const o2::aod::FDCollisions& cols, + const o2::aod::FDParticles& parts) + { + for (const auto& [collision1, collision2] : soa::selfCombinations(colBinning, 5, -1, cols, cols)) { + + const int multiplicityCol = collision1.multNtr(); + mixQaRegistry.fill(HIST("MixingQA/hMECollisionBins"), colBinning.getBin({collision1.posZ(), multiplicityCol})); + + auto groupPartsOne = partsOne->sliceByCached(aod::femtoflowparticle::fDCollisionId, collision1.globalIndex(), cache); + auto groupPartsTwo = partsTwo->sliceByCached(aod::femtoflowparticle::fDCollisionId, collision2.globalIndex(), cache); + + const auto& magFieldTesla1 = collision1.magField(); + const auto& magFieldTesla2 = collision2.magField(); + + if (magFieldTesla1 != magFieldTesla2) { + continue; + } + /// \todo before mixing we should check whether both collisions contain a pair of particles! + // if (partsOne.size() == 0 || nPart2Evt1 == 0 || nPart1Evt2 == 0 || partsTwo.size() == 0 ) continue; + + doMixedEvent(groupPartsOne, groupPartsTwo, parts, magFieldTesla1, multiplicityCol); + } + } + PROCESS_SWITCH(FemtoFlowPairTaskTrackTrack, processMixedEvent, "Enable processing mixed events", true); + + /// brief process function for to call doMixedEvent with Monte Carlo + /// @param cols subscribe to the collisions table (Monte Carlo Reconstructed reconstructed) + /// @param parts subscribe to joined table FemtoFlowParticles and FemtoFlowMCLables to access Monte Carlo truth + /// @param FemtoFlowMCParticles subscribe to the Monte Carlo truth table + void processMixedEventMC(const o2::aod::FDCollisions& cols, + const soa::Join& parts, + const o2::aod::FDMCParticles&) + { + for (const auto& [collision1, collision2] : soa::selfCombinations(colBinning, 5, -1, cols, cols)) { + + const int multiplicityCol = collision1.multNtr(); + mixQaRegistry.fill(HIST("MixingQA/hMECollisionBins"), colBinning.getBin({collision1.posZ(), multiplicityCol})); + + auto groupPartsOne = partsOneMC->sliceByCached(aod::femtoflowparticle::fDCollisionId, collision1.globalIndex(), cache); + auto groupPartsTwo = partsTwoMC->sliceByCached(aod::femtoflowparticle::fDCollisionId, collision2.globalIndex(), cache); + + const auto& magFieldTesla1 = collision1.magField(); + const auto& magFieldTesla2 = collision2.magField(); + + if (magFieldTesla1 != magFieldTesla2) { + continue; + } + /// \todo before mixing we should check whether both collisions contain a pair of particles! + // if (partsOne.size() == 0 || nPart2Evt1 == 0 || nPart1Evt2 == 0 || partsTwo.size() == 0 ) continue; + + doMixedEvent(groupPartsOne, groupPartsTwo, parts, magFieldTesla1, multiplicityCol); + } + } + PROCESS_SWITCH(FemtoFlowPairTaskTrackTrack, processMixedEventMC, "Enable processing mixed events MC", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + WorkflowSpec workflow{ + adaptAnalysisTask(cfgc), + }; + return workflow; +} diff --git a/PWGCF/FemtoDream/Core/femtoDreamCollisionSelection.h b/PWGCF/FemtoDream/Core/femtoDreamCollisionSelection.h index 73350e81c6c..1d684684ae3 100644 --- a/PWGCF/FemtoDream/Core/femtoDreamCollisionSelection.h +++ b/PWGCF/FemtoDream/Core/femtoDreamCollisionSelection.h @@ -16,13 +16,18 @@ #ifndef PWGCF_FEMTODREAM_CORE_FEMTODREAMCOLLISIONSELECTION_H_ #define PWGCF_FEMTODREAM_CORE_FEMTODREAMCOLLISIONSELECTION_H_ -#include -#include #include "Common/CCDB/TriggerAliases.h" +#include "Common/Core/EventPlaneHelper.h" #include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Qvectors.h" + #include "Framework/HistogramRegistry.h" #include "Framework/Logger.h" +#include +#include +#include + using namespace o2::framework; namespace o2::analysis::femtoDream @@ -156,6 +161,36 @@ class FemtoDreamCollisionSelection return true; } + /// Pile-up selection of PbPb collisions + /// \tparam T type of the collision + /// \param col Collision + /// \return whether or not the collisions fulfills the specified selections + template + bool isPileUpCollisionPbPb(C const& col) + { + int tpcOccupancyMin = 0; + int tpcOccupancyMax = 1000; + const auto occupancy = col.trackOccupancyInTimeRange(); + if ((occupancy < tpcOccupancyMin || occupancy > tpcOccupancyMax)) { + return false; + } + + bool noSameBunchPileup = true; + bool isGoodZvtxFT0vsPV = true; + bool isGoodITSLayersAll = true; + bool noCollInRofStandard = true; + bool noHighMultCollInPrevRof = true; + bool noCollInTimeRangeStandard = true; + // bool isVertexITSTPC = true; + if ((noSameBunchPileup && !col.selection_bit(aod::evsel::kNoSameBunchPileup)) || (isGoodZvtxFT0vsPV && !col.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) || (isGoodITSLayersAll && !col.selection_bit(aod::evsel::kIsGoodITSLayersAll)) || (noCollInRofStandard && !col.selection_bit(aod::evsel::kNoCollInRofStandard)) || (noHighMultCollInPrevRof && !col.selection_bit(aod::evsel::kNoHighMultCollInPrevRof)) || (noCollInTimeRangeStandard && !col.selection_bit(aod::evsel::kNoCollInTimeRangeStandard)) + // || (isVertexITSTPC && !col.selection_bit(aod::evsel::kIsVertexITSTPC)) + ) { + return false; + } + + return true; + } + /// Some basic QA of the event /// \tparam T type of the collision /// \param col Collision @@ -175,6 +210,37 @@ class FemtoDreamCollisionSelection } } + /// Initializes histograms for the flow calculation + /// \param registry Histogram registry to be passed + void initFlow(HistogramRegistry* registry, bool doQnSeparation, int mumQnBins = 10, int binPt = 100, int binEta = 32) + { + if (!mCutsSet) { + LOGF(error, "Event selection not set - quitting!"); + } + mReQthisEvt = new TH2D("ReQthisEvt", "", binPt, 0., 5., binEta, -0.8, 0.8); + mImQthisEvt = new TH2D("ImQthisEvt", "", binPt, 0., 5., binEta, -0.8, 0.8); + mReQ2thisEvt = new TH2D("ReQ2thisEvt", "", binPt, 0., 5., binEta, -0.8, 0.8); + mImQ2thisEvt = new TH2D("ImQ2thisEvt", "", binPt, 0., 5., binEta, -0.8, 0.8); + mMQthisEvt = new TH2D("MQthisEvt", "", binPt, 0., 5., binEta, -0.8, 0.8); + mMQWeightthisEvt = new TH2D("MQWeightthisEvt", "", binPt, 0., 5., binEta, -0.8, 0.8); + mHistogramQn = registry; + mHistogramQn->add("Event/centFT0CBefore", "; cent", kTH1F, {{10, 0, 100}}); + mHistogramQn->add("Event/centFT0CAfter", "; cent", kTH1F, {{10, 0, 100}}); + mHistogramQn->add("Event/centVsqn", "; cent; qn", kTH2F, {{10, 0, 100}, {100, 0, 1000}}); + mHistogramQn->add("Event/centVsqnVsSpher", "; cent; qn; Sphericity", kTH3F, {{10, 0, 100}, {100, 0, 1000}, {100, 0, 1}}); + mHistogramQn->add("Event/qnBin", "; qnBin; entries", kTH1F, {{20, 0, 20}}); + + mHistogramQn->add("Event/profileC22", "; cent; c22", kTProfile, {{10, 0, 100}}); + mHistogramQn->add("Event/profileC24", "; cent; c24", kTProfile, {{10, 0, 100}}); + if (doQnSeparation) { + for (int iqn(0); iqn < mumQnBins; ++iqn) { + mHistogramQn->add(("Qn/profileC22_" + std::to_string(iqn)).c_str(), "; cent; c22", kTProfile, {{10, 0, 100}}); + mHistogramQn->add(("Qn/mult_" + std::to_string(iqn)).c_str(), "; cent; c22", kTH1F, {{100, 0, 1000}}); + } + } + return; + } + /// \todo to be implemented! /// Compute the sphericity of an event /// Important here is that the filter on tracks does not interfere here! @@ -246,6 +312,238 @@ class FemtoDreamCollisionSelection return spt; } + /// \todo to be implemented! + /// Compute the qn-vector(FT0C) of an event + /// \tparam T type of the collision + /// \param col Collision + /// \return value of the qn-vector of FT0C of the event + template + float computeqnVec(T const& col) + { + double qn = std::sqrt(col.qvecFT0CReVec()[0] * col.qvecFT0CReVec()[0] + col.qvecFT0CImVec()[0] * col.qvecFT0CImVec()[0]) * std::sqrt(col.sumAmplFT0C()); + return qn; + } + + /// \todo to be implemented! + /// \param col Collision + /// \return the 1-d qn-vector separator to 2-d + std::vector> getQnBinSeparator2D(std::vector flat) + { + constexpr size_t nBins = 11; + + if (flat.empty() || flat.size() % nBins != 0) { + LOGP(error, "ConfQnBinSeparator size = {} is not divisible by {}", + flat.size(), nBins); + return {{-999, -999}}; + } + + size_t nCent = flat.size() / nBins; + std::vector> res(nCent, std::vector(nBins)); + + for (size_t i = 0; i < nCent; ++i) { + for (size_t j = 0; j < nBins; ++j) { + res[i][j] = flat[i * nBins + j]; + } + } + return res; + } + + /// \todo to be implemented! + /// Get the bin number of qn-vector(FT0C) of an event + /// \tparam T type of the collision + /// \param col Collision + /// \param centBinWidth centrality bin width, example: per 1%, per 10% ... + /// \return bin number of qn-vector of the event + template + int myqnBin(T const& col, float centMax, std::vector qnBinSeparator, float fSpher, float fMult, float centBinWidth = 1.f) + { + auto twoDSeparator = getQnBinSeparator2D(qnBinSeparator); + if (twoDSeparator.empty() || twoDSeparator[0][0] == -999.) { + LOGP(warning, "ConfQnBinSeparator not set, using default fallback!"); + return -999; // safe fallback + } + + mHistogramQn->fill(HIST("Event/centFT0CBefore"), col.centFT0C()); + int qnBin = -999; + float qn = computeqnVec(col); + int mycentBin = static_cast(col.centFT0C() / centBinWidth); + if (mycentBin >= static_cast(centMax / centBinWidth)) + return qnBin; + + if (mycentBin > static_cast(twoDSeparator.size()) - 1) + return qnBin; + + for (int iqn(0); iqn < static_cast(twoDSeparator[mycentBin].size()) - 1; ++iqn) { + if (qn > twoDSeparator[mycentBin][iqn] && qn <= twoDSeparator[mycentBin][iqn + 1]) { + qnBin = iqn; + break; + } else { + continue; + } + } + + mHistogramQn->fill(HIST("Event/centFT0CAfter"), col.centFT0C()); + mHistogramQn->fill(HIST("Event/centVsqn"), col.centFT0C(), qn); + mHistogramQn->fill(HIST("Event/centVsqnVsSpher"), col.centFT0C(), qn, fSpher); + mHistogramQn->fill(HIST("Event/qnBin"), qnBin); + if (qnBin >= 0 && qnBin < 10) { + switch (qnBin) { + case 0: + mHistogramQn->fill(HIST("Qn/mult_") + HIST("0"), fMult); + break; + case 1: + mHistogramQn->fill(HIST("Qn/mult_") + HIST("1"), fMult); + break; + case 2: + mHistogramQn->fill(HIST("Qn/mult_") + HIST("2"), fMult); + break; + case 3: + mHistogramQn->fill(HIST("Qn/mult_") + HIST("3"), fMult); + break; + case 4: + mHistogramQn->fill(HIST("Qn/mult_") + HIST("4"), fMult); + break; + case 5: + mHistogramQn->fill(HIST("Qn/mult_") + HIST("5"), fMult); + break; + case 6: + mHistogramQn->fill(HIST("Qn/mult_") + HIST("6"), fMult); + break; + case 7: + mHistogramQn->fill(HIST("Qn/mult_") + HIST("7"), fMult); + break; + case 8: + mHistogramQn->fill(HIST("Qn/mult_") + HIST("8"), fMult); + break; + case 9: + mHistogramQn->fill(HIST("Qn/mult_") + HIST("9"), fMult); + break; + default: + return qnBin; // invalid qn bin + } + } + return qnBin; + } + + /// \todo to be implemented! + /// Fill cumulants histo for flow calculation + /// Important here is that the filter on tracks does not interfere here! + /// In Run 2 we used here global tracks within |eta| < 0.8 + /// \tparam T1 type of the collision + /// \tparam T2 type of the tracks + /// \param col Collision + /// \param tracks All tracks + /// \return value of the sphericity of the event + template + void fillCumulants(T1 const& col, T2 const& tracks, float fHarmonic = 2.f) + { + int numOfTracks = col.numContrib(); + if (numOfTracks < 3) + return; + + mReQthisEvt->Reset(); + mImQthisEvt->Reset(); + mReQ2thisEvt->Reset(); + mImQ2thisEvt->Reset(); + mMQthisEvt->Reset(); + mMQWeightthisEvt->Reset(); + + for (auto const& track : tracks) { + double weight = 1; // Will implement NUA&NUE correction + double phi = track.phi(); + double pt = track.pt(); + double eta = track.eta(); + double cosnphi = weight * TMath::Cos(fHarmonic * phi); + double sinnphi = weight * TMath::Sin(fHarmonic * phi); + double cos2nphi = weight * TMath::Cos(2 * fHarmonic * phi); + double sin2nphi = weight * TMath::Sin(2 * fHarmonic * phi); + mReQthisEvt->Fill(pt, eta, cosnphi); + mImQthisEvt->Fill(pt, eta, sinnphi); + mReQ2thisEvt->Fill(pt, eta, cos2nphi); + mImQ2thisEvt->Fill(pt, eta, sin2nphi); + mMQthisEvt->Fill(pt, eta); + mMQWeightthisEvt->Fill(pt, eta, weight); + } + + return; + } + + /// \todo to be implemented! + /// Do cumulants for flow calculation + /// Important here is that the filter on tracks does not interfere here! + /// In Run 2 we used here global tracks within |eta| < 0.8 + /// \tparam T1 type of the collision + /// \tparam T2 type of the tracks + /// \param col Collision + /// \param tracks All tracks + /// \return value of the sphericity of the event + template + void doCumulants(T const& col, bool doQnSeparation = false, int qnBin = -999, int mumQnBinNum = 10, float fEtaGap = 0.3f, int binPt = 100, int binEta = 32) + { + if (mMQthisEvt->Integral(1, binPt, 1, binEta) < 2) + return; + + double allReQ = mReQthisEvt->Integral(1, binPt, 1, binEta); + double allImQ = mImQthisEvt->Integral(1, binPt, 1, binEta); + TComplex Q(allReQ, allImQ); + TComplex QStar = TComplex::Conjugate(Q); + + double posEtaRe = mReQthisEvt->Integral(1, binPt, mReQthisEvt->GetYaxis()->FindBin(fEtaGap + 1e-6), binEta); + double posEtaIm = mImQthisEvt->Integral(1, binPt, mImQthisEvt->GetYaxis()->FindBin(fEtaGap + 1e-6), binEta); + if (mMQthisEvt->Integral(1, binPt, mMQthisEvt->GetYaxis()->FindBin(fEtaGap + 1e-6), binEta) < 2) + return; + float posEtaMQ = mMQWeightthisEvt->Integral(1, binPt, mMQthisEvt->GetYaxis()->FindBin(fEtaGap + 1e-6), binEta); + TComplex posEtaQ = TComplex(posEtaRe, posEtaIm); + TComplex posEtaQStar = TComplex::Conjugate(posEtaQ); + + double negEtaRe = mReQthisEvt->Integral(1, binPt, 1, mReQthisEvt->GetYaxis()->FindBin(-1 * fEtaGap - 1e-6)); + double negEtaIm = mImQthisEvt->Integral(1, binPt, 1, mImQthisEvt->GetYaxis()->FindBin(-1 * fEtaGap - 1e-6)); + if (mMQthisEvt->Integral(1, binPt, 1, mMQthisEvt->GetYaxis()->FindBin(-1 * fEtaGap - 1e-6)) < 2) + return; + float negEtaMQ = mMQWeightthisEvt->Integral(1, binPt, 1, mMQthisEvt->GetYaxis()->FindBin(-1 * fEtaGap - 1e-6)); + TComplex negEtaQ = TComplex(negEtaRe, negEtaIm); + TComplex negEtaQStar = TComplex::Conjugate(negEtaQ); + + mHistogramQn->get(HIST("Event/profileC22"))->Fill(col.centFT0C(), (negEtaQ * posEtaQStar).Re() / (negEtaMQ * posEtaMQ), (negEtaMQ * posEtaMQ)); + if (doQnSeparation && qnBin >= 0 && qnBin < mumQnBinNum) { + switch (qnBin) { + case 0: + mHistogramQn->get(HIST("Qn/profileC22_") + HIST("0"))->Fill(col.centFT0C(), (negEtaQ * posEtaQStar).Re() / (negEtaMQ * posEtaMQ), (negEtaMQ * posEtaMQ)); + break; + case 1: + mHistogramQn->get(HIST("Qn/profileC22_") + HIST("1"))->Fill(col.centFT0C(), (negEtaQ * posEtaQStar).Re() / (negEtaMQ * posEtaMQ), (negEtaMQ * posEtaMQ)); + break; + case 2: + mHistogramQn->get(HIST("Qn/profileC22_") + HIST("2"))->Fill(col.centFT0C(), (negEtaQ * posEtaQStar).Re() / (negEtaMQ * posEtaMQ), (negEtaMQ * posEtaMQ)); + break; + case 3: + mHistogramQn->get(HIST("Qn/profileC22_") + HIST("3"))->Fill(col.centFT0C(), (negEtaQ * posEtaQStar).Re() / (negEtaMQ * posEtaMQ), (negEtaMQ * posEtaMQ)); + break; + case 4: + mHistogramQn->get(HIST("Qn/profileC22_") + HIST("4"))->Fill(col.centFT0C(), (negEtaQ * posEtaQStar).Re() / (negEtaMQ * posEtaMQ), (negEtaMQ * posEtaMQ)); + break; + case 5: + mHistogramQn->get(HIST("Qn/profileC22_") + HIST("5"))->Fill(col.centFT0C(), (negEtaQ * posEtaQStar).Re() / (negEtaMQ * posEtaMQ), (negEtaMQ * posEtaMQ)); + break; + case 6: + mHistogramQn->get(HIST("Qn/profileC22_") + HIST("6"))->Fill(col.centFT0C(), (negEtaQ * posEtaQStar).Re() / (negEtaMQ * posEtaMQ), (negEtaMQ * posEtaMQ)); + break; + case 7: + mHistogramQn->get(HIST("Qn/profileC22_") + HIST("7"))->Fill(col.centFT0C(), (negEtaQ * posEtaQStar).Re() / (negEtaMQ * posEtaMQ), (negEtaMQ * posEtaMQ)); + break; + case 8: + mHistogramQn->get(HIST("Qn/profileC22_") + HIST("8"))->Fill(col.centFT0C(), (negEtaQ * posEtaQStar).Re() / (negEtaMQ * posEtaMQ), (negEtaMQ * posEtaMQ)); + break; + case 9: + mHistogramQn->get(HIST("Qn/profileC22_") + HIST("9"))->Fill(col.centFT0C(), (negEtaQ * posEtaQStar).Re() / (negEtaMQ * posEtaMQ), (negEtaMQ * posEtaMQ)); + break; + default: + return; // invalid qn bin + } + } + return; + } + private: HistogramRegistry* mHistogramRegistry = nullptr; ///< For QA output bool mCutsSet = false; ///< Protection against running without cuts @@ -257,6 +555,13 @@ class FemtoDreamCollisionSelection float mZvtxMax = 999.f; ///< Maximal deviation from nominal z-vertex (cm) float mMinSphericity = 0.f; float mSphericityPtmin = 0.f; + HistogramRegistry* mHistogramQn = nullptr; ///< For flow cumulant output + TH2D* mReQthisEvt = nullptr; ///< For flow cumulant in an event + TH2D* mImQthisEvt = nullptr; ///< For flow cumulant in an event + TH2D* mReQ2thisEvt = nullptr; ///< For flow cumulant in an event + TH2D* mImQ2thisEvt = nullptr; ///< For flow cumulant in an event + TH2D* mMQthisEvt = nullptr; + TH2D* mMQWeightthisEvt = nullptr; ///< For flow cumulant in an event }; } // namespace o2::analysis::femtoDream diff --git a/PWGCF/FemtoDream/Core/femtoDreamContainer.h b/PWGCF/FemtoDream/Core/femtoDreamContainer.h index 3d95fc08311..80e47a7dbaf 100644 --- a/PWGCF/FemtoDream/Core/femtoDreamContainer.h +++ b/PWGCF/FemtoDream/Core/femtoDreamContainer.h @@ -180,6 +180,43 @@ class FemtoDreamContainer } } + /// Initialize the histograms for pairs in divided qn bins + template + void init_base_qn(std::string folderName, std::string femtoObs, + T& femtoObsAxis, T& mTAxi4D, T& multPercentileAxis4D, T& qnAxis4D) + { + mHistogramRegistry->add((folderName + "/relPairkstarmTMultMultPercentileQn").c_str(), ("; " + femtoObs + "; #it{m}_{T} (GeV/#it{c}); Centrality; qn").c_str(), kTHnSparseF, {femtoObsAxis, mTAxi4D, multPercentileAxis4D, qnAxis4D}); + } + + template + void init_qn(HistogramRegistry* registry, + T& kstarBins4D, T& mTBins4D, T& multPercentileBins4D, + bool isMC, float highkstarCut, ConfigurableAxis qnBins4D = {"qnBins4D", {10, 0, 10}, "qn binning"}) + { + mHistogramRegistry = registry; + std::string femtoObs; + if constexpr (mFemtoObs == femtoDreamContainer::Observable::kstar) { + femtoObs = "#it{k*} (GeV/#it{c})"; + } + mHighkstarCut = highkstarCut; + + framework::AxisSpec kstarAxis4D = {kstarBins4D, femtoObs}; + framework::AxisSpec mTAxis4D = {mTBins4D, "#it{m}_{T} (GeV/#it{c})"}; + framework::AxisSpec multPercentileAxis4D = {multPercentileBins4D, "Centralty(%)"}; + framework::AxisSpec qnAxis4D = {qnBins4D, "qn"}; + + std::string folderName = static_cast(mFolderSuffix[mEventType]) + static_cast(o2::aod::femtodreamMCparticle::MCTypeName[o2::aod::femtodreamMCparticle::MCType::kRecon]) + static_cast("_qn"); + + init_base_qn(folderName, femtoObs, + kstarAxis4D, mTAxis4D, multPercentileAxis4D, qnAxis4D); + + if (isMC) { + folderName = static_cast(mFolderSuffix[mEventType]) + static_cast(o2::aod::femtodreamMCparticle::MCTypeName[o2::aod::femtodreamMCparticle::MCType::kTruth]) + static_cast("_qn"); + init_base_qn(folderName, femtoObs, + kstarAxis4D, mTAxis4D, multPercentileAxis4D, qnAxis4D); + } + } + /// Set the PDG codes of the two particles involved /// \param pdg1 PDG code of particle one /// \param pdg2 PDG code of particle two @@ -317,6 +354,57 @@ class FemtoDreamContainer } } + /// Pass a pair to the container and compute all the relevant observables in divided qn bins + template + void setPair_qn_base(const float femtoObs, const float mT, const float multPercentile, const int myQnBin, const int numQnBins = 10) + { + if (myQnBin >= 0 && myQnBin < numQnBins) { + mHistogramRegistry->fill(HIST(mFolderSuffix[mEventType]) + HIST(o2::aod::femtodreamMCparticle::MCTypeName[mc]) + HIST("_qn") + HIST("/relPairkstarmTMultMultPercentileQn"), femtoObs, mT, multPercentile, myQnBin); + } else { + return; + } + } + + template + void setPair_qn(T1 const& part1, T2 const& part2, const float multPercentile, const int myQnBin) + { + float femtoObs, femtoObsMC; + // Calculate femto observable and the mT with reconstructed information + if constexpr (mFemtoObs == femtoDreamContainer::Observable::kstar) { + femtoObs = FemtoDreamMath::getkstar(part1, mMassOne, part2, mMassTwo); + } + if (mHighkstarCut > 0) { + if (femtoObs > mHighkstarCut) { + return; + } + } + const float mT = FemtoDreamMath::getmT(part1, mMassOne, part2, mMassTwo); + const int numQnBins = 10; + + if (mHistogramRegistry) { + setPair_qn_base(femtoObs, mT, multPercentile, myQnBin, numQnBins); + + if constexpr (isMC) { + if (part1.has_fdMCParticle() && part2.has_fdMCParticle()) { + // calculate the femto observable and the mT with MC truth information + if constexpr (mFemtoObs == femtoDreamContainer::Observable::kstar) { + femtoObsMC = FemtoDreamMath::getkstar(part1.fdMCParticle(), mMassOne, part2.fdMCParticle(), mMassTwo); + } + const float mTMC = FemtoDreamMath::getmT(part1.fdMCParticle(), mMassOne, part2.fdMCParticle(), mMassTwo); + + if (abs(part1.fdMCParticle().pdgMCTruth()) == mPDGOne && abs(part2.fdMCParticle().pdgMCTruth()) == mPDGTwo) { // Note: all pair-histogramms are filled with MC truth information ONLY in case of non-fake candidates + setPair_qn_base(femtoObsMC, mTMC, multPercentile, myQnBin, numQnBins); + } else { + mHistogramRegistry->fill(HIST(mFolderSuffix[mEventType]) + HIST(o2::aod::femtodreamMCparticle::MCTypeName[o2::aod::femtodreamMCparticle::MCType::kTruth]) + HIST("/hFakePairsCounter"), 0); + } + + } else { + mHistogramRegistry->fill(HIST(mFolderSuffix[mEventType]) + HIST(o2::aod::femtodreamMCparticle::MCTypeName[o2::aod::femtodreamMCparticle::MCType::kTruth]) + HIST("/hNoMCtruthPairsCounter"), 0); + } + } + } + } + protected: HistogramRegistry* mHistogramRegistry = nullptr; ///< For QA output static constexpr std::string_view mFolderSuffix[2] = {"SameEvent", "MixedEvent"}; ///< Folder naming for the output according to mEventType diff --git a/PWGCF/FemtoDream/TableProducer/femtoDreamProducerReducedTask.cxx b/PWGCF/FemtoDream/TableProducer/femtoDreamProducerReducedTask.cxx index a1cd73cbf6f..ebb50a19a4a 100644 --- a/PWGCF/FemtoDream/TableProducer/femtoDreamProducerReducedTask.cxx +++ b/PWGCF/FemtoDream/TableProducer/femtoDreamProducerReducedTask.cxx @@ -50,6 +50,7 @@ namespace o2::aod using FemtoFullCollision = soa::Join::iterator; using FemtoFullCollisionMC = soa::Join::iterator; using FemtoFullCollision_noCent_MC = soa::Join::iterator; +using FemtoFullCollision_CentPbPb = soa::Join::iterator; using FemtoFullTracks = soa::Join outputCollision; + Produces outputExtQnCollision; Produces outputParts; Produces outputPartsMC; Produces outputDebugParts; @@ -99,14 +101,29 @@ struct femtoDreamProducerReducedTask { Configurable> ConfTrkITSnclsIbMin{FemtoDreamTrackSelection::getSelectionName(femtoDreamTrackSelection::kITSnClsIbMin, "ConfTrk"), std::vector{-1.f, 1.f}, FemtoDreamTrackSelection::getSelectionHelper(femtoDreamTrackSelection::kITSnClsIbMin, "Track selection: ")}; Configurable> ConfTrkDCAxyMax{FemtoDreamTrackSelection::getSelectionName(femtoDreamTrackSelection::kDCAxyMax, "ConfTrk"), std::vector{0.1f, 0.5f}, FemtoDreamTrackSelection::getSelectionHelper(femtoDreamTrackSelection::kDCAxyMax, "Track selection: ")}; /// here we need an open cut to do the DCA fits later on! Configurable> ConfTrkDCAzMax{FemtoDreamTrackSelection::getSelectionName(femtoDreamTrackSelection::kDCAzMax, "ConfTrk"), std::vector{0.2f, 0.5f}, FemtoDreamTrackSelection::getSelectionHelper(femtoDreamTrackSelection::kDCAzMax, "Track selection: ")}; - Configurable> ConfTrkPIDnSigmaMax{FemtoDreamTrackSelection::getSelectionName(femtoDreamTrackSelection::kPIDnSigmaMax, "Conf"), std::vector{3.5f, 3.f, 2.5f}, FemtoDreamTrackSelection::getSelectionHelper(femtoDreamTrackSelection::kPIDnSigmaMax, "Track selection: ")}; + Configurable> ConfTrkPIDnSigmaMax{FemtoDreamTrackSelection::getSelectionName(femtoDreamTrackSelection::kPIDnSigmaMax, "ConfTrk"), std::vector{3.5f, 3.f, 2.5f}, FemtoDreamTrackSelection::getSelectionHelper(femtoDreamTrackSelection::kPIDnSigmaMax, "Track selection: ")}; // off set the center of the nsigma distribution to deal with bad TPC/TOF calibration Configurable ConfTrkPIDnSigmaOffsetTPC{"ConfTrkPIDnSigmaOffsetTPC", 0., "Offset for TPC nSigma because of bad calibration"}; Configurable ConfTrkPIDnSigmaOffsetTOF{"ConfTrkPIDnSigmaOffsetTOF", 0., "Offset for TOF nSigma because of bad calibration"}; Configurable> ConfTrkPIDspecies{"ConfTrkPIDspecies", std::vector{o2::track::PID::Pion, o2::track::PID::Kaon, o2::track::PID::Proton, o2::track::PID::Deuteron}, "Trk sel: Particles species for PID"}; + struct : o2::framework::ConfigurableGroup { + Configurable ConfgFlowCalculate{"ConfgFlowCalculate", false, "Evt sel: Cumulant of flow"}; // To do + Configurable ConfgQnSeparation{"ConfgQnSeparation", false, "Evt sel: Qn of event"}; + Configurable> ConfQnBinSeparator{"ConfQnBinSeparator", std::vector{-999.f, -999.f, -999.f}, "Qn bin separator"}; + Configurable ConfCentralityMax{"ConfCentralityMax", 80.f, "Evt sel: Maximum Centrality cut"}; + Configurable ConfCentBinWidth{"ConfCentBinWidth", 1.f, "Centrality bin length for qn separator"}; + Configurable ConfQnBinMin{"ConfQnBinMin", 0, "Minimum qn bin"}; + Configurable ConfQnBinMax{"ConfQnBinMax", 10, "Maximum qn bin"}; + } qnCal; + + struct : o2::framework::ConfigurableGroup { + Configurable ConfIsPbPb{"ConfIsPbPb", false, "Running on Run3 or Run2"}; // Choose if running on PbPb data + } evtSel_PbPb; + HistogramRegistry qaRegistry{"QAHistos", {}, OutputObjHandlingPolicy::AnalysisObject}; HistogramRegistry Registry{"Tracks", {}, OutputObjHandlingPolicy::AnalysisObject}; + HistogramRegistry FlowRegistry{"QnandFlowInfo", {}, OutputObjHandlingPolicy::AnalysisObject}; int mRunNumber; float mMagField; @@ -139,6 +156,11 @@ struct femtoDreamProducerReducedTask { trackCuts.init(&qaRegistry, &Registry); + + if (qnCal.ConfgFlowCalculate) { + colCuts.initFlow(&FlowRegistry, qnCal.ConfgQnSeparation); + } + mRunNumber = 0; mMagField = 0.0; /// Initializing CCDB @@ -325,8 +347,155 @@ struct femtoDreamProducerReducedTask { } } - void - processData(aod::FemtoFullCollision const& col, aod::BCsWithTimestamps const&, aod::FemtoFullTracks const& tracks) + // Centrality (Multiplicity percentile) obtained from FT0C + template + void fillCollisionsAndTracks_PbPb(CollisionType const& col, TrackType const& tracks) + { + const auto vtxZ = col.posZ(); + const auto spher = colCuts.computeSphericity(col, tracks); + int mult = 0; + int multNtr = 0; + if (ConfIsRun3) { + if constexpr (useCentrality) { + mult = col.centFT0C(); + } else { + mult = 0.; + } + multNtr = col.multNTracksPV(); + } else { + mult = 1; // multiplicity percentile is known in Run 2 + multNtr = col.multTracklets(); + } + if (ConfEvtUseTPCmult) { + multNtr = col.multTPC(); + } + colCuts.fillQA(col, mult); + + /// First thing to do is to check whether the basic event selection criteria are fulfilled + /// That includes checking if there are any usable tracks in a collision + if (!colCuts.isSelectedCollision(col)) { + return; + } + if (colCuts.isEmptyCollision(col, tracks, trackCuts)) { + return; + } + + // Pileup rejection in PbPb data + if (evtSel_PbPb.ConfIsPbPb && !colCuts.isPileUpCollisionPbPb(col)) { + return; + } + // now the table is filled + outputCollision(vtxZ, mult, multNtr, spher, mMagField); + + // these IDs are necessary to keep track of the children + // since this producer only produces the tables for tracks, there are no children + std::vector childIDs = {0, 0}; + for (auto& track : tracks) { + /// if the most open selection criteria are not fulfilled there is no point looking further at the track + if (!trackCuts.isSelectedMinimal(track)) { + continue; + } + trackCuts.fillQA(track); + // an array of two bit-wise containers of the systematic variations is obtained + // one container for the track quality cuts and one for the PID cuts + auto cutContainer = trackCuts.getCutContainer(track, track.pt(), track.eta(), sqrtf(powf(track.dcaXY(), 2.f) + powf(track.dcaZ(), 2.f))); + + // now the table is filled + outputParts(outputCollision.lastIndex(), + track.pt(), + track.eta(), + track.phi(), + aod::femtodreamparticle::ParticleType::kTrack, + cutContainer.at(femtoDreamTrackSelection::TrackContainerPosition::kCuts), + cutContainer.at(femtoDreamTrackSelection::TrackContainerPosition::kPID), + track.dcaXY(), childIDs, 0, 0); + if constexpr (isMC) { + fillMCParticle(col, track, o2::aod::femtodreamparticle::ParticleType::kTrack); + } + + if (ConfIsDebug) { + outputDebugParts(track.sign(), + (uint8_t)track.tpcNClsFound(), + track.tpcNClsFindable(), + (uint8_t)track.tpcNClsCrossedRows(), + track.tpcNClsShared(), + track.tpcInnerParam(), + track.itsNCls(), + track.itsNClsInnerBarrel(), + track.dcaXY(), + track.dcaZ(), + track.tpcSignal(), + track.tpcNSigmaEl(), + track.tpcNSigmaPi(), + track.tpcNSigmaKa(), + track.tpcNSigmaPr(), + track.tpcNSigmaDe(), + track.tpcNSigmaTr(), + track.tpcNSigmaHe(), + track.tofNSigmaEl(), + track.tofNSigmaPi(), + track.tofNSigmaKa(), + track.tofNSigmaPr(), + track.tofNSigmaDe(), + track.tofNSigmaTr(), + track.tofNSigmaHe(), + -1, + track.itsNSigmaEl(), + track.itsNSigmaPi(), + track.itsNSigmaKa(), + track.itsNSigmaPr(), + track.itsNSigmaDe(), + track.itsNSigmaTr(), + track.itsNSigmaHe(), + -999., -999., -999., -999., -999., -999., + -999., -999., -999., -999., -999., -999., -999.); + } + } + } + + // Calculate and separate qn bins + // Do and fill cumulant in qn bins + template + void fillCollisionsFlow(CollisionType const& col, TrackType const& tracks) + { + // get magnetic field for run + + const auto spher = colCuts.computeSphericity(col, tracks); + int multNtr = 0; + if (ConfIsRun3) { + multNtr = col.multNTracksPV(); + } else { + multNtr = col.multTracklets(); + } + if (ConfEvtUseTPCmult) { + multNtr = col.multTPC(); + } + + /// First thing to do is to check whether the basic event selection criteria are fulfilled + /// That includes checking if there are any usable tracks in a collision + if (!colCuts.isSelectedCollision(col)) { + return; + } + if (colCuts.isEmptyCollision(col, tracks, trackCuts)) { + return; + } + + // Pileup rejection in PbPb data + if (evtSel_PbPb.ConfIsPbPb && !colCuts.isPileUpCollisionPbPb(col)) { + return; + } + + // Calculate and fill qnBins + auto qnBin = colCuts.myqnBin(col, qnCal.ConfCentralityMax, qnCal.ConfQnBinSeparator, spher, multNtr, 1.f); + if (qnBin < qnCal.ConfQnBinMin || qnBin > qnCal.ConfQnBinMax) { + qnBin = -999; + } + colCuts.fillCumulants(col, tracks); + colCuts.doCumulants(col, qnCal.ConfgQnSeparation, qnBin); + outputExtQnCollision(qnBin); + } + + void processData(aod::FemtoFullCollision const& col, aod::BCsWithTimestamps const&, aod::FemtoFullTracks const& tracks) { // get magnetic field for run getMagneticFieldTesla(col.bc_as()); @@ -365,8 +534,24 @@ struct femtoDreamProducerReducedTask { fillCollisionsAndTracks(col, tracksWithItsPid); } PROCESS_SWITCH(femtoDreamProducerReducedTask, processMC_noCentrality, "Provide MC data", false); -}; + void processData_FlowCalc(aod::FemtoFullCollision_CentPbPb const& col, + aod::BCsWithTimestamps const&, + aod::FemtoFullTracks const& tracks) + { + // get magnetic field for run + getMagneticFieldTesla(col.bc_as()); + auto tracksWithItsPid = soa::Attach(tracks); + // fill the tables + fillCollisionsAndTracks_PbPb(col, tracksWithItsPid); + if (qnCal.ConfgQnSeparation) { + fillCollisionsFlow(col, tracksWithItsPid); + } + } + PROCESS_SWITCH(femtoDreamProducerReducedTask, processData_FlowCalc, + "Provide experimental data with cumulant flow calculation", false); +}; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { WorkflowSpec workflow{adaptAnalysisTask(cfgc)}; diff --git a/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskTrackTrack.cxx b/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskTrackTrack.cxx index 6f45f0c2672..672ac421ece 100644 --- a/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskTrackTrack.cxx +++ b/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskTrackTrack.cxx @@ -82,10 +82,19 @@ struct femtoDreamPairTaskTrackTrack { Filter EventMultiplicity = aod::femtodreamcollision::multNtr >= EventSel.MultMin && aod::femtodreamcollision::multNtr <= EventSel.MultMax && aod::femtodreamcollision::sphericity >= EventSel.SphericityMin; Filter EventMultiplicityPercentile = aod::femtodreamcollision::multV0M >= EventSel.MultPercentileMin && aod::femtodreamcollision::multV0M <= EventSel.MultPercentileMax; + /// qn-separator + struct : ConfigurableGroup { + Configurable doQnSeparation{"doQnSeparation", false, "Do qn separation"}; + Configurable storeEvtTrkInfo{"storeEvtTrkInfo", false, "Fill info of track1 and track2 while pariing in divided qn bins"}; + Configurable numQnBins{"numQnBins", 10, "Number of qn bins"}; + } qnCal; + using FilteredCollisions = soa::Filtered; using FilteredCollision = FilteredCollisions::iterator; using FilteredMCCollisions = soa::Filtered>; using FilteredMCCollision = FilteredMCCollisions::iterator; + using FilteredQnCollisions = soa::Filtered>; + using FilteredQnCollision = FilteredQnCollisions::iterator; using FilteredMaskedCollisions = soa::Filtered>; using FilteredMaskedCollision = FilteredMaskedCollisions::iterator; @@ -226,6 +235,11 @@ struct femtoDreamPairTaskTrackTrack { FemtoDreamPairCleaner pairCleaner; FemtoDreamDetaDphiStar pairCloseRejectionSE; FemtoDreamDetaDphiStar pairCloseRejectionME; + + // Container for correlation functions in devided qn bins + FemtoDreamContainer sameEventQnCont; + // FemtoDreamContainer mixedEventQnCont; + /// Histogram output HistogramRegistry Registry{"Output", {}, OutputObjHandlingPolicy::AnalysisObject}; @@ -268,6 +282,12 @@ struct femtoDreamPairTaskTrackTrack { pairCloseRejectionME.init(&Registry, &Registry, Option.CPRdeltaPhiMax.value, Option.CPRdeltaEtaMax.value, Option.CPRPlotPerRadii.value, 2, Option.CPROld.value); } + if (qnCal.doQnSeparation) { + sameEventQnCont.init_qn(&Registry, + Binning4D.kstar, Binning4D.mT, Binning4D.multPercentile, + Option.IsMC, Option.HighkstarCut); + } + // get bit for the collision mask std::bitset<8 * sizeof(femtodreamcollision::BitMaskType)> mask; int index = 0; @@ -618,8 +638,81 @@ struct femtoDreamPairTaskTrackTrack { } } PROCESS_SWITCH(femtoDreamPairTaskTrackTrack, processMixedEventMCMasked, "Enable processing mixed events MC with masked collisions", false); -}; + /// This function processes the same event in divided qn bins + /// col.multV0M() get the event centrality from ft0c for PbPb data + template + void doSameEventQn(PartitionType SliceTrk1, PartitionType SliceTrk2, PartType parts, Collision col) + { + if (qnCal.storeEvtTrkInfo) { + for (auto& part : SliceTrk1) { + trackHistoPartOne.fillQA(part, aod::femtodreamparticle::kPt, col.multNtr(), col.multV0M()); + } + + if (!Option.SameSpecies.value) { + for (auto& part : SliceTrk2) { + trackHistoPartTwo.fillQA(part, aod::femtodreamparticle::kPt, col.multNtr(), col.multV0M()); + } + } + } + + /// Now build the combinations + float rand = 0.; + if (Option.SameSpecies.value) { + for (auto& [p1, p2] : combinations(CombinationsStrictlyUpperIndexPolicy(SliceTrk1, SliceTrk2))) { + if (Option.CPROn.value) { + if (pairCloseRejectionSE.isClosePair(p1, p2, parts, col.magField())) { + continue; + } + } + // track cleaning + if (!pairCleaner.isCleanPair(p1, p2, parts)) { + continue; + } + if (Option.RandomizePair.value) { + rand = random->Rndm(); + } + if (rand <= 0.5) { + sameEventQnCont.setPair_qn(p1, p2, col.multV0M(), col.qnBin()); + } else { + sameEventQnCont.setPair_qn(p2, p1, col.multV0M(), col.qnBin()); + } + } + } else { + for (auto& [p1, p2] : combinations(CombinationsFullIndexPolicy(SliceTrk1, SliceTrk2))) { + if (Option.CPROn.value) { + if (pairCloseRejectionSE.isClosePair(p1, p2, parts, col.magField())) { + continue; + } + } + // track cleaning + if (!pairCleaner.isCleanPair(p1, p2, parts)) { + continue; + } + sameEventQnCont.setPair_qn(p1, p2, col.multV0M(), col.qnBin()); + } + } + } + + /// process function for to call doSameEventQn with Data + /// \param col subscribe to the collision table (Data) + /// \param parts subscribe to the femtoDreamParticleTable + void processSameEventQn(FilteredQnCollision& col, o2::aod::FDParticles& parts) + { + if (qnCal.storeEvtTrkInfo) { + fillCollision(col); + } + auto SliceTrk1 = PartitionTrk1->sliceByCached(aod::femtodreamparticle::fdCollisionId, col.globalIndex(), cache); + auto SliceTrk2 = PartitionTrk2->sliceByCached(aod::femtodreamparticle::fdCollisionId, col.globalIndex(), cache); + if (SliceTrk1.size() == 0 && SliceTrk2.size() == 0) { + return; + } + if (qnCal.doQnSeparation) { + doSameEventQn(SliceTrk1, SliceTrk2, parts, col); + } + } + PROCESS_SWITCH(femtoDreamPairTaskTrackTrack, processSameEventQn, "Enable processing same event in divided qn bins", false); +}; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { WorkflowSpec workflow{