diff --git a/PWGCF/Femto/CMakeLists.txt b/PWGCF/Femto/CMakeLists.txt index 3b94846cb86..6c68f83c70f 100644 --- a/PWGCF/Femto/CMakeLists.txt +++ b/PWGCF/Femto/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright 2019-2025 CERN and copyright holders of ALICE O2. +# Copyright 2019-2024 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. # @@ -9,6 +9,8 @@ # granted to it by virtue of its status as an Intergovernmental Organization # or submit itself to any jurisdiction. -#add_subdirectory(DataModel) +add_subdirectory(Core) +add_subdirectory(DataModel) add_subdirectory(TableProducer) - +add_subdirectory(Tasks) +# add_subdirectory(Utils) diff --git a/PWGCF/Femto/Core/CMakeLists.txt b/PWGCF/Femto/Core/CMakeLists.txt new file mode 100644 index 00000000000..4c182222bf2 --- /dev/null +++ b/PWGCF/Femto/Core/CMakeLists.txt @@ -0,0 +1,10 @@ +# Copyright 2019-2024 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. diff --git a/PWGCF/Femto/Core/baseSelection.h b/PWGCF/Femto/Core/baseSelection.h new file mode 100644 index 00000000000..f452db10e21 --- /dev/null +++ b/PWGCF/Femto/Core/baseSelection.h @@ -0,0 +1,275 @@ +// 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 baseSelection.h +/// \brief Defines the BaseSelection class for managing and evaluating multiple selections over multiple observables. +/// \author Anton Riedel, TU München, anton.riedel@tum.de + +#ifndef PWGCF_FEMTO_CORE_BASESELECTION_H_ +#define PWGCF_FEMTO_CORE_BASESELECTION_H_ + +#include "PWGCF/Femto/Core/selectionContainer.h" + +#include "fairlogger/Logger.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace o2::analysis::femto +{ + +/// \class BaseSelection +/// \brief Template class for managing selection criteria across multiple observables. +/// +/// This class manages an array of SelectionContainer objects, each corresponding to a specific observable. +/// It evaluates which selections are fulfilled, assembles a final bitmask, and tracks required vs. optional cuts. +/// +/// \tparam T Type of observable values (mostly floats). +/// \tparam BitmaskType Type used for internal bitmask operations (e.g., uint32_t, uint64_t). +/// \tparam NumObservables Total number of observables handled. +template +class BaseSelection +{ + public: + /// \brief Default constructor. + BaseSelection() {} + + /// \brief Destructor + virtual ~BaseSelection() = default; + + /// \brief Add a static-value based selection for a specific observable. + /// \param selectionValues Vector of threshold values. + /// \param observableIndex Index of the observable. + /// \param limitType Type of limit (from limits::LimitType). + /// \param skipMostPermissiveBit Whether to skip the loosest threshold in the bitmask. + /// \param isMinimalCut Whether this cut is mandatory or optional. + void addSelection(std::vector const& selectionValues, int observableIndex, limits::LimitType limitType, bool skipMostPermissiveBit, bool isMinimalCut) + { + if (static_cast(observableIndex) >= NumObservables) { + LOG(fatal) << "Observable is not valid. Observable (index) has to be smaller than " << NumObservables; + } + if (skipMostPermissiveBit) { + mNSelections += selectionValues.size() - 1; + } else { + mNSelections += selectionValues.size(); + } + if (mNSelections >= sizeof(BitmaskType) * CHAR_BIT) { + LOG(fatal) << "Too many selections. At most " << sizeof(BitmaskType) * CHAR_BIT << " are supported"; + } + // check if any cut is optional + if (!isMinimalCut) { + mHasOptionalSelection = true; + } + mSelectionContainers.at(observableIndex) = SelectionContainer(selectionValues, limitType, skipMostPermissiveBit, isMinimalCut); + } + + /// \brief Add a function-based selection for a specific observable. + /// \param baseName Base name for TF1 functions. + /// \param lowerLimit Lower bound for the TF1 domain. + /// \param upperLimit Upper bound for the TF1 domain. + /// \param selectionValues Function definitions as strings. + /// \param observableIndex Index of the observable. + /// \param limitType Type of limit. + /// \param skipMostPermissiveBit Whether to skip the loosest threshold in the bitmask. + /// \param isMinimalCut Whether this cut is mandatory or optional. + void addSelection(std::string const& baseName, + T lowerLimit, + T upperLimit, + std::vector const& selectionValues, + int observableIndex, + limits::LimitType limitType, + bool skipMostPermissiveBit, + bool isMinimalCut) + { + if (static_cast(observableIndex) >= NumObservables) { + LOG(fatal) << "Observable is not valid. Observable (index) has to be smaller than " << NumObservables; + } + if (skipMostPermissiveBit) { + mNSelections += selectionValues.size() - 1; + } else { + mNSelections += selectionValues.size(); + } + if (mNSelections >= sizeof(BitmaskType) * CHAR_BIT) { + LOG(fatal) << "Too many selections. At most " << sizeof(BitmaskType) * CHAR_BIT << " are supported"; + } + mSelectionContainers.at(observableIndex) = SelectionContainer(baseName, lowerLimit, upperLimit, selectionValues, limitType, skipMostPermissiveBit, isMinimalCut); + } + + /// \brief Update the limits of a function-based selection for a specific observable. + /// \param observable Index of the observable. + /// \param value Value at which to evaluate the selection functions. + void updateLimits(int observable, T value) { mSelectionContainers.at(observable).updateLimits(value); } + + /// \brief Reset the internal bitmask and evaluation flags before evaluating a new event. + void reset() + { + mFinalBitmask.reset(); + mPassesMinimalSelections = true; + // will be true if no optional cut as been defined and + // will be set to false if we have optional cuts (but will be set to true in the case at least one optional cut succeeds) + mPassesOptionalSelections = !mHasOptionalSelection; + } + + /// \brief Evaluate a single observable against its configured selections. + /// \param observableIndex Index of the observable. + /// \param value Value of the observable. + void evaluateObservable(int observableIndex, T value) + { + // if there are no selections configured, bail out + if (mSelectionContainers.at(observableIndex).isEmpty()) { + return; + } + // if any previous observable did not pass minimal selections, there is no point in setting bitmask for other observables + // minimal selection for each observable is computed after adding it + if (mPassesMinimalSelections == false) { + return; + } + // set bitmask for given observable + mSelectionContainers.at(observableIndex).evaluate(value); + // check if minimal selction for this observable holds + if (mSelectionContainers.at(observableIndex).passesAsMinimalCut() == false) { + mPassesMinimalSelections = false; + } + // check if any optional selection holds + if (mSelectionContainers.at(observableIndex).passesAsOptionalCut() == true) { + mPassesOptionalSelections = true; + } + } + + /// \brief Check if all required (minimal) and optional cuts are passed. + /// \return True if all required and at least one optional cut (if present) is passed. + bool passesAllRequiredSelections() const + { + return mPassesMinimalSelections && mPassesOptionalSelections; + } + + /// \brief Check if the optional selection for a specific observable is passed. + /// \param observableIndex Index of the observable. + /// \return True if at least one optional selection is fulfilled. + bool passesOptionalSelection(int observableIndex) const + { + return mSelectionContainers.at(observableIndex).passesAsOptionalCut(); + } + + /// \brief Assemble the global selection bitmask from individual observable selections. + void assembleBitmask() + { + // if minimal selections are not passed, just set bitmask to 0 + if (mPassesMinimalSelections == false) { + mFinalBitmask.reset(); + return; + } + + // to assemble bitmask, convert all bitmask into integers + // shift the current one and add the new bits + for (auto const& selectionContainer : mSelectionContainers) { + // if there are no selections for a certain observable, skip + if (selectionContainer.isEmpty()) { + continue; + } + // Shift the result to make space and add the new value + mFinalBitmask = (mFinalBitmask << selectionContainer.getShift()) | selectionContainer.getBitmask(); + } + } + + /// \brief Retrieve the assembled bitmask as an integer value. + /// \return The combined selection bitmask. + BitmaskType getBitmask() const { return static_cast(mFinalBitmask.to_ullong()); } + + /// \brief Retrieve the assembled bitmask as an integer value. + /// \return The combined selection bitmask. + BitmaskType getBitmask(int observableIndex) const { return static_cast(mSelectionContainers.at(observableIndex).getBitmask().to_ullong()); } + + /// \brief Retrieve the assembled bitmask as an integer value. + /// \return The combined selection bitmask. + template + void setBitmask(int observableIndex, R bitmask) + { + mSelectionContainers.at(observableIndex).setBitmask(bitmask); + } + + /// \brief Print detailed information about all configured selections. + /// \tparam MapType Type used in the observable name map (usually an enum or int). + /// \param objectName Name of the current object (e.g. particle species). + /// \param observableNames Map from observable index to human-readable names. + template + void printSelections(const std::string& objectName, const std::unordered_map& observableNames) const + { + LOG(info) << "Printing Configuration of " << objectName; + + size_t globalBitIndex = 0; // Tracks bit position across all containers + + for (size_t idx = mSelectionContainers.size(); idx-- > 0;) { + const auto& container = mSelectionContainers[idx]; + if (container.isEmpty()) { + continue; + } + + const MapType key = static_cast(idx); + const std::string& name = observableNames.count(key) ? observableNames.at(key) : "[Unknown]"; + + LOG(info) << "Observable: " << name << " (index " << idx << ")"; + LOG(info) << " Limit type : " << container.getLimitTypeAsString(); + LOG(info) << " Minimal cut : " << (container.isMinimalCut() ? "yes" : "no"); + LOG(info) << " Skip most permissive : " << (container.skipMostPermissiveBit() ? "yes" : "no"); + LOG(info) << " Bitmask shift : " << container.getShift(); + LOG(info) << " Selections : "; + + const auto& values = container.getSelectionValues(); + const auto& functions = container.getSelectionFunction(); + const bool useFunctions = !functions.empty(); + const size_t numSelections = useFunctions ? functions.size() : values.size(); + const bool skipMostPermissive = container.skipMostPermissiveBit(); + + int valWidth = 20; + int bitWidth = 30; + + for (size_t j = 0; j < numSelections; ++j) { + std::stringstream line; + + // Selection string (either value or function) + const std::string& sel = useFunctions ? std::string(functions[j].GetFormula()->GetExpFormula().Data()) : std::to_string(values[j]); + line << " " << std::left << std::setw(valWidth) << sel; + + // Bitmask + if (skipMostPermissive && j == 0) { + line << std::setw(bitWidth) << "-> loosest minimal selection, no bit saved"; + } else { + const uint64_t bitmask = uint64_t{1} << globalBitIndex++; + line << std::setw(bitWidth) << ("-> bitmask: " + std::to_string(bitmask)); + } + + LOG(info) << line.str(); + } + + LOG(info) << ""; // blank line between observables + } + LOG(info) << "Printing done"; + } + + protected: + std::array, NumObservables> mSelectionContainers = {}; ///< Array containing all selections + std::bitset mFinalBitmask = {}; ///< final bitmaks + size_t mNSelections = 0; ///< Number of selections + bool mPassesMinimalSelections = true; ///< Set to true if all minimal (mandatory) selections are passed + bool mHasOptionalSelection = false; ///< Set to true if at least one selections is optional + bool mPassesOptionalSelections = true; ///< Set to true if at least one optional (non-mandatory) selections is passed +}; +} // namespace o2::analysis::femto + +#endif // PWGCF_FEMTO_CORE_BASESELECTION_H_ diff --git a/PWGCF/Femto/Core/cascadeBuilder.h b/PWGCF/Femto/Core/cascadeBuilder.h new file mode 100644 index 00000000000..637bbce5f3d --- /dev/null +++ b/PWGCF/Femto/Core/cascadeBuilder.h @@ -0,0 +1,465 @@ +// 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 cascadeBuilder.h +/// \brief cascade builder +/// \author anton.riedel@tum.de, TU München, anton.riedel@tum.de + +#ifndef PWGCF_FEMTO_CORE_CASCADEBUILDER_H_ +#define PWGCF_FEMTO_CORE_CASCADEBUILDER_H_ + +#include "PWGCF/Femto/Core/baseSelection.h" +#include "PWGCF/Femto/Core/dataTypes.h" +#include "PWGCF/Femto/Core/femtoUtils.h" +#include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/Core/selectionContainer.h" +#include "PWGCF/Femto/DataModel/FemtoTables.h" + +#include "CommonConstants/MathConstants.h" +#include "Framework/AnalysisHelpers.h" +#include "Framework/Configurable.h" + +#include "fairlogger/Logger.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace o2::analysis::femto +{ +namespace cascadebuilder +{ + +struct ConfCascadeFilters : o2::framework::ConfigurableGroup { + std::string prefix = std::string("CascadeFilters"); + o2::framework::Configurable ptMin{"ptMin", 0.f, "Minimum pT"}; + o2::framework::Configurable ptMax{"ptMax", 99.f, "Maximum pT"}; + o2::framework::Configurable etaMin{"etaMin", -10.f, "Minimum eta"}; + o2::framework::Configurable etaMax{"etaMax", 10.f, "Maximum eta"}; + o2::framework::Configurable phiMin{"phiMin", 0.f, "Minimum phi"}; + o2::framework::Configurable phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"}; + o2::framework::Configurable massXiMin{"massXiMin", 1.2f, "Minimum Xi mass"}; + o2::framework::Configurable massXiMax{"massXiMax", 1.4f, "Maximum Xi mass"}; + o2::framework::Configurable rejectMassXiMin{"rejectMassXiMin", 1.317f, "Reject Minimum Xi mass for Omega hypothesis"}; + o2::framework::Configurable rejectMassXiMax{"rejectMassXiMax", 1.325f, "Rejection Maximum Xi mass for Omega hypothesis"}; + o2::framework::Configurable massOmegaMin{"massOmegaMin", 1.5f, "Minimum Omega mass"}; + o2::framework::Configurable massOmegaMax{"massOmegaMax", 1.9f, "Maximum Omega mass"}; + o2::framework::Configurable rejectMassOmegaMin{"rejectMassOmegaMin", 1.668f, "Reject minimum Omega mass for Xi hypothesis"}; + o2::framework::Configurable rejectMassOmegaMax{"rejectMassOmegaMax", 1.676f, "Reject maximum Omega mass for Xi hypothesis"}; + o2::framework::Configurable massLambdaMin{"massLambdaMin", 1.0f, "Minimum Lambda mass"}; + o2::framework::Configurable massLambdaMax{"massLambdaMax", 1.2f, "Maximum Lambda mass"}; +}; + +#define CASCADE_DEFAULT_BITS \ + o2::framework::Configurable> cascadeCpaMin{"cascadeCpaMin", {0.95f}, "Minimum cosine of pointing angle"}; \ + o2::framework::Configurable> cascadeTransRadMin{"cascadeTransRadMin", {0.9f}, "Minimum transverse radius (cm)"}; \ + o2::framework::Configurable> cascadeDcaDauMax{"cascadeDcaDauMax", {0.25f}, "Maximum DCA between the daughters at decay vertex (cm)"}; \ + o2::framework::Configurable> lambdaCpaMin{"lambdaCpaMin", {0.78f}, "Minimum cosine of pointing angle"}; \ + o2::framework::Configurable> lambdaTransRadMin{"lambdaTransRadMin", {0.9f}, "Minimum transverse radius (cm)"}; \ + o2::framework::Configurable> lambdaDcaDauMax{"lambdaDcaDauMax", {0.5f}, "Maximum DCA between the daughters at decay vertex (cm)"}; \ + o2::framework::Configurable> lambdaDcaToPvMin{"lambdaDcaToPvMin", {0.3f}, "Minimum DCA between the lambda and primary vertex"}; \ + o2::framework::Configurable> dauAbsEtaMax{"dauAbsEtaMax", {0.8f}, "Minimum DCA of the daughters from primary vertex (cm)"}; \ + o2::framework::Configurable> dauDcaMin{"dauDcaMin", {0.05f}, "Minimum DCA of the daughters from primary vertex (cm)"}; \ + o2::framework::Configurable> dauTpcClustersMin{"dauTpcClustersMin", {80.f}, "Minimum number of TPC clusters for daughter tracks"}; \ + o2::framework::Configurable> posDauTpc{"posDauTpc", {5.f}, "Maximum |nsimga_Pion/Proton| TPC for positive daughter tracks"}; \ + o2::framework::Configurable> negDauTpc{"negDauTpc", {5.f}, "Maximum |nsimga_Pion/Proton| TPC for negative daughter tracks"}; + +struct ConfXiBits : o2::framework::ConfigurableGroup { + std::string prefix = std::string("XiBits"); + CASCADE_DEFAULT_BITS + o2::framework::Configurable> bachelorTpcPion{"bachelorTpcPion", {5.f}, "Maximum |nsimga_Pion| TPC for bachelor tracks"}; +}; + +struct ConfOmegaBits : o2::framework::ConfigurableGroup { + std::string prefix = std::string("OmegaBits"); + CASCADE_DEFAULT_BITS + o2::framework::Configurable> bachelorTpcKaon{"bachelorTpcKaon", {5.f}, "Maximum |nsimga_Kaon| TPC for bachelor tracks"}; +}; + +#undef CASCADE_DEFAULT_BITS + +#define CASCADE_DEFAULT_SELECTION(defaultMassMin, defaultMassMax, defaultPdgCode) \ + o2::framework::Configurable pdgCode{"pdgCode", defaultPdgCode, "Track PDG code"}; \ + o2::framework::Configurable sign{"sign", 1, "Sign of the Lambda (+1 for Lambda and -1 for Antilambda"}; \ + o2::framework::Configurable ptMin{"ptMin", 0.f, "Minimum pT"}; \ + o2::framework::Configurable ptMax{"ptMax", 999.f, "Maximum pT"}; \ + o2::framework::Configurable etaMin{"etaMin", -10.f, "Minimum eta"}; \ + o2::framework::Configurable etaMax{"etaMax", 10.f, "Maximum eta"}; \ + o2::framework::Configurable phiMin{"phiMin", 0.f, "Minimum eta"}; \ + o2::framework::Configurable phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"}; \ + o2::framework::Configurable massMin{"massMin", defaultMassMin, "Minimum invariant mass for Cascade"}; \ + o2::framework::Configurable massMax{"massMax", defaultMassMax, "Maximum invariant mass for Cascade"}; \ + o2::framework::Configurable mask{"mask", 0, "Bitmask for cascade selection"}; + +struct ConfXiSelection : o2::framework::ConfigurableGroup { + std::string prefix = std::string("XiSelection"); + CASCADE_DEFAULT_SELECTION(1.22, 1.42, 3212) +}; + +struct ConfOmegaSelection : o2::framework::ConfigurableGroup { + std::string prefix = std::string("OmegaSelection"); + CASCADE_DEFAULT_SELECTION(1.57, 1.77, 3334) +}; + +/// The different selections this task is capable of doing +enum CascadeSels { + // selections for cascades + kCascadeCpaMin, ///< Min. CPA (cosine pointing angle) + kCascadeDcaDaughMax, ///< Max. DCA of the daughers at decay vertex + kCascadeTransRadMin, ///< max. transverse radius + + // selection for lambda daughter + kLambdaCpaMin, ///< Min. DCA of the lambda daughers at primary vertex + kLambdaDcaDauMax, ///< TPC PID for daughters (Pion/Proton) + kLambdaTransRadMin, ///< Min. number of TPC clusters of daughter + kLambdaDcaToPvMin, ///< Min. DCA to primary vertex of daughter lambda + + // selection for bachelor/daugthers + kDauAbsEtaMax, ///< Min. DCA of the daughers/bachelor at primary vertex + kDauTpcClsMin, ///< Min. number of TPC clusters of daughters/bachelor + kDauDcaMin, ///< TPC Pion PID for negative daughter + + // PID selection for cascade bachelor + kBachelorTpcPion, ///< TPC Pion PID for bachelor + kBachelorTpcKaon, ///< TPC Kaon PID for bachelor + /// + // PID selection for lambda daughers + kPosDauTpc, ///< TPC PID for positive daughter + kNegDauTpc, ///< TPC PID for negative daughter + + kCascadeSelsMax +}; + +const char cascadeSelsName[] = "Cascade Selection Object"; +const std::unordered_map cascadeSelsToString = { + {kCascadeCpaMin, "Cascade CPA Min"}, + {kCascadeDcaDaughMax, "Cascade DCA Daughters Max"}, + {kCascadeTransRadMin, "Cascade Transverse Radius Min"}, + + {kLambdaCpaMin, "Lambda CPA Min"}, + {kLambdaDcaDauMax, "Lambda DCA Daughter Max"}, + {kLambdaTransRadMin, "Lambda Transverse Radius Min"}, + {kLambdaDcaToPvMin, "Lambda DCA to PV Min"}, + + {kDauAbsEtaMax, "Daughter Abs Eta Max"}, + {kDauTpcClsMin, "Daughter TPC Clusters Min"}, + {kDauDcaMin, "Daughter DCA Min"}, + + {kBachelorTpcPion, "Bachelor TPC Pion PID"}, + {kBachelorTpcKaon, "Bachelor TPC Kaon PID"}, + + {kPosDauTpc, "Positive Daughter TPC PID"}, + {kNegDauTpc, "Negative Daughter TPC PID"}, + + {kCascadeSelsMax, "Cascade Selections Max"}}; + +/// \class FemtoDreamTrackCuts +/// \brief Cut class to contain and execute all cuts applied to tracks +template +class CascadeSelection : public BaseSelection +{ + public: + CascadeSelection() {} + virtual ~CascadeSelection() = default; + + template + void configure(T1 const& config, T2 const& filter) + { + if constexpr (modes::isEqual(cascadeType, modes::Cascade::kXi)) { + mXiMassLowerLimit = filter.massXiMin.value; + mXiMassUpperLimit = filter.massXiMax.value; + mOmegaMassLowerLimit = filter.rejectMassOmegaMin.value; + mOmegaMassUpperLimit = filter.rejectMassOmegaMax.value; + this->addSelection(config.bachelorTpcPion.value, kBachelorTpcPion, limits::kAbsUpperLimit, true, true); + } + if constexpr (modes::isEqual(cascadeType, modes::Cascade::kOmega)) { + mOmegaMassLowerLimit = filter.massOmegaMin.value; + mOmegaMassUpperLimit = filter.massOmegaMax.value; + mXiMassLowerLimit = filter.rejectMassXiMin.value; + mXiMassUpperLimit = filter.rejectMassXiMax.value; + this->addSelection(config.bachelorTpcKaon.value, kBachelorTpcKaon, limits::kAbsUpperLimit, true, true); + } + + mPtMin = filter.ptMin.value; + mPtMax = filter.ptMax.value; + mEtaMin = filter.etaMin.value; + mEtaMax = filter.etaMax.value; + mPhiMin = filter.phiMin.value; + mPhiMax = filter.phiMax.value; + mLambdaMassMin = filter.massLambdaMin.value; + mLambdaMassMax = filter.massLambdaMax.value; + + this->addSelection(config.posDauTpc.value, kPosDauTpc, limits::kAbsUpperLimit, true, true); + this->addSelection(config.negDauTpc.value, kNegDauTpc, limits::kAbsUpperLimit, true, true); + + this->addSelection(config.cascadeCpaMin.value, kCascadeCpaMin, limits::kLowerLimit, true, true); + this->addSelection(config.cascadeTransRadMin.value, kCascadeTransRadMin, limits::kLowerLimit, true, true); + this->addSelection(config.cascadeDcaDauMax.value, kCascadeDcaDaughMax, limits::kAbsUpperLimit, true, true); + this->addSelection(config.lambdaCpaMin.value, kLambdaCpaMin, limits::kLowerLimit, true, true); + this->addSelection(config.lambdaTransRadMin.value, kLambdaTransRadMin, limits::kLowerLimit, true, true); + this->addSelection(config.lambdaDcaDauMax.value, kLambdaDcaDauMax, limits::kAbsUpperLimit, true, true); + this->addSelection(config.lambdaDcaToPvMin.value, kLambdaDcaToPvMin, limits::kLowerLimit, true, true); + this->addSelection(config.dauAbsEtaMax.value, kDauAbsEtaMax, limits::kAbsUpperLimit, true, true); + this->addSelection(config.dauDcaMin.value, kDauDcaMin, limits::kAbsLowerLimit, true, true); + this->addSelection(config.dauTpcClustersMin.value, kDauTpcClsMin, limits::kLowerLimit, true, true); + }; + + template + void applySelections(T1 const& cascade, T2 const& /*tracks*/, T3 const& col) + { + this->reset(); + // cascade selections + this->evaluateObservable(kCascadeCpaMin, cascade.casccosPA(col.posX(), col.posY(), col.posZ())); + this->evaluateObservable(kCascadeDcaDaughMax, cascade.dcacascdaughters()); + this->evaluateObservable(kCascadeTransRadMin, cascade.cascradius()); + + // lambda selection + this->evaluateObservable(kLambdaCpaMin, cascade.v0cosPA(col.posX(), col.posY(), col.posZ())); + this->evaluateObservable(kLambdaDcaDauMax, cascade.dcaV0daughters()); + this->evaluateObservable(kLambdaTransRadMin, cascade.v0radius()); + this->evaluateObservable(kLambdaDcaToPvMin, cascade.dcav0topv(col.posX(), col.posY(), col.posZ())); + + auto bachelor = cascade.template bachelor_as(); + auto posDaughter = cascade.template posTrack_as(); + auto negDaughter = cascade.template negTrack_as(); + + // daughter selections + std::array etaDaughters = {std::fabs(bachelor.eta()), std::fabs(posDaughter.eta()), std::fabs(negDaughter.eta())}; + this->evaluateObservable(kDauAbsEtaMax, *std::max_element(etaDaughters.begin(), etaDaughters.end())); + + std::array dcaDaughters = {std::hypot(bachelor.dcaXY(), bachelor.dcaZ()), + std::hypot(posDaughter.dcaXY(), posDaughter.dcaZ()), + std::hypot(negDaughter.dcaXY(), negDaughter.dcaZ())}; + this->evaluateObservable(kDauDcaMin, *std::min_element(dcaDaughters.begin(), dcaDaughters.end())); + + std::array clustersDaughters = {1.f * bachelor.tpcNClsFound(), 1.f * posDaughter.tpcNClsFound(), 1.f * negDaughter.tpcNClsFound()}; + this->evaluateObservable(kDauTpcClsMin, *std::min_element(clustersDaughters.begin(), clustersDaughters.end())); + + // bachelor pid selection + // check both pion and kaon PID for xi and omega + this->evaluateObservable(kBachelorTpcPion, bachelor.tpcNSigmaPi()); + this->evaluateObservable(kBachelorTpcKaon, bachelor.tpcNSigmaKa()); + + // depending on the charge, we check lambda or antilambda hypothesis + if (cascade.sign() < 0) { + this->evaluateObservable(kPosDauTpc, posDaughter.tpcNSigmaPr()); + this->evaluateObservable(kNegDauTpc, negDaughter.tpcNSigmaPi()); + } else if (cascade.sign() > 0) { + this->evaluateObservable(kPosDauTpc, posDaughter.tpcNSigmaPi()); + this->evaluateObservable(kNegDauTpc, negDaughter.tpcNSigmaPr()); + } else { + LOG(warn) << "Encountered Cascade candidate with 0 charge"; + } + + this->assembleBitmask(); + }; + + template + bool checkFilters(const T& cascade) const + { + return ((cascade.pt() > mPtMin && cascade.pt() < mPtMax) && + (cascade.eta() > mEtaMin && cascade.eta() < mEtaMax) && + (cascade.phi() > mPhiMin && cascade.phi() < mPhiMax) && + (cascade.mLambda() > mLambdaMassMin && cascade.mLambda() < mLambdaMassMax)); + } + + template + bool checkHypothesis(T const& cascadeCandidate) + { + // no need to check PID of the bachelor/daughters here, they are set as minimal cuts + if constexpr (modes::isEqual(cascadeType, modes::Cascade::kXi)) { + return (mXiMassLowerLimit < cascadeCandidate.mXi() && mXiMassUpperLimit > cascadeCandidate.mXi()) && // inside xi mass window + (cascadeCandidate.mOmega() < mOmegaMassLowerLimit || cascadeCandidate.mOmega() > mOmegaMassUpperLimit); // outside omega mass window + } + if constexpr (modes::isEqual(cascadeType, modes::Cascade::kXi)) { + return (mOmegaMassLowerLimit < cascadeCandidate.mOmega() && mOmegaMassUpperLimit > cascadeCandidate.mOmega()) && // inside omega mass window + (cascadeCandidate.mXi() < mXiMassLowerLimit || cascadeCandidate.mXi() > mXiMassUpperLimit); // outside xi mass window + } + return false; + } + + protected: + float mXiMassLowerLimit = 0.f; + float mXiMassUpperLimit = 999.f; + + float mOmegaMassLowerLimit = 0.f; + float mOmegaMassUpperLimit = 999.f; + + // kinematic filters + float mPtMin = 0.f; + float mPtMax = 6.f; + float mEtaMin = -0.9f; + float mEtaMax = 0.9f; + float mPhiMin = 0.f; + float mPhiMax = o2::constants::math::TwoPI; + float mLambdaMassMin = 1.f; + float mLambdaMassMax = 1.2f; +}; + +struct CascadeBuilderProducts : o2::framework::ProducesGroup { + o2::framework::Produces producedXis; + o2::framework::Produces producedXiMasks; + o2::framework::Produces producedXiExtras; + o2::framework::Produces producedOmegas; + o2::framework::Produces producedOmegaMasks; + o2::framework::Produces producedOmegaExtras; +}; + +struct ConfCascadeTables : o2::framework::ConfigurableGroup { + std::string prefix = std::string("CascadeTables"); + o2::framework::Configurable produceXis{"produceXis", -1, "Produce Xis (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceXiMasks{"produceXiMasks", -1, "Produce XiMasks (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceXiExtras{"produceXiExtras", -1, "Produce XiExtras (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceOmegas{"produceOmegas", -1, "Produce Omegas (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceOmegaMasks{"produceOmegaMasks", -1, "Produce OmegaMasks (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceOmegaExtras{"produceOmegaExtras", -1, "Produce OmegaExtras (-1: auto; 0 off; 1 on)"}; +}; + +template +class CascadeBuilder +{ + public: + CascadeBuilder() {} + virtual ~CascadeBuilder() = default; + + template + void init(T1& config, T2& filter, T3& table, T4& initContext) + { + cascadeSelection.configure(config, filter); + if constexpr (modes::isEqual(cascadeType, modes::Cascade::kXi)) { + LOG(info) << "Initialize femto Xi builder..."; + produceXis = utils::enableTable("FXis_001", table.produceXis.value, initContext); + produceXiMasks = utils::enableTable("FXiMasks_001", table.produceXiMasks.value, initContext); + produceXiExtras = utils::enableTable("FXiExtras_001", table.produceXiExtras.value, initContext); + } + if constexpr (modes::isEqual(cascadeType, modes::Cascade::kOmega)) { + LOG(info) << "Initialize femto Omega builder..."; + produceOmegas = utils::enableTable("FOmegas_001", table.produceOmegas.value, initContext); + produceOmegaMasks = utils::enableTable("FOmegaMasks_001", table.produceOmegaMasks.value, initContext); + produceOmegaExtras = utils::enableTable("FOmegaExtras_001", table.produceOmegaExtras.value, initContext); + } + + if (produceXis || produceXiExtras || produceXiMasks || produceOmegas || produceOmegaMasks || produceOmegaExtras) { + mFillAnyTable = true; + cascadeSelection.printSelections(cascadeSelsName, cascadeSelsToString); + } else { + LOG(info) << "No tables configured"; + } + LOG(info) << "Initialization done..."; + } + + template + void fillCascades(T1& collisionProducts, T2& trackProducts, T3& cascadeProducts, T4 const& fullCascades, T5 const& fullTracks, T6 const& col, T7& trackBuilder, T8& indexMap) + { + if (!mFillAnyTable) { + return; + } + + int64_t bachelorIndex = 0; + int64_t posDaughterIndex = 0; + int64_t negDaughterIndex = 0; + for (const auto& cascade : fullCascades) { + if (!cascadeSelection.checkFilters(cascade)) { + continue; + } + cascadeSelection.applySelections(cascade, fullTracks, col); + if (cascadeSelection.passesAllRequiredSelections() && cascadeSelection.checkHypothesis(cascade)) { + + auto bachelor = cascade.template bachelor_as(); + auto posDaughter = cascade.template posTrack_as(); + auto negDaughter = cascade.template negTrack_as(); + + bachelorIndex = trackBuilder.template getDaughterIndex(bachelor, trackProducts, collisionProducts, indexMap); + posDaughterIndex = trackBuilder.template getDaughterIndex(posDaughter, trackProducts, collisionProducts, indexMap); + negDaughterIndex = trackBuilder.template getDaughterIndex(negDaughter, trackProducts, collisionProducts, indexMap); + + fillCascade(collisionProducts, cascadeProducts, cascade, col, bachelorIndex, posDaughterIndex, negDaughterIndex); + } + } + } + + template + void fillCascade(T1& collisionProducts, T2& cascadeProducts, T3 const& cascade, T4 const& col, int bachelorIndex, int posDaughterIndex, int negDaughterIndex) + { + if constexpr (modes::isEqual(cascadeType, modes::Cascade::kXi)) { + if (produceXis) { + cascadeProducts.producedXis(collisionProducts.producedCollision.lastIndex(), + cascade.sign() * cascade.pt(), + cascade.eta(), + cascade.phi(), + cascade.mXi(), + bachelorIndex, + posDaughterIndex, + negDaughterIndex); + } + if (produceXiMasks) { + cascadeProducts.producedXiMasks(cascadeSelection.getBitmask()); + } + if (produceXiExtras) { + cascadeProducts.producedXiExtras( + cascade.mOmega(), + cascade.casccosPA(col.posX(), col.posY(), col.posZ()), + cascade.dcacascdaughters(), + cascade.cascradius(), + cascade.v0cosPA(col.posX(), col.posY(), col.posZ()), + cascade.dcaV0daughters(), + cascade.v0radius(), + cascade.dcav0topv(col.posY(), col.posY(), col.posZ())); + } + } + if constexpr (modes::isEqual(cascadeType, modes::Cascade::kOmega)) { + if (produceOmegas) { + cascadeProducts.producedOmegas(collisionProducts.producedCollision.lastIndex(), + cascade.sign() * cascade.pt(), + cascade.eta(), + cascade.phi(), + cascade.mOmega(), + bachelorIndex, + posDaughterIndex, + negDaughterIndex); + } + if (produceOmegaMasks) { + cascadeProducts.producedOmegaMasks(cascadeSelection.getBitmask()); + } + if (produceOmegaExtras) { + cascadeProducts.producedOmegaExtras( + cascade.mXi(), + cascade.casccosPA(col.posX(), col.posY(), col.posZ()), + cascade.dcacascdaughters(), + cascade.cascradius(), + cascade.v0cosPA(col.posX(), col.posY(), col.posZ()), + cascade.dcaV0daughters(), + cascade.v0radius(), + cascade.dcav0topv(col.posY(), col.posY(), col.posZ())); + } + } + } + + bool fillAnyTable() { return mFillAnyTable; } + + private: + CascadeSelection cascadeSelection; + bool mFillAnyTable = false; + bool produceXis = false; + bool produceXiMasks = false; + bool produceXiExtras = false; + bool produceOmegas = false; + bool produceOmegaMasks = false; + bool produceOmegaExtras = false; +}; + +} // namespace cascadebuilder +} // namespace o2::analysis::femto +#endif // PWGCF_FEMTO_CORE_CASCADEBUILDER_H_ diff --git a/PWGCF/Femto/Core/cascadeHistManager.h b/PWGCF/Femto/Core/cascadeHistManager.h new file mode 100644 index 00000000000..19c0f9f0d97 --- /dev/null +++ b/PWGCF/Femto/Core/cascadeHistManager.h @@ -0,0 +1,268 @@ +// 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 cascadeHistManager.h +/// \brief histogram manager for cascade histograms +/// \author Anton Riedel, TU München, anton.riedel@cern.ch + +#ifndef PWGCF_FEMTO_CORE_CASCADEHISTMANAGER_H_ +#define PWGCF_FEMTO_CORE_CASCADEHISTMANAGER_H_ + +#include "PWGCF/Femto/Core/histManager.h" +#include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/Core/trackHistManager.h" + +#include "CommonConstants/MathConstants.h" +#include "Framework/Configurable.h" +#include "Framework/HistogramRegistry.h" +#include "Framework/HistogramSpec.h" + +#include +#include +#include +#include +#include + +namespace o2::analysis::femto +{ +namespace cascadehistmanager +{ +// enum for track histograms +enum CascadeHist { + // analysis + kPt, + kEta, + kPhi, + kMass, + kSign, + // qa variables + kMassXi, + kMassOmega, + kCosPa, + kDecayDauDca, + kTransRadius, + // 2d qa + kPtVsEta, + kPtVsPhi, + kPhiVsEta, + kPtVsCosPa, + kPtVsMassXi, + kPtVsMassOmega, + kMassXiVsMassOmega, + kCascadeHistLast +}; + +template +struct ConfCascadeBinning : o2::framework::ConfigurableGroup { + std::string prefix = Prefix; + o2::framework::ConfigurableAxis pt{"pt", {{600, 0, 6}}, "Pt"}; + o2::framework::ConfigurableAxis eta{"eta", {{300, -1.5, 1.5}}, "Eta"}; + o2::framework::ConfigurableAxis phi{"phi", {{720, 0, 1.f * o2::constants::math::TwoPI}}, "Phi"}; + o2::framework::ConfigurableAxis mass{"mass", {{1000, 1.f, 2.f}}, "Mass"}; + o2::framework::ConfigurableAxis sign{"sign", {{3, -1.5, 1.5}}, "Sign"}; +}; + +constexpr const char PrefixXiBinning[] = "XiBinning"; +using ConfXiBinning = ConfCascadeBinning; +constexpr const char PrefixOmegaBinning[] = "OmegaBinning"; +using ConfOmegaBinning = ConfCascadeBinning; + +template +struct ConfCascadeQaBinning : o2::framework::ConfigurableGroup { + std::string prefix = Prefix; + o2::framework::ConfigurableAxis cosPa{"cosPa", {{100, 0.9, 1}}, "Cosine of poiting angle"}; + o2::framework::ConfigurableAxis dauDcaAtDecay{"dauDcaAtDecay", {{150, 0, 1.5}}, "Daughter DCA at decay vertex"}; + o2::framework::ConfigurableAxis transRadius{"transRadius", {{100, 0, 100}}, "Transverse radius"}; + o2::framework::ConfigurableAxis massXi{"massXi", {{400, 1.2f, 1.6f}}, "mass for antiparticle hypothesis"}; + o2::framework::ConfigurableAxis massOmega{"massOmega", {{400, 1.4f, 1.8f}}, "mass for antiparticle hypothesis"}; +}; + +constexpr const char PrefixXiQaBinning[] = "XiQaBinning"; +using ConfXiQaBinning = ConfCascadeQaBinning; + +constexpr const char PrefixOmegatQaBinning[] = "OmegaQaBinning"; +using ConfOmegaQaBinning = ConfCascadeQaBinning; + +// must be in sync with enum TrackVariables +// the enum gives the correct index in the array +constexpr std::array, kCascadeHistLast> HistTable = { + {{kPt, o2::framework::kTH1F, "hPt", "Transverse Momentum; p_{T} (GeV/#it{c}); Entries"}, + {kEta, o2::framework::kTH1F, "hEta", "Pseudorapdity; #eta; Entries"}, + {kPhi, o2::framework::kTH1F, "hPhi", "Azimuthal angle; #varphi; Entries"}, + {kMass, o2::framework::kTH1F, "hMass", "Invariant Mass; m_{Inv} (GeV/#it{c}^{2}); Entries"}, + {kSign, o2::framework::kTH1F, "hSign", "Sign (-1 -> antiparticle, 0 -> self conjugate, +1 -> particle); sign; Entries"}, + {kMassXi, o2::framework::kTH1F, "hMassXi", "Mass #Xi; m_{#Lambda#pi} (GeV/#it{c}^{2}); Entries"}, + {kMassOmega, o2::framework::kTH1F, "hMassOmega", "mass #Omega; m_{#LambdaK} (GeV/#it{c}^{2}); Entries"}, + {kCosPa, o2::framework::kTH1F, "hCosPa", "Cosine of pointing angle; coa(#alpha); Entries"}, + {kDecayDauDca, o2::framework::kTH1F, "hDauDca", "Daughter DCA at decay vertex ; DCA_{Decay vertex} (cm); Entries"}, + {kTransRadius, o2::framework::kTH1F, "hTransRadius", "Transverse radius ; r_{xy} (cm); Entries"}, + {kPtVsEta, o2::framework::kTH2F, "hPtVsEta", "p_{T} vs #eta; p_{T} (GeV/#it{c}) ; #eta"}, + {kPtVsPhi, o2::framework::kTH2F, "hPtVsPhi", "p_{T} vs #varphi; p_{T} (GeV/#it{c}) ; #varphi"}, + {kPhiVsEta, o2::framework::kTH2F, "hPhiVsEta", "#varphi vs #eta; #varphi ; #eta"}, + {kPtVsCosPa, o2::framework::kTH2F, "hPtVsCosPa", "Cosine of poiting angle vs p_{T}; cos(#alpha); p_{T} (GeV/#it{c})"}, + {kPtVsMassXi, o2::framework::kTH2F, "hPtVsMassXi", "p_{T} vs mass #Xi; p_{T} (GeV/#it{c}); m_{#Lambda#pi} (GeV/#it{c}^{2})"}, + {kPtVsMassOmega, o2::framework::kTH2F, "hPtVsMassOmega", "p_{T} vs mass #Omega; p_{T} (GeV/#it{c}); m_{#LambdaK} (GeV/#it{c}^{2})"}, + {kMassXiVsMassOmega, o2::framework::kTH2F, "hMassXiVsMassOmega", "mass #Xi vs mass #Omega; m_{#Lambda#pi} (GeV/#it{c}^{2}); m_{#LambdaK} (GeV/#it{c}^{2})"}}}; + +template +auto makeCascadeAnalysisHistSpecMap(const T& confBinningAnalysis) +{ + return std::map>{ + {kPt, {confBinningAnalysis.pt}}, + {kEta, {confBinningAnalysis.eta}}, + {kPhi, {confBinningAnalysis.phi}}, + {kMass, {confBinningAnalysis.mass}}, + {kSign, {confBinningAnalysis.sign}}}; +} + +template +std::map> makeCascadeQaHistSpecMap(T1 const& confBinningAnalysis, T2 const& confBinningQa) +{ + return std::map>{ + {kPt, {confBinningAnalysis.pt}}, + {kEta, {confBinningAnalysis.eta}}, + {kPhi, {confBinningAnalysis.phi}}, + {kMass, {confBinningAnalysis.mass}}, + {kSign, {confBinningAnalysis.sign}}, + {kCosPa, {confBinningQa.cosPa}}, + {kDecayDauDca, {confBinningQa.dauDcaAtDecay}}, + {kTransRadius, {confBinningQa.transRadius}}, + {kPtVsEta, {confBinningAnalysis.pt, confBinningAnalysis.eta}}, + {kPtVsPhi, {confBinningAnalysis.pt, confBinningAnalysis.phi}}, + {kPhiVsEta, {confBinningAnalysis.phi, confBinningAnalysis.eta}}, + {kPtVsCosPa, {confBinningAnalysis.pt, confBinningQa.cosPa}}, + {kMassXi, {confBinningQa.massXi}}, + {kMassOmega, {confBinningQa.massOmega}}, + {kPtVsMassXi, {confBinningAnalysis.pt, confBinningQa.massXi}}, + {kPtVsMassOmega, {confBinningAnalysis.pt, confBinningQa.massOmega}}, + {kMassXiVsMassOmega, {confBinningQa.massXi, confBinningQa.massOmega}}}; +}; + +constexpr char PrefixXiQa[] = "XiQA/"; +constexpr char PrefixXi[] = "Xi/"; +constexpr char PrefixOmegaQa[] = "OmegaQa/"; +constexpr char PrefixOmega[] = "Omega/"; + +constexpr char PrefixLambdaCascade[] = "LambdaCascadeQa/"; + +constexpr std::string_view AnalysisDir = "Kinematics/"; +constexpr std::string_view QaDir = "QA/"; + +/// \class FemtoDreamEventHisto +/// \brief Class for histogramming event properties +// template +template +class CascadeHistManager +{ + public: + /// Destructor + virtual ~CascadeHistManager() = default; + /// Initializes histograms for the task + /// \param registry Histogram registry to be passed + /// + void init(o2::framework::HistogramRegistry* registry, + std::map> cascadeSpecs, + std::map> BachelorSpecs, + std::map> PosDauSpecs, + std::map> NegDauSpecs) + { + mHistogramRegistry = registry; + mBachelorManager.init(registry, BachelorSpecs); + mPosDauManager.init(registry, PosDauSpecs); + mNegDauManager.init(registry, NegDauSpecs); + if constexpr (modes::isFlagSet(mode, modes::Mode::kAnalysis)) { + std::string analysisDir = std::string(cascadePrefix) + std::string(AnalysisDir); + mHistogramRegistry->add(analysisDir + GetHistNamev2(kPt, HistTable), GetHistDesc(kPt, HistTable), GetHistType(kPt, HistTable), {cascadeSpecs[kPt]}); + mHistogramRegistry->add(analysisDir + GetHistNamev2(kEta, HistTable), GetHistDesc(kEta, HistTable), GetHistType(kEta, HistTable), {cascadeSpecs[kEta]}); + mHistogramRegistry->add(analysisDir + GetHistNamev2(kPhi, HistTable), GetHistDesc(kPhi, HistTable), GetHistType(kPhi, HistTable), {cascadeSpecs[kPhi]}); + mHistogramRegistry->add(analysisDir + GetHistNamev2(kMass, HistTable), GetHistDesc(kMass, HistTable), GetHistType(kMass, HistTable), {cascadeSpecs[kMass]}); + mHistogramRegistry->add(analysisDir + GetHistNamev2(kSign, HistTable), GetHistDesc(kSign, HistTable), GetHistType(kSign, HistTable), {cascadeSpecs[kSign]}); + } + + if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { + std::string qaDir = std::string(cascadePrefix) + std::string(QaDir); + + mHistogramRegistry->add(qaDir + GetHistNamev2(kCosPa, HistTable), GetHistDesc(kCosPa, HistTable), GetHistType(kCosPa, HistTable), {cascadeSpecs[kCosPa]}); + mHistogramRegistry->add(qaDir + GetHistNamev2(kDecayDauDca, HistTable), GetHistDesc(kDecayDauDca, HistTable), GetHistType(kDecayDauDca, HistTable), {cascadeSpecs[kDecayDauDca]}); + mHistogramRegistry->add(qaDir + GetHistNamev2(kTransRadius, HistTable), GetHistDesc(kTransRadius, HistTable), GetHistType(kTransRadius, HistTable), {cascadeSpecs[kTransRadius]}); + + // qa 2d + mHistogramRegistry->add(qaDir + GetHistNamev2(kPtVsEta, HistTable), GetHistDesc(kPtVsEta, HistTable), GetHistType(kPtVsEta, HistTable), {cascadeSpecs[kPtVsEta]}); + mHistogramRegistry->add(qaDir + GetHistNamev2(kPtVsPhi, HistTable), GetHistDesc(kPtVsPhi, HistTable), GetHistType(kPtVsPhi, HistTable), {cascadeSpecs[kPtVsPhi]}); + mHistogramRegistry->add(qaDir + GetHistNamev2(kPhiVsEta, HistTable), GetHistDesc(kPhiVsEta, HistTable), GetHistType(kPhiVsEta, HistTable), {cascadeSpecs[kPhiVsEta]}); + mHistogramRegistry->add(qaDir + GetHistNamev2(kPtVsCosPa, HistTable), GetHistDesc(kPtVsCosPa, HistTable), GetHistType(kPtVsCosPa, HistTable), {cascadeSpecs[kPtVsCosPa]}); + + mHistogramRegistry->add(qaDir + GetHistNamev2(kMassXi, HistTable), GetHistDesc(kMassXi, HistTable), GetHistType(kMassXi, HistTable), {cascadeSpecs[kMassXi]}); + mHistogramRegistry->add(qaDir + GetHistNamev2(kMassOmega, HistTable), GetHistDesc(kMassOmega, HistTable), GetHistType(kMassOmega, HistTable), {cascadeSpecs[kMassOmega]}); + mHistogramRegistry->add(qaDir + GetHistNamev2(kPtVsMassXi, HistTable), GetHistDesc(kPtVsMassXi, HistTable), GetHistType(kPtVsMassXi, HistTable), {cascadeSpecs[kPtVsMassXi]}); + mHistogramRegistry->add(qaDir + GetHistNamev2(kPtVsMassOmega, HistTable), GetHistDesc(kPtVsMassOmega, HistTable), GetHistType(kPtVsMassOmega, HistTable), {cascadeSpecs[kPtVsMassOmega]}); + mHistogramRegistry->add(qaDir + GetHistNamev2(kMassXiVsMassOmega, HistTable), GetHistDesc(kMassXiVsMassOmega, HistTable), GetHistType(kMassXiVsMassOmega, HistTable), {cascadeSpecs[kMassXiVsMassOmega]}); + } + } + + template + void fill(T1 const& cascadeCandidate, T2 const& /*tracks*/) + { + + auto bachelor = cascadeCandidate.template bachelor_as(); + mBachelorManager.fill(bachelor); + auto posDaughter = cascadeCandidate.template posDau_as(); + mPosDauManager.fill(posDaughter); + auto negDaughter = cascadeCandidate.template negDau_as(); + mNegDauManager.fill(negDaughter); + + if constexpr (modes::isFlagSet(mode, modes::Mode::kAnalysis)) { + mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(AnalysisDir) + HIST(GetHistName(kPt, HistTable)), cascadeCandidate.pt()); + mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(AnalysisDir) + HIST(GetHistName(kEta, HistTable)), cascadeCandidate.eta()); + mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(AnalysisDir) + HIST(GetHistName(kPhi, HistTable)), cascadeCandidate.phi()); + mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(AnalysisDir) + HIST(GetHistName(kMass, HistTable)), cascadeCandidate.mass()); + mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(AnalysisDir) + HIST(GetHistName(kSign, HistTable)), cascadeCandidate.sign()); + } + if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { + float massXi, massOmega; + if constexpr (modes::isEqual(cascade, modes::Cascade::kXi)) { + massXi = cascadeCandidate.mass(); + massOmega = cascadeCandidate.massOmega(); + } + if constexpr (modes::isEqual(cascade, modes::Cascade::kOmega)) { + massXi = cascadeCandidate.massXi(); + massOmega = cascadeCandidate.mass(); + } + mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(QaDir) + HIST(GetHistName(kCosPa, HistTable)), cascadeCandidate.cascadeCosPa()); + mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(QaDir) + HIST(GetHistName(kDecayDauDca, HistTable)), cascadeCandidate.cascadeDauDca()); + mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(QaDir) + HIST(GetHistName(kTransRadius, HistTable)), cascadeCandidate.cascadeTransRadius()); + mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(QaDir) + HIST(GetHistName(kPtVsEta, HistTable)), cascadeCandidate.pt(), cascadeCandidate.eta()); + mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(QaDir) + HIST(GetHistName(kPtVsPhi, HistTable)), cascadeCandidate.pt(), cascadeCandidate.phi()); + mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(QaDir) + HIST(GetHistName(kPhiVsEta, HistTable)), cascadeCandidate.phi(), cascadeCandidate.eta()); + mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(QaDir) + HIST(GetHistName(kPtVsCosPa, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); + mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(QaDir) + HIST(GetHistName(kMassXi, HistTable)), massXi); + mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(QaDir) + HIST(GetHistName(kMassOmega, HistTable)), massOmega); + mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(QaDir) + HIST(GetHistName(kPtVsMassXi, HistTable)), cascadeCandidate.pt(), massXi); + mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(QaDir) + HIST(GetHistName(kPtVsMassOmega, HistTable)), cascadeCandidate.pt(), massOmega); + mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(QaDir) + HIST(GetHistName(kMassXiVsMassOmega, HistTable)), massXi, massOmega); + } + } + + private: + o2::framework::HistogramRegistry* mHistogramRegistry; + + trackhistmanager::TrackHistManager mBachelorManager; + trackhistmanager::TrackHistManager mPosDauManager; + trackhistmanager::TrackHistManager mNegDauManager; +}; +}; // namespace cascadehistmanager +}; // namespace o2::analysis::femto +#endif // PWGCF_FEMTO_CORE_CASCADEHISTMANAGER_H_ diff --git a/PWGCF/Femto/Core/collisionBuilder.h b/PWGCF/Femto/Core/collisionBuilder.h new file mode 100644 index 00000000000..a11a4071721 --- /dev/null +++ b/PWGCF/Femto/Core/collisionBuilder.h @@ -0,0 +1,358 @@ +// Copyright 2019-2022 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 collisionBuilder.h +/// \brief collision builder +/// \author Anton Riedel, TU München, anton.riedel@cern.ch + +#ifndef PWGCF_FEMTO_CORE_COLLISIONBUILDER_H_ +#define PWGCF_FEMTO_CORE_COLLISIONBUILDER_H_ + +#include "PWGCF/Femto/Core/femtoUtils.h" +#include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/DataModel/FemtoTables.h" + +#include "Common/CCDB/EventSelectionParams.h" + +#include "Framework/AnalysisHelpers.h" +#include "Framework/Configurable.h" + +#include "fairlogger/Logger.h" + +#include +#include + +namespace o2::analysis::femto +{ +namespace collisionbuilder +{ + +// configurables for collision selection +struct ConfCollisionFilter : o2::framework::ConfigurableGroup { + std::string prefix = std::string("CollisionFilter"); + o2::framework::Configurable vtxZMin{"vtxZMin", -10.f, "Minimum vertex Z position (cm)"}; + o2::framework::Configurable vtxZMax{"vtxZMax", 10.f, "Maximum vertex Z position (cm)"}; + o2::framework::Configurable multMin{"multMin", 0.f, "Minimum multiplicity"}; + o2::framework::Configurable multMax{"multMax", 999.f, "Maximum multiplicity"}; + o2::framework::Configurable centMin{"centMin", 0.f, "Minimum centrality (multiplicity percentile)"}; + o2::framework::Configurable centMax{"centMax", 999.f, "Maximum centrality (multiplicity percentile)"}; + o2::framework::Configurable spherMin{"spherMin", 0.f, "Minimum centrality (multiplicity percentile)"}; + o2::framework::Configurable spherMax{"spherMax", 2.f, "Maximum centrality (multiplicity percentile)"}; + o2::framework::Configurable occupancyMin{"occupancyMin", 0.f, "Minimum occupancy (Cut works at producer level and if occupancy is stored also at consumer level)"}; + o2::framework::Configurable occupancyMax{"occupancyMax", 1e6f, "Maximum occupancy (Cut works at producer level and if occupancy is stored also at consumer level)"}; + o2::framework::Configurable magFieldMin{"magFieldMin", -1.f, "Minimum magnetic field strength (T)"}; + o2::framework::Configurable magFieldMax{"magFieldMax", 1.f, "Maximum magnetic field strength (T)"}; +}; + +struct ConfCollisionFlags : o2::framework::ConfigurableGroup { + o2::framework::Configurable sel8{"sel8", true, "Use sel8 (Should suffice for pp, for PbPb the other flags are more important)"}; + o2::framework::Configurable noSameBunchPileup{"noSameBunchPileup", false, "Reject collisions in case of pileup with another collision in the same foundBC"}; + o2::framework::Configurable isVertexItsTpc{"isVertexItsTpc", false, "At least one ITS-TPC track found for the vertex"}; + o2::framework::Configurable isGoodZvtxFt0VsPv{"isGoodZvtxFt0VsPv", false, "small difference between z-vertex from PV and from FT0"}; + o2::framework::Configurable noCollInTimeRangeNarrow{"noCollInTimeRangeNarrow", false, "no other collisions in specified time range (narrower than Strict)"}; + o2::framework::Configurable noCollInTimeRangeStrict{"noCollInTimeRangeStrict", false, "no other collisions in specified time range"}; + o2::framework::Configurable noCollInTimeRangeStandard{"noCollInTimeRangeStandard", false, "no other collisions in specified time range with per-collision multiplicity above threshold"}; + o2::framework::Configurable noCollInRofStrict{"noCollInRofStrict", false, "no other collisions in this Readout Frame"}; + o2::framework::Configurable noCollInRofStandard{"noCollInRofStandard", false, "no other collisions in this Readout Frame with per-collision multiplicity above threshold"}; + o2::framework::Configurable noHighMultCollInPrevRof{"noHighMultCollInPrevRof", false, "veto an event if FT0C amplitude in previous ITS ROF is above threshold"}; + o2::framework::Configurable isGoodItsLayer3{"isGoodItsLayer3", false, "number of inactive chips on ITS layer 3 is below maximum allowed value"}; + o2::framework::Configurable isGoodItsLayer0123{"isGoodItsLayer0123", false, "numbers of inactive chips on ITS layers 0-3 are below maximum allowed values"}; + o2::framework::Configurable isGoodItsLayersAll{"isGoodItsLayersAll", false, "numbers of inactive chips on all ITS layers are below maximum allowed values"}; +}; + +/// \class FemtoDreamTrackCuts +/// \brief Cut class to contain and execute all cuts applied to tracks +class CollisionSelection +{ + public: + CollisionSelection() {} + virtual ~CollisionSelection() = default; + + template + void configure(T1 const& filter, T2 const& flags) + { + // flags + mSel8 = flags.sel8.value; + mNoSameBunchPileup = flags.noSameBunchPileup.value; + mIsVertexItsTpc = flags.isVertexItsTpc.value; + mIsGoodZvtxFt0VsPv = flags.isGoodZvtxFt0VsPv.value; + mNoCollInTimeRangeNarrow = flags.noCollInTimeRangeNarrow.value; + mNoCollInTimeRangeStrict = flags.noCollInTimeRangeStrict.value; + mNoCollInTimeRangeStandard = flags.noCollInTimeRangeStandard.value; + mNoCollInRofStrict = flags.noCollInRofStrict.value; + mNoCollInRofStandard = flags.noCollInRofStandard.value; + mNoHighMultCollInPrevRof = flags.noHighMultCollInPrevRof.value; + mIsGoodItsLayer3 = flags.isGoodItsLayer3.value; + mIsGoodItsLayer0123 = flags.isGoodItsLayer0123.value; + mIsGoodItsLayersAll = flags.isGoodItsLayersAll.value; + + // cuts + mVtxZMin = filter.vtxZMin.value; + mVtxZMax = filter.vtxZMax.value; + mMagFieldMin = filter.magFieldMin.value; + mMagFieldMax = filter.magFieldMax.value; + mMultMin = filter.multMin.value; + mMultMax = filter.multMax.value; + mCentMin = filter.centMin.value; + mCentMax = filter.centMax.value; + mSphericityMin = filter.spherMin.value; + mSphericityMax = filter.spherMax.value; + mOccupancyMin = filter.occupancyMin.value; + mOccupancyMax = filter.occupancyMax.value; + }; + + void setMagneticField(float MagField) + { + mMagField = MagField; + } + + float getMagneticField() + { + return mMagField; + } + + template + void setSphericity(T tracks) + { + mSphericity = utils::sphericity(tracks); + } + + float getSphericity() const { return mSphericity; } + + template + void setCentrality(const T& col) + { + if constexpr (modes::isFlagSet(system, modes::System::kPP)) { + mCentrality = col.centFT0M(); + } + if constexpr (modes::isFlagSet(system, modes::System::kPbPb)) { + mCentrality = col.centFT0C(); + } + } + float getCentrality() const { return mCentrality; } + + template + bool checkCuts(T const& col) + { + + // flags + if (mSel8 && !col.sel8()) { // might change in the future, but sel8 checks TVXTrigger and cuts out time frame border and ITS readout frame border + return false; + } + if (mNoSameBunchPileup && !col.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + return false; + } + if (mIsVertexItsTpc && !col.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) { + return false; + } + if (mIsGoodZvtxFt0VsPv && !col.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) { + return false; + } + if (mNoCollInTimeRangeNarrow && !col.selection_bit(aod::evsel::kNoCollInTimeRangeNarrow)) { + return false; + } + if (mNoCollInTimeRangeStrict && !col.selection_bit(aod::evsel::kNoCollInTimeRangeStrict)) { + return false; + } + if (mNoCollInTimeRangeStandard && !col.selection_bit(aod::evsel::kNoCollInTimeRangeStandard)) { + return false; + } + if (mNoCollInRofStrict && !col.selection_bit(aod::evsel::kNoCollInRofStrict)) { + return false; + } + if (mNoCollInRofStandard && !col.selection_bit(aod::evsel::kNoCollInRofStandard)) { + return false; + } + if (mNoHighMultCollInPrevRof && !col.selection_bit(aod::evsel::kNoHighMultCollInPrevRof)) { + return false; + } + if (mIsGoodItsLayer3 && !col.selection_bit(aod::evsel::kIsGoodITSLayer3)) { + return false; + } + if (mIsGoodItsLayer0123 && !col.selection_bit(aod::evsel::kIsGoodITSLayer0123)) { + return false; + } + if (mIsGoodItsLayersAll && !col.selection_bit(aod::evsel::kIsGoodITSLayersAll)) { + return false; + } + + // cuts + if (col.posZ() < mVtxZMin || col.posZ() > mVtxZMax) { + return false; + } + if (col.multNTracksPV() < mMultMin || col.multNTracksPV() > mMultMax) { + return false; + } + if (mCentrality < mCentMin || mCentrality > mCentMax) { + return false; + } + if (mMagField < mMagFieldMin || mMagField > mMagFieldMax) { + return false; + } + if (mSphericity < mSphericityMin || mSphericity > mSphericityMax) { + return false; + } + return true; + } + + private: + float mCentrality = 0.f; + + // flags + bool mSel8 = false; + bool mNoSameBunchPileup = false; + bool mIsVertexItsTpc = false; + bool mIsGoodZvtxFt0VsPv = false; + bool mNoCollInTimeRangeNarrow = false; + bool mNoCollInTimeRangeStrict = false; + bool mNoCollInTimeRangeStandard = false; + bool mNoCollInRofStrict = false; + bool mNoCollInRofStandard = false; + bool mNoHighMultCollInPrevRof = false; + bool mIsGoodItsLayer3 = false; + bool mIsGoodItsLayer0123 = false; + bool mIsGoodItsLayersAll = false; + + // cuts + float mVtxZMin = -12.f; + float mVtxZMax = -12.f; + float mSphericityMin = 0.f; + float mSphericityMax = 2.f; + float mMagFieldMin = -1.f; + float mMagFieldMax = 1.f; + float mMultMin = 0.f; + float mMultMax = 999.f; + float mCentMin = 0.f; + float mCentMax = 999.f; + float mOccupancyMin = 0.; + float mOccupancyMax = 1e6f; + + float mMagField = 0.f; + float mSphericity = 0.f; +}; + +struct CollisionBuilderProducts : o2::framework::ProducesGroup { + o2::framework::Produces producedCollision; + o2::framework::Produces producedOccupancy; + o2::framework::Produces producedQns; + o2::framework::Produces producedPositions; + o2::framework::Produces producedMultiplicityEstimators; + o2::framework::Produces producedCentralityEstimators; +}; + +struct ConfCollisionTables : o2::framework::ConfigurableGroup { + std::string prefix = std::string("CollisionTables"); + o2::framework::Configurable produceCollisions{"produceCollisions", -1, "Produce Collisions (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceOccupancy{"produceOccupancy", -1, "Produce Occupancy (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceQns{"produceQns", -1, "Produce Qn (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable producePositions{"producePositions", -1, "Produce Positions (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceMults{"produceMults", -1, "Produce Multiplicities (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceCents{"produceCents", -1, "Produce Centralities (-1: auto; 0 off; 1 on)"}; +}; + +class CollisionBuilder +{ + public: + CollisionBuilder() {} + virtual ~CollisionBuilder() = default; + + template + void init(T1& filter, T2& flags, T3& table, T4& initContext) + { + collisionSelection.configure(filter, flags); + LOG(info) << "Initialize femto collision builder..."; + producedCollisions = utils::enableTable("FCols_001", table.produceCollisions.value, initContext); + produceOccupancy = utils::enableTable("FColOccs_001", table.produceOccupancy.value, initContext); + produceQns = utils::enableTable("FColQnBins_001", table.produceQns.value, initContext); + producedPositions = utils::enableTable("FColPos_001", table.producePositions.value, initContext); + producedMultiplicities = utils::enableTable("FColMults_001", table.produceMults.value, initContext); + producedCentralities = utils::enableTable("FColCents_001", table.produceCents.value, initContext); + if (producedCollisions || producedPositions || producedMultiplicities || producedCentralities) { + fillAnyTable = true; + } else { + LOG(info) << "No tables configured"; + } + LOG(info) << "Initialization done..."; + } + + template + void buildCollision(T1& col, T2 tracks, float magField) + { + collisionSelection.setMagneticField(magField); + collisionSelection.setSphericity(tracks); + collisionSelection.setCentrality(col); + } + + template + bool checkCuts(T const& col) + { + return collisionSelection.checkCuts(col); + } + + template + void fillCollision(T1& collisionProducts, T2 const& col) + { + if (!fillAnyTable) { + return; + } + if (producedCollisions) { + collisionProducts.producedCollision(col.posZ(), + col.multNTracksPV(), + collisionSelection.getCentrality(), + collisionSelection.getSphericity(), + collisionSelection.getMagneticField()); + } + + if (produceOccupancy) { + collisionProducts.producedOccupancy(col.trackOccupancyInTimeRange()); + } + + if (producedPositions) { + collisionProducts.producedPositions(col.posX(), + col.posY()); + } + if (producedMultiplicities) { + collisionProducts.producedMultiplicityEstimators( + col.multFT0A(), + col.multFT0C(), + col.multNTracksPVeta1(), + col.multNTracksPVetaHalf(), + col.trackOccupancyInTimeRange(), + col.ft0cOccupancyInTimeRange()); + } + if (producedCentralities) { + collisionProducts.producedCentralityEstimators( + col.centFT0A(), + col.centFT0C()); + } + + // PbPb specific columns + if constexpr (modes::isFlagSet(system, modes::System::kPbPb)) { + if (produceQns) { + collisionProducts.producedQns(utils::qn(col)); + } + } + } + + private: + CollisionSelection collisionSelection; + bool fillAnyTable = false; + bool producedCollisions = false; + bool produceOccupancy = false; + bool produceQns = false; + bool producedPositions = false; + bool producedMultiplicities = false; + bool producedCentralities = false; +}; +}; // namespace collisionbuilder +}; // namespace o2::analysis::femto +; +#endif // PWGCF_FEMTO_CORE_COLLISIONBUILDER_H_ diff --git a/PWGCF/Femto/Core/collisionHistManager.h b/PWGCF/Femto/Core/collisionHistManager.h new file mode 100644 index 00000000000..83c9d1ef64b --- /dev/null +++ b/PWGCF/Femto/Core/collisionHistManager.h @@ -0,0 +1,153 @@ +// Copyright 2019-2022 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 collisionHistManager.h +/// \brief collision histogram manager +/// \author anton.riedel@tum.de, TU München, anton.riedel@tum.de + +#ifndef PWGCF_FEMTO_CORE_COLLISIONHISTMANAGER_H_ +#define PWGCF_FEMTO_CORE_COLLISIONHISTMANAGER_H_ + +#include "PWGCF/Femto/Core/histManager.h" +#include "PWGCF/Femto/Core/modes.h" + +#include "Framework/Configurable.h" +#include "Framework/HistogramRegistry.h" +#include "Framework/HistogramSpec.h" + +#include +#include +#include +#include +#include + +namespace o2::analysis::femto +{ +namespace colhistmanager +{ + +enum ColHist { + kPosz, + kMult, + kCent, + kMagField, + kSphericity, + // 2d qa + kPoszVsMult, + kPoszVsCent, + kCentVsMult, + kCentVsSphericity, + kMultVsSphericity, + kColHistLast +}; + +constexpr std::string_view ColAnalysisDir = "Collisions/Analysis/"; +constexpr std::string_view ColQaDir = "Collisions/QA/"; + +constexpr std::array, kColHistLast> HistTable = { + { + {kPosz, o2::framework::kTH1F, "hPosz", "Vertex Z; V_{Z} (cm); Entries"}, + {kMult, o2::framework::kTH1F, "hMult", "Multiplicity; Multiplicity; Entries"}, + {kCent, o2::framework::kTH1F, "hCent", "Centrality; Centrality (%); Entries"}, + {kMagField, o2::framework::kTH1F, "hMagField", "Magnetic Field; B (T); Entries"}, + {kSphericity, o2::framework::kTH1F, "hSphericity", "Sphericity; Sphericity; Entries"}, + {kPoszVsMult, o2::framework::kTH2F, "hPoszVsMult", "Vertex Z vs Multiplicity; V_{Z} (cm); Multiplicity"}, + {kPoszVsCent, o2::framework::kTH2F, "hPoszVsCent", "Vertex Z vs Centrality; V_{Z} (cm); Centrality (%)"}, + {kCentVsMult, o2::framework::kTH2F, "hCentVsMult", "Centrality vs Multiplicity; Centrality (%); Multiplicity"}, + {kMultVsSphericity, o2::framework::kTH2F, "hMultVsSphericity", "Multiplicity vs Sphericity; Multiplicity; Sphericity"}, + {kCentVsSphericity, o2::framework::kTH2F, "hCentVsSphericity", "Centrality vs Sphericity; Centrality (%); Sphericity"}, + }}; + +template +auto makeColHistSpecMap(const BinningStruct& binning) +{ + return std::map>{ + {kPosz, {binning.vtZ}}, + {kMult, {binning.mult}}, + {kCent, {binning.cent}}, + {kSphericity, {binning.spher}}, + {kMagField, {binning.magField}}, + {kPoszVsMult, {binning.vtZ, binning.mult}}, + {kPoszVsCent, {binning.vtZ, binning.cent}}, + {kCentVsMult, {binning.cent, binning.mult}}, + {kMultVsSphericity, {binning.mult, binning.spher}}, + {kCentVsSphericity, {binning.cent, binning.spher}}}; +} + +struct ConfCollisionBinning : o2::framework::ConfigurableGroup { + std::string prefix = std::string("CollisionBinning"); + o2::framework::ConfigurableAxis vtZ{"vtZ", {200, -10, 10}, "Vertex Z binning"}; + o2::framework::ConfigurableAxis mult{"mult", {200, 0, 200}, "Multiplicity binning"}; + o2::framework::ConfigurableAxis cent{"cent", {100, 0.0f, 100.0f}, "Centrality (multiplicity percentile) binning"}; + o2::framework::ConfigurableAxis spher{"spher", {200, 0.0f, 2.0f}, "Sphericity binning"}; + o2::framework::ConfigurableAxis magField{"magField", {2, -1, 1}, "Magnetic field binning"}; +}; + +/// \class FemtoDreamEventHisto +/// \brief Class for histogramming event properties +template +class CollisionHistManager +{ + public: + /// Destructor + virtual ~CollisionHistManager() = default; + /// Initializes histograms for the task + /// \param registry Histogram registry to be passed + void init(o2::framework::HistogramRegistry* registry, std::map> Specs) + { + mHistogramRegistry = registry; + if constexpr (isFlagSet(mode, modes::Mode::kAnalysis)) { + std::string analysisDir = std::string(ColAnalysisDir); + mHistogramRegistry->add(analysisDir + GetHistNamev2(kPosz, HistTable), GetHistDesc(kPosz, HistTable), GetHistType(kPosz, HistTable), {Specs[kPosz]}); + mHistogramRegistry->add(analysisDir + GetHistNamev2(kMult, HistTable), GetHistDesc(kMult, HistTable), GetHistType(kMult, HistTable), {Specs[kMult]}); + mHistogramRegistry->add(analysisDir + GetHistNamev2(kCent, HistTable), GetHistDesc(kCent, HistTable), GetHistType(kCent, HistTable), {Specs[kCent]}); + mHistogramRegistry->add(analysisDir + GetHistNamev2(kSphericity, HistTable), GetHistDesc(kSphericity, HistTable), GetHistType(kSphericity, HistTable), {Specs[kSphericity]}); + mHistogramRegistry->add(analysisDir + GetHistNamev2(kMagField, HistTable), GetHistDesc(kMagField, HistTable), GetHistType(kMagField, HistTable), {Specs[kMagField]}); + mHistogramRegistry->add(analysisDir + GetHistNamev2(kPoszVsMult, HistTable), GetHistDesc(kPoszVsMult, HistTable), GetHistType(kPoszVsMult, HistTable), {Specs[kPoszVsMult]}); + mHistogramRegistry->add(analysisDir + GetHistNamev2(kPoszVsCent, HistTable), GetHistDesc(kPoszVsCent, HistTable), GetHistType(kPoszVsCent, HistTable), {Specs[kPoszVsCent]}); + mHistogramRegistry->add(analysisDir + GetHistNamev2(kCentVsMult, HistTable), GetHistDesc(kCentVsMult, HistTable), GetHistType(kCentVsMult, HistTable), {Specs[kCentVsMult]}); + mHistogramRegistry->add(analysisDir + GetHistNamev2(kMultVsSphericity, HistTable), GetHistDesc(kMultVsSphericity, HistTable), GetHistType(kMultVsSphericity, HistTable), {Specs[kMultVsSphericity]}); + mHistogramRegistry->add(analysisDir + GetHistNamev2(kCentVsSphericity, HistTable), GetHistDesc(kCentVsSphericity, HistTable), GetHistType(kCentVsSphericity, HistTable), {Specs[kCentVsSphericity]}); + } + + if constexpr (isFlagSet(mode, modes::Mode::kQa)) { + std::string qaDir = std::string(ColQaDir); + // to be implemented + } + } // namespace o2::analysis::femtounited + + template + void fill(T const& col) + { + if constexpr (isFlagSet(mode, modes::Mode::kAnalysis)) { + mHistogramRegistry->fill(HIST(ColAnalysisDir) + HIST(GetHistName(kPosz, HistTable)), col.posZ()); + mHistogramRegistry->fill(HIST(ColAnalysisDir) + HIST(GetHistName(kMult, HistTable)), col.mult()); + mHistogramRegistry->fill(HIST(ColAnalysisDir) + HIST(GetHistName(kCent, HistTable)), col.cent()); + mHistogramRegistry->fill(HIST(ColAnalysisDir) + HIST(GetHistName(kSphericity, HistTable)), col.sphericity()); + mHistogramRegistry->fill(HIST(ColAnalysisDir) + HIST(GetHistName(kMagField, HistTable)), col.magField()); + mHistogramRegistry->fill(HIST(ColAnalysisDir) + HIST(GetHistName(kPoszVsMult, HistTable)), col.posZ(), col.mult()); + mHistogramRegistry->fill(HIST(ColAnalysisDir) + HIST(GetHistName(kPoszVsCent, HistTable)), col.posZ(), col.cent()); + mHistogramRegistry->fill(HIST(ColAnalysisDir) + HIST(GetHistName(kCentVsMult, HistTable)), col.cent(), col.mult()); + mHistogramRegistry->fill(HIST(ColAnalysisDir) + HIST(GetHistName(kMultVsSphericity, HistTable)), col.mult(), col.sphericity()); + mHistogramRegistry->fill(HIST(ColAnalysisDir) + HIST(GetHistName(kCentVsSphericity, HistTable)), col.cent(), col.sphericity()); + } + + if constexpr (isFlagSet(mode, modes::Mode::kQa)) { + // to be implemented + } + } + + private: + o2::framework::HistogramRegistry* mHistogramRegistry; +}; // namespace femtounitedcolhistmanager +}; // namespace colhistmanager +}; // namespace o2::analysis::femto +#endif // PWGCF_FEMTO_CORE_COLLISIONHISTMANAGER_H_ diff --git a/PWGCF/Femto/Core/dataTypes.h b/PWGCF/Femto/Core/dataTypes.h new file mode 100644 index 00000000000..ade9456f068 --- /dev/null +++ b/PWGCF/Femto/Core/dataTypes.h @@ -0,0 +1,51 @@ +// 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 dataTypes.h +/// \brief datatypes for bitmasks +/// \author anton.riedel@tum.de, TU München, anton.riedel@tum.de + +#ifndef PWGCF_FEMTO_CORE_DATATYPES_H_ +#define PWGCF_FEMTO_CORE_DATATYPES_H_ + +#include + +namespace o2::aod +{ +namespace femtodatatypes +{ +// Note: Length of the bitmask is the limit of how many selections can be configured + +// datatypes for tracks +using TrackMaskType = uint64_t; +using TrackType = uint16_t; + +// datatypes for v0s +using V0MaskType = uint16_t; +using V0Type = uint8_t; + +// datatypes for two track resonances +using TwoTrackResonanceMaskType = uint32_t; +// two track resonance types +using TwoTrackResonanceType = uint8_t; + +// datatypes for cascades +using CascadeMaskType = uint16_t; +using CascadeType = uint8_t; + +// datatypes for pairs +using PairType = uint8_t; + +} // namespace femtodatatypes + +} // namespace o2::aod + +#endif // PWGCF_FEMTO_CORE_DATATYPES_H_ diff --git a/PWGCF/Femto/Core/femtoUtils.h b/PWGCF/Femto/Core/femtoUtils.h new file mode 100644 index 00000000000..10f81cf3669 --- /dev/null +++ b/PWGCF/Femto/Core/femtoUtils.h @@ -0,0 +1,189 @@ +// Copyright 2019-2022 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 Collision selection +/// \author Anton Riedel, TU München, anton.riedel@cern.ch + +#ifndef PWGCF_FEMTO_CORE_FEMTOUTILS_H_ +#define PWGCF_FEMTO_CORE_FEMTOUTILS_H_ + +#include "Common/Core/TableHelper.h" + +#include "CommonConstants/PhysicsConstants.h" +#include "Framework/InitContext.h" + +#include "TPDGCode.h" + +#include "fairlogger/Logger.h" + +#include +#include +#include +#include + +namespace o2::analysis::femto +{ +namespace utils +{ + +template +inline std::optional getIndex(const T1& index, const std::unordered_map& map) +{ + auto it = map.find(index); + if (it != map.end()) { + return it->second; + } + return std::nullopt; +} + +template +float itsSignal(T const& track) +{ + uint32_t clsizeflag = track.itsClusterSizes(); + auto clSizeLayer0 = (clsizeflag >> (0 * 4)) & 0xf; + auto clSizeLayer1 = (clsizeflag >> (1 * 4)) & 0xf; + auto clSizeLayer2 = (clsizeflag >> (2 * 4)) & 0xf; + auto clSizeLayer3 = (clsizeflag >> (3 * 4)) & 0xf; + auto clSizeLayer4 = (clsizeflag >> (4 * 4)) & 0xf; + auto clSizeLayer5 = (clsizeflag >> (5 * 4)) & 0xf; + auto clSizeLayer6 = (clsizeflag >> (6 * 4)) & 0xf; + int numLayers = 7; + int sumClusterSizes = clSizeLayer0 + clSizeLayer1 + clSizeLayer2 + clSizeLayer3 + clSizeLayer4 + clSizeLayer5 + clSizeLayer6; + float cosLamnda = 1. / std::cosh(track.eta()); + return (static_cast(sumClusterSizes) / numLayers) * cosLamnda; +}; + +template +float sphericity(T const& tracks) +{ + + int minNumberTracks = 2; + float maxSphericity = 2.f; + + if (tracks.size() <= minNumberTracks) { + return maxSphericity; + } + + // Initialize the transverse momentum tensor components + float sxx = 0.f; + float syy = 0.f; + float sxy = 0.f; + float sumPt = 0.f; + + // Loop over the tracks to compute the tensor components + for (const auto& track : tracks) { + sxx += (track.px() * track.px()) / track.pt(); + syy += (track.py() * track.py()) / track.pt(); + sxy += (track.px() * track.py()) / track.pt(); + sumPt += track.pt(); + } + sxx /= sumPt; + syy /= sumPt; + sxy /= sumPt; + + // Compute the eigenvalues (real values) + float lambda1 = ((sxx + syy) + std::sqrt((sxx + syy) * (sxx + syy) - 4 * (sxx * syy - sxy * sxy))) / 2; + float lambda2 = ((sxx + syy) - std::sqrt((sxx + syy) * (sxx + syy) - 4 * (sxx * syy - sxy * sxy))) / 2; + + if (lambda1 <= 0.f || lambda2 <= 0.f) { + return maxSphericity; + } + + // Compute sphericity + return 2.f * lambda2 / (lambda1 + lambda2); +} + +inline float getMass(int pdgCode) +{ + // use this function instead of TDatabasePDG to return masses defined in the PhysicsConstants.h header + // this approach saves a lot of memory and important partilces like deuteron are missing in TDatabasePDG anyway + float mass = 0.f; + // add new particles if necessary here + switch (std::abs(pdgCode)) { + case kPiPlus: + mass = o2::constants::physics::MassPiPlus; + break; + case kKPlus: + mass = o2::constants::physics::MassKPlus; + break; + case kProton: + mass = o2::constants::physics::MassProton; + break; + case kLambda0: + mass = o2::constants::physics::MassLambda; + break; + case o2::constants::physics::Pdg::kPhi: + mass = o2::constants::physics::MassPhi; + break; + case kRho770_0: + mass = 775.26; // not defined in O2? + break; + case kRho770Plus: + mass = 775.11; // not defined in O2? + break; + case o2::constants::physics::Pdg::kK0Star892: + mass = o2::constants::physics::MassK0Star892; + break; + case o2::constants::physics::Pdg::kLambdaCPlus: + mass = o2::constants::physics::MassLambdaCPlus; + break; + case o2::constants::physics::Pdg::kDeuteron: + mass = o2::constants::physics::MassDeuteron; + break; + case o2::constants::physics::Pdg::kTriton: + mass = o2::constants::physics::MassTriton; + break; + case o2::constants::physics::Pdg::kHelium3: + mass = o2::constants::physics::MassHelium3; + break; + default: + LOG(fatal) << "PDG code is not suppored"; + } + return mass; +} + +template +float qn(T const& col) +{ + float qn = std::sqrt(col.qvecFT0CReVec()[0] * col.qvecFT0CReVec()[0] + col.qvecFT0CImVec()[0] * col.qvecFT0CImVec()[0]) * std::sqrt(col.sumAmplFT0C()); + return qn; +} + +inline std::optional dphistar(float magfield, float radius, float sign, float pt, float phi) +{ + float arg = 0.3f * sign * magfield * radius * 0.01f / (2.f * pt); + if (std::fabs(arg) < 1.f) { + return phi - std::asin(arg); + } + return std::nullopt; +} + +inline bool enableTable(const char* tableName, int userSetting, o2::framework::InitContext& initContext) +{ + if (userSetting == 1) { + LOG(info) << "Enabled femto table (forced on): " << tableName; + return true; + } + if (userSetting == 0) { + LOG(info) << "Disabled femto table (forced off): " << tableName; + return false; + } + bool required = o2::common::core::isTableRequiredInWorkflow(initContext, tableName); + if (required) { + LOG(info) << "Enabled femto table (auto): " << tableName; + } + return required; +} + +}; // namespace utils +}; // namespace o2::analysis::femto +#endif // PWGCF_FEMTO_CORE_FEMTOUTILS_H_ diff --git a/PWGCF/Femto/Core/histManager.h b/PWGCF/Femto/Core/histManager.h new file mode 100644 index 00000000000..8c22277d642 --- /dev/null +++ b/PWGCF/Femto/Core/histManager.h @@ -0,0 +1,82 @@ +// 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 histManager.h +/// \brief common structs for histogram managers +/// \author anton.riedel@tum.de, TU München, anton.riedel@tum.de + +#ifndef PWGCF_FEMTO_CORE_HISTMANAGER_H_ +#define PWGCF_FEMTO_CORE_HISTMANAGER_H_ + +#include "Framework/HistogramSpec.h" + +#include +#include + +namespace o2::analysis::femto +{ +namespace histmanager +{ + +template +struct HistInfo { + Hist hist; + o2::framework::HistType histtype; + std::string_view histname; + std::string_view histdesc; +}; + +template +constexpr o2::framework::HistType GetHistType(EnumType variable, const ArrayType& array) +{ + for (const auto& entry : array) { + if (entry.hist == variable) { + return entry.histtype; + } + } + return o2::framework::kUndefinedHist; +} + +template +constexpr std::string_view GetHistName(EnumType variable, const ArrayType& array) +{ + for (const auto& entry : array) { + if (entry.hist == variable) { + return entry.histname; + } + } + return ""; // Return an empty string or a default name if not found +} + +template +constexpr std::string GetHistNamev2(EnumType variable, const ArrayType& array) +{ + for (const auto& entry : array) { + if (entry.hist == variable) { + return std::string(entry.histname); + } + } + return std::string(""); // Return an empty string or a default name if not found +} + +template +constexpr const char* GetHistDesc(EnumType variable, const ArrayType& array) +{ + for (const auto& entry : array) { + if (entry.hist == variable) { + return entry.histdesc.data(); + } + } + return ""; // Return an empty string or a default description if not found +} +} // namespace histmanager +} // namespace o2::analysis::femto +#endif // PWGCF_FEMTO_CORE_HISTMANAGER_H_ diff --git a/PWGCF/Femto/Core/modes.h b/PWGCF/Femto/Core/modes.h new file mode 100644 index 00000000000..83a230672ff --- /dev/null +++ b/PWGCF/Femto/Core/modes.h @@ -0,0 +1,112 @@ +// 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 modes.h +/// \brief common modes +/// \author anton.riedel@tum.de, TU München, anton.riedel@tum.de + +#ifndef PWGCF_FEMTO_CORE_MODES_H_ +#define PWGCF_FEMTO_CORE_MODES_H_ + +#include "PWGCF/Femto/Core/dataTypes.h" + +#include + +#include +#include + +namespace o2::analysis::femto +{ +namespace modes +{ + +// check if flag is set +template +constexpr bool isFlagSet(T value, T flag) +{ + using U = std::underlying_type_t; + return (static_cast(value) & static_cast(flag)) != 0; +} + +// check if flag is equal +template +constexpr bool isEqual(T lhs, T rhs) +{ + using U = std::underlying_type_t; + return static_cast(lhs) == static_cast(rhs); +} + +enum class Mode : uint32_t { + kAnalysis = BIT(0), + kQa = BIT(1), + kMc = BIT(2), + kAnalysis_Qa = kAnalysis | kQa, + kAnalysis_Mc = kAnalysis | kMc, + kAnalysis_Qa_Mc = kAnalysis | kQa | kMc, +}; + +enum class System : uint32_t { + kPP = BIT(0), + kPbPb = BIT(1), + kMC = BIT(2), + kRun3 = BIT(3), + kRun2 = BIT(4), + kNoCentCal = BIT(5), + kPP_Run3 = kPP | kRun3, + kPP_Run2 = kPP | kRun2, + kPP_NoCentCal_Run3 = kPP | kRun3 | kNoCentCal, + kPbPb_Run3 = kPbPb | kRun3, + kPbPb_Run2 = kPbPb | kRun2, +}; + +enum class Track : o2::aod::femtodatatypes::TrackType { + kPrimaryTrack, + kV0Daughter, + kCascadeBachelor, + kResonanceDaughter +}; + +enum class V0 : o2::aod::femtodatatypes::V0Type { + kLambda, + kAntiLambda, + kK0short +}; + +enum class Cascade : o2::aod::femtodatatypes::CascadeType { + kXi, + kOmega +}; + +// enum of supported resonances +enum class TwoTrackResonance : o2::aod::femtodatatypes::TwoTrackResonanceType { + kRho0, + kPhi, + kKstar0, + kKstar0Bar +}; + +enum class Pairs : o2::aod::femtodatatypes::PairType { + kTrackTrack, + kTrackV0, + kTrackResonance, + kTrackCascade +}; + +enum class TrackPairs : o2::aod::femtodatatypes::PairType { + kTrackTrack, + kTrackPosDaughter, + kTrackNegDaughter, + kTrackBachelor +}; + +}; // namespace modes +}; // namespace o2::analysis::femto +#endif // PWGCF_FEMTO_CORE_MODES_H_ diff --git a/PWGCF/Femto/Core/partitions.h b/PWGCF/Femto/Core/partitions.h new file mode 100644 index 00000000000..adbac4d2213 --- /dev/null +++ b/PWGCF/Femto/Core/partitions.h @@ -0,0 +1,113 @@ +// 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 partitions.h +/// \brief common partition definitons +/// \author anton.riedel@tum.de, TU München, anton.riedel@tum.de + +#ifndef PWGCF_FEMTO_CORE_PARTITIONS_H_ +#define PWGCF_FEMTO_CORE_PARTITIONS_H_ + +// collsion filters +#define MAKE_COLLISION_FILTER(selection) \ + (femtocollisions::posZ >= (selection).vtxZMin && femtocollisions::posZ <= (selection).vtxZMax) && \ + (femtocollisions::mult >= (selection).multMin && femtocollisions::mult <= (selection).multMax) && \ + (femtocollisions::cent >= (selection).centMin && femtocollisions::cent <= (selection).centMax) && \ + (femtocollisions::sphericity >= (selection).spherMin && femtocollisions::sphericity <= (selection).spherMax) && \ + (femtocollisions::magField >= (selection).magFieldMin && femtocollisions::magField <= (selection).magFieldMax) + +// standard track partition +#define MAKE_TRACK_PARTITION(selection) \ + ifnode(selection.sign.node() > 0, femtobase::stored::signedPt > 0.f, femtobase::stored::signedPt < 0.f) && \ + (nabs(femtobase::stored::signedPt) > selection.ptMin) && \ + (nabs(femtobase::stored::signedPt) < selection.ptMax) && \ + (femtobase::stored::eta > selection.etaMin) && \ + (femtobase::stored::eta < selection.etaMax) && \ + (femtobase::stored::phi > selection.phiMin) && \ + (femtobase::stored::phi < selection.phiMax) && \ + ifnode(nabs(femtobase::stored::signedPt) * (nexp(femtobase::stored::eta) + nexp(-1.f * femtobase::stored::eta)) / 2.f <= selection.pidThres, \ + ncheckbit(femtotracks::trackMask, selection.maskLowMomentum), \ + ncheckbit(femtotracks::trackMask, selection.maskHighMomentum)) + +// partition for phis and rhos, i.e. resonance that are their own antiparticle +#define MAKE_RESONANCE_0_PARTITON(selection) \ + (femtobase::stored::pt > selection.ptMin) && \ + (femtobase::stored::pt < selection.ptMax) && \ + (femtobase::stored::eta > selection.etaMin) && \ + (femtobase::stored::eta < selection.etaMax) && \ + (femtobase::stored::phi > selection.phiMin) && \ + (femtobase::stored::phi < selection.phiMax) && \ + (femtobase::stored::mass > selection.massMin) && \ + (femtobase::stored::mass < selection.massMax) && \ + ifnode(ncheckbit(femtotwotrackresonances::mask, selection.posDauBitForThres), \ + ncheckbit(femtotwotrackresonances::mask, selection.posDauMaskAboveThres), \ + ncheckbit(femtotwotrackresonances::mask, selection.posDauMaskBelowThres)) && \ + ifnode(ncheckbit(femtotwotrackresonances::mask, selection.negDauBitForThres), \ + ncheckbit(femtotwotrackresonances::mask, selection.negDauMaskAboveThres), \ + ncheckbit(femtotwotrackresonances::mask, selection.negDauMaskBelowThres)) + +// partition for kstars, they have distince antiparticle +#define MAKE_RESONANCE_1_PARTITON(selection) \ + ifnode(selection.sign.node() > 0, femtobase::stored::signedPt > 0.f, femtobase::stored::signedPt < 0.f) && \ + (nabs(femtobase::stored::signedPt) > selection.ptMin) && \ + (nabs(femtobase::stored::signedPt) < selection.ptMax) && \ + (femtobase::stored::eta > selection.etaMin) && \ + (femtobase::stored::eta < selection.etaMax) && \ + (femtobase::stored::phi > selection.phiMin) && \ + (femtobase::stored::phi < selection.phiMax) && \ + (femtobase::stored::mass > selection.massMin) && \ + (femtobase::stored::mass < selection.massMax) && \ + ifnode(ncheckbit(femtotwotrackresonances::mask, selection.posDauBitForThres), \ + ncheckbit(femtotwotrackresonances::mask, selection.posDauMaskAboveThres), \ + ncheckbit(femtotwotrackresonances::mask, selection.posDauMaskBelowThres)) && \ + ifnode(ncheckbit(femtotwotrackresonances::mask, selection.negDauBitForThres), \ + ncheckbit(femtotwotrackresonances::mask, selection.negDauMaskAboveThres), \ + ncheckbit(femtotwotrackresonances::mask, selection.negDauMaskBelowThres)) + +// partition for lambdas +#define MAKE_LAMBDA_PARTITION(selection) \ + ifnode(selection.sign.node() > 0, femtobase::stored::signedPt > 0.f, femtobase::stored::signedPt < 0.f) && \ + (nabs(femtobase::stored::signedPt) > selection.ptMin) && \ + (nabs(femtobase::stored::signedPt) < selection.ptMax) && \ + (femtobase::stored::eta > selection.etaMin) && \ + (femtobase::stored::eta < selection.etaMax) && \ + (femtobase::stored::phi > selection.phiMin) && \ + (femtobase::stored::phi < selection.phiMax) && \ + (femtobase::stored::mass > selection.massMin) && \ + (femtobase::stored::mass < selection.massMax) && \ + ncheckbit(femtov0s::mask, selection.mask) + +// partition for k0shorts +// need special partition since k0shorts have no antiparticle +#define MAKE_K0SHORT_PARTITION(selection) \ + (femtobase::stored::pt > selection.ptMin) && \ + (femtobase::stored::pt < selection.ptMax) && \ + (femtobase::stored::eta > selection.etaMin) && \ + (femtobase::stored::eta < selection.etaMax) && \ + (femtobase::stored::phi > selection.phiMin) && \ + (femtobase::stored::phi < selection.phiMax) && \ + (femtobase::stored::mass > selection.massMin) && \ + (femtobase::stored::mass < selection.massMax) && \ + ncheckbit(femtov0s::mask, selection.mask) + +#define MAKE_CASCADE_PARTITION(selection) \ + ifnode(selection.sign.node() > 0, femtobase::stored::signedPt > 0.f, femtobase::stored::signedPt < 0.f) && \ + (nabs(femtobase::stored::signedPt) > selection.ptMin) && \ + (nabs(femtobase::stored::signedPt) < selection.ptMax) && \ + (femtobase::stored::eta > selection.etaMin) && \ + (femtobase::stored::eta < selection.etaMax) && \ + (femtobase::stored::phi > selection.phiMin) && \ + (femtobase::stored::phi < selection.phiMax) && \ + (femtobase::stored::mass > selection.massMin) && \ + (femtobase::stored::mass < selection.massMax) && \ + ncheckbit(femtocascades::mask, selection.mask) + +#endif // PWGCF_FEMTO_CORE_PARTITIONS_H_ diff --git a/PWGCF/Femto/Core/selectionContainer.h b/PWGCF/Femto/Core/selectionContainer.h new file mode 100644 index 00000000000..8b050a4b700 --- /dev/null +++ b/PWGCF/Femto/Core/selectionContainer.h @@ -0,0 +1,342 @@ +// 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 selectionContainer.h +/// \brief Defines the SelectionContainer class for managing selection criteria in analysis. +/// \author Anton Riedel, TU München, anton.riedel@tum.de + +#ifndef PWGCF_FEMTO_CORE_SELECTIONCONTAINER_H_ +#define PWGCF_FEMTO_CORE_SELECTIONCONTAINER_H_ + +#include "CommonConstants/MathConstants.h" + +#include "TF1.h" + +#include "fairlogger/Logger.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace o2::analysis::femto +{ + +/// Limit type for selections +namespace limits +{ +enum LimitType { 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 + kUpperFunctionLimit, ///< simple upper limit of a function value, e.g. DCA_xy > f(pt) + kAbsUpperFunctionLimit, ///< upper limit of an absolute value given by a function, e.g. |DCA_xy| > f(pt) + kLowerFunctionLimit, ///< simple lower limit of a function value, e.g. DCA_xy < f(pt) + kAbsLowerFunctionLimit ///< lower limit of an absolute value given by a function, e.g. |DCA_xy| < f(pt) +}; + +std::unordered_map limitTypeAsStrings = { + {kUpperLimit, "Upper Limit"}, + {kAbsUpperLimit, "Absolute Upper Limit"}, + {kLowerLimit, "Lower Limit"}, + {kAbsLowerLimit, "Absolute Lower Limit"}, + {kEqual, "Equal"}, + {kUpperFunctionLimit, "Upper Function Limit"}, + {kAbsUpperFunctionLimit, "Absolute Upper Function Limit"}, + {kLowerFunctionLimit, "Lower Function Limit"}, + {kAbsLowerFunctionLimit, "Absolute Lower Function Limit"}}; + +}; // namespace limits + +/// \class SelectionContainer +/// \brief Class for storing and evaluating multiple selection thresholds for a single observable. +/// \tparam T Data type for selection values (mostly floats) +/// \tparam BitmaskType Type used for bitmask storage (e.g., uint8_t, uint32_t). +template +class SelectionContainer +{ + public: + /// Default constructor + SelectionContainer() {} + + /// \brief Constructor for static value-based selection. + /// \param SelectionValues Vector of values for the selection. + /// \param limitType Type of limit (from limits::LimitType). + /// \param SkipMostPermissiveBit Whether to skip the most permissive bit in the bitmask. + /// \param IsMinimalCut Whether this selection should be treated as a minimal required cut. + SelectionContainer(std::vector const& SelectionValues, limits::LimitType limitType, bool SkipMostPermissiveBit, bool IsMinimalCut) + : mSelectionValues(SelectionValues), + mLimitType(limitType), + mSkipMostPermissiveBit(SkipMostPermissiveBit), + mIsMinimalCut(IsMinimalCut) + { + if (mSelectionValues.size() > sizeof(BitmaskType) * CHAR_BIT) { + LOG(fatal) << "Too many selections for single a observable. Limit is " << sizeof(BitmaskType) * CHAR_BIT; + } + // for kEqual we can never skip the last bit + if (limitType == limits::kEqual) { + mSkipMostPermissiveBit = false; + } + // values for selection are not necessarily ordered correctly + sortSelections(); + } + + /// \brief Constructor for function-based dynamic selection. + /// \param baseName Base name for TF1 functions. + /// \param lowerLimit Lower bound for TF1 domain. + /// \param upperLimit Upper bound for TF1 domain. + /// \param functions Vector of strings defining TF1 functions. + /// \param limitType Type of limit. + /// \param skipMostPermissiveBit Whether to skip the most permissive bit in the bitmask. + /// \param IsMinimalCut Whether this selection should be treated as a minimal required cut. + SelectionContainer(std::string const& baseName, + T lowerLimit, + T upperLimit, + std::vector const& functions, + limits::LimitType limitType, + bool skipMostPermissiveBit, + bool IsMinimalCut) + : mLimitType(limitType), + mSkipMostPermissiveBit(skipMostPermissiveBit), + mIsMinimalCut(IsMinimalCut) + { + if (functions.size() > sizeof(BitmaskType) * CHAR_BIT) { + LOG(fatal) << "Too many selections for single a observable. Limit is " << sizeof(BitmaskType) * CHAR_BIT; + } + for (std::size_t i = 0; i < functions.size(); i++) { + const std::string& func = functions.at(i); + const std::string& safeFunc = func.empty() ? "0.1" : func; // in case string is empty, set to constant value of 0.1 + mSelectionFunctions.emplace_back((baseName + std::to_string(i)).c_str(), safeFunc.c_str(), lowerLimit, upperLimit); + } + // functions for selection are not necessarily ordered correctly + // use value at midpoint to order them + // here we rely on the user that the functions can be ordered like this over the whole interval + T midPoint = (lowerLimit + upperLimit) / 2.; + sortFunctions(midPoint); + // initialize the values also to the midpoint + for (std::size_t i = 0; i < functions.size(); i++) { + mSelectionValues.push_back(mSelectionFunctions.at(i).Eval(midPoint)); + } + } + + virtual ~SelectionContainer() = default; + + /// \brief Sort static selection values based on the limit type. + void sortSelections() + { + switch (mLimitType) { + case (limits::kUpperLimit): + case (limits::kAbsUpperLimit): + std::sort(mSelectionValues.begin(), mSelectionValues.end(), [](T a, T b) { return a > b; }); + break; + case (limits::kLowerLimit): + case (limits::kAbsLowerLimit): + case (limits::kEqual): + std::sort(mSelectionValues.begin(), mSelectionValues.end(), [](T a, T b) { return a < b; }); + break; + default: + break; + } + } + + /// \brief Sort selection functions based on evaluation at a given point. + /// \param value Point at which to evaluate the functions for ordering. + void sortFunctions(T value) + { + switch (mLimitType) { + case (limits::kUpperFunctionLimit): + case (limits::kAbsUpperFunctionLimit): + std::sort(mSelectionFunctions.begin(), mSelectionFunctions.end(), [value](TF1 a, TF1 b) { return a.Eval(value) > b.Eval(value); }); + break; + case (limits::kLowerFunctionLimit): + case (limits::kAbsLowerFunctionLimit): + std::sort(mSelectionFunctions.begin(), mSelectionFunctions.end(), [value](TF1 a, TF1 b) { return a.Eval(value) < b.Eval(value); }); + break; + default: + break; + } + } + + /// \brief Update selection limits using internal functions evaluated at a given value. + /// \param value Input value to evaluate functions at. + void updateLimits(T value) + { + // functions are ordered so just add the values in the same order + for (std::size_t i = 0; i < mSelectionValues.size(); i++) { + mSelectionValues.at(i) = mSelectionFunctions.at(i).Eval(value); + } + } + + /// \brief Evaluate which selection criteria are fulfilled for a given value. + /// \param value Value of the observable to evaluate. + void evaluate(T value) + { + // better safe than sorry and reset the bitmask before you evaluate and set minimal selection to true + mBitmask.reset(); + // the values are ordered, from most loose to most tight, as soon as one comparison is not true, we can break out of the loop + bool breakLoop = false; + // iterate over all limits and set the corresponding bit if we pass the selection, otherwise break out as soon as we can + // only break if the observable is used for the minimal selection + for (size_t i = 0; i < mSelectionValues.size(); i++) { + switch (mLimitType) { + case (limits::kUpperLimit): + case (limits::kUpperFunctionLimit): + if (value <= mSelectionValues.at(i)) { + mBitmask.set(i); + } else { + breakLoop = true; + } + break; + case (limits::kAbsUpperLimit): + case (limits::kAbsUpperFunctionLimit): + if (std::abs(value) <= mSelectionValues.at(i)) { + mBitmask.set(i); + } else { + breakLoop = true; + } + break; + case (limits::kLowerLimit): + case (limits::kLowerFunctionLimit): + if (value >= mSelectionValues.at(i)) { + mBitmask.set(i); + } else { + breakLoop = true; + } + break; + case (limits::kAbsLowerLimit): + case (limits::kAbsLowerFunctionLimit): + if (std::abs(value) >= mSelectionValues.at(i)) { + mBitmask.set(i); + } else { + breakLoop = true; + } + break; + case (limits::kEqual): + // special case for kEqual since here we cannot really establish an order so we need to check all cases explicitly and we cannot bail early + if (std::abs(value - mSelectionValues.at(i)) < constants::math::Epsilon) { + mBitmask.set(i); + } + break; + default: + breakLoop = true; + } + // bail early if a comparison fails + // the values are ordered, so all following we also fail, there there is no point in contiuing + if (breakLoop) { + break; + } + } + } + + /// \brief Retrieve the bitmask indicating which selections were passed. + /// \return Bitset representing passed selections. + std::bitset getBitmask() const + { + // if we do not skip the last bit, return full bitmask + // in the constructor we ensure that for kEqual we do not skip the most permissive bit + if (mSkipMostPermissiveBit == false) { + return mBitmask; + } else { + // for the other selections we can remove the first bit since it is the minimal selection and therefore always true + return mBitmask >> 1; + } + } + template + void setBitmask(R bitmask) + { + mBitmask = std::bitset(bitmask); + } + + /// \brief Check whether the minimal cut condition is fulfilled. + /// \return True if minimal selection is fulfilled, false otherwise. + bool passesAsMinimalCut() const + { + if (mIsMinimalCut) { + // check if loosest bit is set + return mBitmask.test(0); + } else { + // if selection is not marked as a minimal cut, we return true by default + return true; + } + } + + /// \brief Check whether any optional cuts are fulfilled. + /// \return True if at least one optional cut is passed. + bool passesAsOptionalCut() const + { + // if selection is marekd as minimal cut, we return false by default + if (mIsMinimalCut) { + return false; + } else { + // check if any bit is set + return mBitmask.any(); + } + } + + /// \brief Get the loosest (most permissive) selection value. + /// \return First (loosest) selection value. + T getLoosestSelection() const { return mSelectionValues.at(0); } + + /// \brief Check if there are any selection values configured. + /// \return True if no selections are configured. + bool isEmpty() const { return mSelectionValues.empty(); } + + /// \brief Get the number of bits to shift for the final bitmask. + /// \return Number of bits to shift. + int getShift() const + { + if (mSelectionValues.empty()) { + return 0; + } + if (mSkipMostPermissiveBit) { + return static_cast(mSelectionValues.size() - 1); + } else { + return static_cast(mSelectionValues.size()); + } + } + + /// \brief Get string representation of the limit type. + /// \return String name of the limit type. + std::string getLimitTypeAsString() const { return limits::limitTypeAsStrings[mLimitType]; } + + /// \brief Get a copy of all selection values. + /// \return Vector of selection values. + std::vector getSelectionValues() const { return mSelectionValues; } + + /// \brief Get a copy of all selection values. + /// \return Vector of selection values. + std::vector getSelectionFunction() const { return mSelectionFunctions; } + + /// \brief Check if this container is marked as minimal cut. + /// \return True if minimal cut, false otherwise. + bool isMinimalCut() const { return mIsMinimalCut; } + + /// \brief Check whether the most permissive bit is skipped. + /// \return True if skipped, false otherwise. + bool skipMostPermissiveBit() const { return mSkipMostPermissiveBit; } + + private: + std::vector mSelectionValues = {}; ///< Values used for the selection + std::vector mSelectionFunctions = {}; ///< Function used for the selection + limits::LimitType mLimitType; ///< Limit type of selection + std::bitset mBitmask = {}; ///< bitmask for the observable + bool mSkipMostPermissiveBit = false; ///< whether to skip the last bit or not + bool mIsMinimalCut = false; ///< whether to use this observable for minimal selection or not +}; + +} // namespace o2::analysis::femto + +#endif // PWGCF_FEMTO_CORE_SELECTIONCONTAINER_H_ diff --git a/PWGCF/Femto/Core/trackBuilder.h b/PWGCF/Femto/Core/trackBuilder.h new file mode 100644 index 00000000000..ae3331226b1 --- /dev/null +++ b/PWGCF/Femto/Core/trackBuilder.h @@ -0,0 +1,615 @@ +// 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 trackBuilder.h +/// \brief track builder +/// \author Anton Riedel, TU München, anton.riedel@cern.ch + +#ifndef PWGCF_FEMTO_CORE_TRACKBUILDER_H_ +#define PWGCF_FEMTO_CORE_TRACKBUILDER_H_ + +#include "PWGCF/Femto/Core/baseSelection.h" +#include "PWGCF/Femto/Core/dataTypes.h" +#include "PWGCF/Femto/Core/femtoUtils.h" +#include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/Core/selectionContainer.h" +#include "PWGCF/Femto/DataModel/FemtoTables.h" + +#include "CommonConstants/MathConstants.h" +#include "Framework/AnalysisHelpers.h" +#include "Framework/Configurable.h" + +#include "fairlogger/Logger.h" + +#include +#include +#include +#include +#include + +namespace o2::analysis::femto +{ +namespace trackbuilder +{ + +struct ConfTrackFilters : o2::framework::ConfigurableGroup { + std::string prefix = std::string("TrackFilters"); + // kinematic cuts for filtering tracks + o2::framework::Configurable ptMin{"ptMin", 0.2f, "Minimum pT"}; + o2::framework::Configurable ptMax{"ptMax", 6.f, "Maximum pT"}; + o2::framework::Configurable etaMin{"etaMin", -0.9f, "Minimum eta"}; + o2::framework::Configurable etaMax{"etaMax", 0.9f, "Maximum eta"}; + o2::framework::Configurable phiMin{"phiMin", 0.f, "Minimum phi"}; + o2::framework::Configurable phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"}; +}; + +struct ConfTrackBits : o2::framework::ConfigurableGroup { + std::string prefix = std::string("TrackBits"); + // track quality cuts + o2::framework::Configurable> tpcClustersMin{"tpcClustersMin", {90.f}, "Minimum number of clusters in TPC"}; + o2::framework::Configurable> tpcCrossedRowsMin{"tpcCrossedRowsMin", {80.f}, "Minimum number of crossed rows in TPC"}; + o2::framework::Configurable> tpcSharedClustersMax{"tpcSharedClustersMax", {160.f}, "Maximum number of shared clusters in TPC"}; + o2::framework::Configurable> tpcSharedClusterFractionMax{"tpcSharedClusterFractionMax", {1.f}, "Maximum fraction of shared clusters in TPC"}; + o2::framework::Configurable> itsClustersMin{"itsClustersMin", {5.f}, "Minimum number of clusters in ITS"}; + o2::framework::Configurable> itsIbClustersMin{"itsIbClustersMin", {3.f}, "Minimum number of clusters in inner barrel (max 3) of ITS"}; + o2::framework::Configurable> dcaxyMax{"dcaxyMax", {"0.004 + 0.013*pow(x, -1)"}, "Maximum |dca_xy| as a function of pT. Has to be a valid TForumal, where x=pt"}; + o2::framework::Configurable> dcazMax{"dcazMax", {"0.004 + 0.013*pow(x, -1)"}, "Maximum |dca_z| as a function of pT. Has to be a valid TForumal, where x=pt"}; + + o2::framework::Configurable minMomentumForTof{"minMomentumForTof", 2.0f, "Minimum momentum to required TOF PID (all species)"}; + + // track its pid cuts + o2::framework::Configurable> itsElectron{"itsElectron", {}, "Maximum |nsigma| for electron PID"}; + o2::framework::Configurable> itsPion{"itsPion", {}, "Maximum |nsigma| for pion PID"}; + o2::framework::Configurable> itsKaon{"itsKaon", {}, "Maximum |nsigma| for kaon PID"}; + o2::framework::Configurable> itsProton{"itsProton", {}, "Maximum |nsigma| for proton PID"}; + o2::framework::Configurable> itsDeuteron{"itsDeuteron", {}, "Maximum |nsigma| for deuteron PID"}; + o2::framework::Configurable> itsTriton{"itsTriton", {}, "Maximum |nsigma| for trition PID"}; + o2::framework::Configurable> itsHelium{"itsHelium", {}, "Maximum |nsigma| for helium PID"}; + + // track tpc pid cuts + o2::framework::Configurable> tpcElectron{"tpcElectron", {}, "Maximum |nsigma| for electron PID"}; + o2::framework::Configurable> tpcPion{"tpcPion", {}, "Maximum |nsigma| for pion PID"}; + o2::framework::Configurable> tpcKaon{"tpcKaon", {}, "Maximum |nsigma| for kaon PID"}; + o2::framework::Configurable> tpcProton{"tpcProton", {}, "Maximum |nsigma| for proton PID"}; + o2::framework::Configurable> tpcDeuteron{"tpcDeuteron", {}, "Maximum |nsigma| for deuteron PID"}; + o2::framework::Configurable> tpcTriton{"tpcTriton", {}, "Maximum |nsigma| for trition PID"}; + o2::framework::Configurable> tpcHelium{"tpcHelium", {}, "Maximum |nsigma| for helium PID"}; + + // track tof pid cuts + o2::framework::Configurable> tofElectron{"tofElectron", {}, "Maximum |nsigma| for electron PID"}; + o2::framework::Configurable> tofPion{"tofPion", {}, "Maximum |nsigma| for pion PID"}; + o2::framework::Configurable> tofKaon{"tofKaon", {}, "Maximum |nsigma| for kaon PID"}; + o2::framework::Configurable> tofProton{"tofProton", {}, "Maximum |nsigma| for proton PID"}; + o2::framework::Configurable> tofDeuteron{"tofDeuteron", {}, "Maximum |nsigma| for deuteron PID"}; + o2::framework::Configurable> tofTriton{"tofTriton", {}, "Maximum |nsigma| for trition PID"}; + o2::framework::Configurable> tofHelium{"tofHelium", {}, "Maximum |nsigma| for helium PID"}; + + // track tpcits pid cuts + o2::framework::Configurable> tpcitsElectron{"tpcitsElectron", {}, "Maximum |nsigma| for electron PID"}; + o2::framework::Configurable> tpcitsPion{"tpcitsPion", {}, "Maximum |nsigma| for pion PID"}; + o2::framework::Configurable> tpcitsKaon{"tpcitsKaon", {}, "Maximum |nsigma| for kaon PID"}; + o2::framework::Configurable> tpcitsProton{"tpcitsProton", {3.f}, "Maximum |nsigma| for proton PID"}; + o2::framework::Configurable> tpcitsDeuteron{"tpcitsDeuteron", {}, "Maximum |nsigma| for deuteron PID"}; + o2::framework::Configurable> tpcitsTriton{"tpcitsTriton", {}, "Maximum |nsigma| for trition PID"}; + o2::framework::Configurable> tpcitsHelium{"tpcitsHelium", {}, "Maximum |nsigma| for helium PID"}; + + // track tpctof pid cuts + o2::framework::Configurable> tpctofElectron{"tpctofElectron", {}, "Maximum |nsigma| for electron PID"}; + o2::framework::Configurable> tpctofPion{"tpctofPion", {}, "Maximum |nsigma| for pion PID"}; + o2::framework::Configurable> tpctofKaon{"tpctofKaon", {}, "Maximum |nsigma| for kaon PID"}; + o2::framework::Configurable> tpctofProton{"tpctofProton", {3.f}, "Maximum |nsigma| for proton PID"}; + o2::framework::Configurable> tpctofDeuteron{"tpctofDeuteron", {}, "Maximum |nsigma| for deuteron PID"}; + o2::framework::Configurable> tpctofTriton{"tpctofTriton", {}, "Maximum |nsigma| for trition PID"}; + o2::framework::Configurable> tpctofHelium{"tpctofHelium", {}, "Maximum |nsigma| for helium PID"}; +}; + +// define the template structure for TrackSelection +template +struct ConfTrackSelection : public o2::framework::ConfigurableGroup { + std::string prefix = Prefix; // Unique prefix based on the template argument + // configuration parameters + o2::framework::Configurable pdgCode{"pdgCode", 2212, "Track PDG code"}; + o2::framework::Configurable sign{"sign", 1, "Sign of the track (1 for positive tracks and -1 for negative tracks)"}; + // filters for kinematics + o2::framework::Configurable ptMin{"ptMin", 0.2f, "Minimum pT (GeV/c)"}; + o2::framework::Configurable ptMax{"ptMax", 6.f, "Maximum pT (GeV/c)"}; + o2::framework::Configurable etaMin{"etaMin", -0.9f, "Minimum eta"}; + o2::framework::Configurable etaMax{"etaMax", 0.9f, "Maximum eta"}; + o2::framework::Configurable phiMin{"phiMin", 0.f, "Minimum phi"}; + o2::framework::Configurable phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"}; + // track selection masks + o2::framework::Configurable maskLowMomentum{"maskLowMomentum", 2u, "Bitmask for selections below momentum threshold"}; + o2::framework::Configurable maskHighMomentum{"maskHighMomentum", 1u, "Bitmask for selections above momentum threshold"}; + // momentum threshold for PID usage + o2::framework::Configurable pidThres{"pidThres", 1.2f, "Momentum threshold for using TPCTOF/TOF pid for tracks with large momentum (GeV/c)"}; +}; + +// Define unique prefixes as constexpr string literals +constexpr const char PrefixTrackSelection1[] = "TrackSelection1"; +constexpr const char PrefixTrackSelection2[] = "TrackSelection2"; +constexpr const char PrefixTrackSelection3[] = "TrackSelection3"; + +// Instantiate different instances with unique prefixes +using ConfTrackSelection1 = ConfTrackSelection; +using ConfTrackSelection2 = ConfTrackSelection; +using ConfTrackSelection3 = ConfTrackSelection; + +/// enum for all track selections +enum TrackSels { + // track quality cuts + kTPCnClsMin, ///< Min. number of TPC clusters + kTPCcRowsMin, ///< Min. number of crossed TPC rows + kTPCsClsMax, ///< Max. number of shared TPC clusters + kTPCsClsFracMax, ///< Max. fractions 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) as a function of pT + kDCAzMax, ///< Max. |DCA_z| (cm) as a function of pT + + /// track pid cuts + kItsElectron, ///< ITS Electon PID + kItsPion, ///< ITS Pion PID + kItsKaon, ///< ITS Kaon PID + kItsProton, ///< ITS Proton PID + kItsDeuteron, ///< ITS Deuteron PID + kItsTriton, ///< ITS Triton PID + kItsHelium, ///< ITS He3 PID + + kTpcElectron, ///< TPC Electon PID + kTpcPion, ///< TPC Pion PID + kTpcKaon, ///< TPC Kaon PID + kTpcProton, ///< TPC Proton PID + kTpcDeuteron, ///< TPC Deuteron PID + kTpcTriton, ///< TPC Triton PID + kTpcHelium, ///< TPC He3 PID + + kTofElectron, ///< TOF Electon PID + kTofPion, ///< TOF Pion PID + kTofKaon, ///< TOF Kaon PID + kTofProton, ///< TOF Proton PID + kTofDeuteron, ///< TOF Deuteron PID + kTofTriton, ///< TOF Triton PID + kTofHelium, ///< TOF He3 PID + + kTpcitsElectron, ///< TPC+ITS Electon PID + kTpcitsPion, ///< TPC+ITS Pion PID + kTpcitsKaon, ///< TPC+ITS Kaon PID + kTpcitsProton, ///< TPC+ITS Proton PID + kTpcitsDeuteron, ///< TPC+ITS Deuteron PID + kTpcitsTriton, ///< TPC+ITS Triton PID + kTpcitsHelium, ///< TPC+ITS He3 PID + + kTpctofElectron, ///< TPC+TOF Electon PID + kTpctofPion, ///< TPC+TOF Pion PID + kTpctofKaon, ///< TPC+TOF Kaon PID + kTpctofProton, ///< TPC+TOF Proton PID + kTpctofDeuteron, ///< TPC+TOF Deuteron PID + kTpctofTriton, ///< TPC+TOF Triton PID + kTpctofHelium, ///< TPC+TOF He3 PID + + kTrackSelsMax +}; + +const char trackSelsName[] = "Track Selection Object"; +const std::unordered_map trackSelsToString = { + {kTPCnClsMin, "Min. number of TPC clusters"}, + {kTPCcRowsMin, "Min. number of crossed TPC rows"}, + {kTPCsClsMax, "Max. number of shared TPC clusters"}, + {kTPCsClsFracMax, "Max. fractions 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) as a function of pT"}, + {kDCAzMax, "Max. |DCA_z| (cm) as a function of pT"}, + + {kItsElectron, "ITS Electron PID"}, + {kItsPion, "ITS Pion PID"}, + {kItsKaon, "ITS Kaon PID"}, + {kItsProton, "ITS Proton PID"}, + {kItsDeuteron, "ITS Deuteron PID"}, + {kItsTriton, "ITS Triton PID"}, + {kItsHelium, "ITS He3 PID"}, + + {kTpcElectron, "TPC Electron PID"}, + {kTpcPion, "TPC Pion PID"}, + {kTpcKaon, "TPC Kaon PID"}, + {kTpcProton, "TPC Proton PID"}, + {kTpcDeuteron, "TPC Deuteron PID"}, + {kTpcTriton, "TPC Triton PID"}, + {kTpcHelium, "TPC He3 PID"}, + + {kTofElectron, "TOF Electron PID"}, + {kTofPion, "TOF Pion PID"}, + {kTofKaon, "TOF Kaon PID"}, + {kTofProton, "TOF Proton PID"}, + {kTofDeuteron, "TOF Deuteron PID"}, + {kTofTriton, "TOF Triton PID"}, + {kTofHelium, "TOF He3 PID"}, + + {kTpcitsElectron, "TPC+ITS Electron PID"}, + {kTpcitsPion, "TPC+ITS Pion PID"}, + {kTpcitsKaon, "TPC+ITS Kaon PID"}, + {kTpcitsProton, "TPC+ITS Proton PID"}, + {kTpcitsDeuteron, "TPC+ITS Deuteron PID"}, + {kTpcitsTriton, "TPC+ITS Triton PID"}, + {kTpcitsHelium, "TPC+ITS He PID"}, + + {kTpctofElectron, "TPC+TOF Electron PID"}, + {kTpctofPion, "TPC+TOF Pion PID"}, + {kTpctofKaon, "TPC+TOF Kaon PID"}, + {kTpctofProton, "TPC+TOF Proton PID"}, + {kTpctofDeuteron, "TPC+TOF Deuteron PID"}, + {kTpctofTriton, "TPC+TOF Triton PID"}, + {kTpctofHelium, "TPC+TOF He3 PID"}}; + +/// \class FemtoDreamTrackCuts +/// \brief Cut class to contain and execute all cuts applied to tracks +class TrackSelection : public BaseSelection +{ + public: + TrackSelection() {} + virtual ~TrackSelection() = default; + + template + void configure(T1& config, T2& filter) + { + mPtMin = filter.ptMin; + mPtMax = filter.ptMax; + mEtaMin = filter.etaMin; + mEtaMax = filter.etaMax; + mPhiMin = filter.phiMin; + mPhiMax = filter.phiMax; + mMinimalMomentumForTof = config.minMomentumForTof.value; + + // add selections for track quality + this->addSelection(config.tpcClustersMin.value, kTPCnClsMin, limits::kLowerLimit, true, true); + this->addSelection(config.tpcCrossedRowsMin.value, kTPCcRowsMin, limits::kLowerLimit, true, true); + this->addSelection(config.tpcSharedClustersMax.value, kTPCsClsMax, limits::kUpperLimit, true, true); + this->addSelection(config.tpcSharedClusterFractionMax.value, kTPCsClsFracMax, limits::kUpperLimit, true, true); + this->addSelection(config.itsClustersMin.value, kITSnClsMin, limits::kLowerLimit, true, true); + this->addSelection(config.itsIbClustersMin.value, kITSnClsIbMin, limits::kLowerLimit, true, true); + this->addSelection(config.dcaxyMax.name, filter.ptMin.value, filter.ptMax.value, config.dcaxyMax.value, kDCAxyMax, limits::kAbsUpperFunctionLimit, true, true); + this->addSelection(config.dcazMax.name, filter.ptMin.value, filter.ptMax.value, config.dcazMax.value, kDCAzMax, limits::kAbsUpperFunctionLimit, true, true); + + // add selections for its pid + this->addSelection(config.itsElectron.value, kItsElectron, limits::kAbsUpperLimit, false, false); + this->addSelection(config.itsPion.value, kItsPion, limits::kAbsUpperLimit, false, false); + this->addSelection(config.itsKaon.value, kItsKaon, limits::kAbsUpperLimit, false, false); + this->addSelection(config.itsProton.value, kItsProton, limits::kAbsUpperLimit, false, false); + this->addSelection(config.itsDeuteron.value, kItsDeuteron, limits::kAbsUpperLimit, false, false); + this->addSelection(config.itsTriton.value, kItsTriton, limits::kAbsUpperLimit, false, false); + this->addSelection(config.itsHelium.value, kItsHelium, limits::kAbsUpperLimit, false, false); + // add selections for tpc pid + this->addSelection(config.tpcElectron.value, kTpcElectron, limits::kAbsUpperLimit, false, false); + this->addSelection(config.tpcPion.value, kTpcPion, limits::kAbsUpperLimit, false, false); + this->addSelection(config.tpcKaon.value, kTpcKaon, limits::kAbsUpperLimit, false, false); + this->addSelection(config.tpcProton.value, kTpcProton, limits::kAbsUpperLimit, false, false); + this->addSelection(config.tpcDeuteron.value, kTpcDeuteron, limits::kAbsUpperLimit, false, false); + this->addSelection(config.tpcTriton.value, kTpcTriton, limits::kAbsUpperLimit, false, false); + this->addSelection(config.tpcHelium.value, kTpcHelium, limits::kAbsUpperLimit, false, false); + // add selections for tof pid + this->addSelection(config.tofElectron.value, kTofElectron, limits::kAbsUpperLimit, false, false); + this->addSelection(config.tofPion.value, kTofPion, limits::kAbsUpperLimit, false, false); + this->addSelection(config.tofKaon.value, kTofKaon, limits::kAbsUpperLimit, false, false); + this->addSelection(config.tofProton.value, kTofProton, limits::kAbsUpperLimit, false, false); + this->addSelection(config.tofDeuteron.value, kTofDeuteron, limits::kAbsUpperLimit, false, false); + this->addSelection(config.tofTriton.value, kTofTriton, limits::kAbsUpperLimit, false, false); + this->addSelection(config.tofHelium.value, kTofHelium, limits::kAbsUpperLimit, false, false); + // add selections for tpcits pid + this->addSelection(config.tpcitsElectron.value, kTpcitsElectron, limits::kUpperLimit, false, false); + this->addSelection(config.tpcitsPion.value, kTpcitsPion, limits::kUpperLimit, false, false); + this->addSelection(config.tpcitsKaon.value, kTpcitsKaon, limits::kUpperLimit, false, false); + this->addSelection(config.tpcitsProton.value, kTpcitsProton, limits::kUpperLimit, false, false); + this->addSelection(config.tpcitsDeuteron.value, kTpcitsDeuteron, limits::kUpperLimit, false, false); + this->addSelection(config.tpcitsTriton.value, kTpcitsTriton, limits::kUpperLimit, false, false); + this->addSelection(config.tpcitsHelium.value, kTpcitsHelium, limits::kUpperLimit, false, false); + // add selections for tpctof pid + this->addSelection(config.tpctofElectron.value, kTpctofElectron, limits::kUpperLimit, false, false); + this->addSelection(config.tpctofPion.value, kTpctofPion, limits::kUpperLimit, false, false); + this->addSelection(config.tpctofKaon.value, kTpctofKaon, limits::kUpperLimit, false, false); + this->addSelection(config.tpctofProton.value, kTpctofProton, limits::kUpperLimit, false, false); + this->addSelection(config.tpctofDeuteron.value, kTpctofDeuteron, limits::kUpperLimit, false, false); + this->addSelection(config.tpctofTriton.value, kTpctofTriton, limits::kUpperLimit, false, false); + this->addSelection(config.tpctofHelium.value, kTpctofHelium, limits::kUpperLimit, false, false); + } + + template + bool hasTofAboveThreshold(T const& track) const + { + // If track momentum exceeds threshold, we require valid TOF info + return !(track.p() > mMinimalMomentumForTof && !track.hasTOF()); + } + + template + bool checkFilters(T const& track) const + { + return ((track.pt() > mPtMin && track.pt() < mPtMax) && + (track.eta() > mEtaMin && track.eta() < mEtaMax) && + (track.phi() > mPhiMin && track.phi() < mPhiMax)); + } + + template + void applySelections(T const& Track) + { + this->reset(); + this->evaluateObservable(kTPCnClsMin, Track.tpcNClsFound()); + this->evaluateObservable(kTPCcRowsMin, Track.tpcNClsCrossedRows()); + this->evaluateObservable(kTPCsClsMax, Track.tpcNClsShared()); + this->evaluateObservable(kTPCsClsFracMax, static_cast(Track.tpcNClsShared()) / static_cast(Track.tpcNClsFound())); + this->evaluateObservable(kITSnClsMin, Track.itsNCls()); + this->evaluateObservable(kITSnClsIbMin, Track.itsNClsInnerBarrel()); + + // evalue bitmask for pt dependent dca cuts + this->updateLimits(kDCAxyMax, Track.pt()); + this->evaluateObservable(kDCAxyMax, Track.dcaXY()); + + this->updateLimits(kDCAzMax, Track.pt()); + this->evaluateObservable(kDCAzMax, Track.dcaZ()); + + // its pid + this->evaluateObservable(kItsElectron, Track.itsNSigmaEl()); + this->evaluateObservable(kItsPion, Track.itsNSigmaPi()); + this->evaluateObservable(kItsKaon, Track.itsNSigmaKa()); + this->evaluateObservable(kItsProton, Track.itsNSigmaPr()); + this->evaluateObservable(kItsDeuteron, Track.itsNSigmaDe()); + this->evaluateObservable(kItsTriton, Track.itsNSigmaTr()); + this->evaluateObservable(kItsHelium, Track.itsNSigmaHe()); + + // tpc pid + this->evaluateObservable(kTpcElectron, Track.tpcNSigmaEl()); + this->evaluateObservable(kTpcPion, Track.tpcNSigmaPi()); + this->evaluateObservable(kTpcKaon, Track.tpcNSigmaKa()); + this->evaluateObservable(kTpcProton, Track.tpcNSigmaPr()); + this->evaluateObservable(kTpcDeuteron, Track.tpcNSigmaDe()); + this->evaluateObservable(kTpctofTriton, Track.tpcNSigmaTr()); + this->evaluateObservable(kTpcHelium, Track.tpcNSigmaHe()); + + // tof pid + this->evaluateObservable(kTofElectron, Track.tofNSigmaEl()); + this->evaluateObservable(kTofPion, Track.tofNSigmaPi()); + this->evaluateObservable(kTofKaon, Track.tofNSigmaKa()); + this->evaluateObservable(kTofProton, Track.tofNSigmaPr()); + this->evaluateObservable(kTofDeuteron, Track.tofNSigmaDe()); + this->evaluateObservable(kTofTriton, Track.tofNSigmaTr()); + this->evaluateObservable(kTofHelium, Track.tofNSigmaHe()); + + // combined tpc + its pid + this->evaluateObservable(kTpcitsElectron, std::hypot(Track.tpcNSigmaEl(), Track.itsNSigmaEl())); + this->evaluateObservable(kTpcitsPion, std::hypot(Track.tpcNSigmaPi(), Track.itsNSigmaPi())); + this->evaluateObservable(kTpcitsKaon, std::hypot(Track.tpcNSigmaKa(), Track.itsNSigmaKa())); + this->evaluateObservable(kTpcitsProton, std::hypot(Track.tpcNSigmaPr(), Track.itsNSigmaPr())); + this->evaluateObservable(kTpcitsDeuteron, std::hypot(Track.tpcNSigmaDe(), Track.itsNSigmaDe())); + this->evaluateObservable(kTpcitsTriton, std::hypot(Track.tpcNSigmaTr(), Track.itsNSigmaTr())); + this->evaluateObservable(kTpcitsHelium, std::hypot(Track.tpcNSigmaHe(), Track.itsNSigmaHe())); + + // combined tpc + tof pid + this->evaluateObservable(kTpctofElectron, std::hypot(Track.tpcNSigmaEl(), Track.tofNSigmaEl())); + this->evaluateObservable(kTpctofPion, std::hypot(Track.tpcNSigmaPi(), Track.tofNSigmaPi())); + this->evaluateObservable(kTpctofKaon, std::hypot(Track.tpcNSigmaKa(), Track.tofNSigmaKa())); + this->evaluateObservable(kTpctofProton, std::hypot(Track.tpcNSigmaPr(), Track.tofNSigmaPr())); + this->evaluateObservable(kTpctofDeuteron, std::hypot(Track.tpcNSigmaDe(), Track.tofNSigmaDe())); + this->evaluateObservable(kTpctofTriton, std::hypot(Track.tpcNSigmaTr(), Track.tofNSigmaTr())); + this->evaluateObservable(kTpctofHelium, std::hypot(Track.tpcNSigmaHe(), Track.tofNSigmaHe())); + + this->assembleBitmask(); + }; + + protected: + float mMinimalMomentumForTof = 2.f; + float mPtMin = 0.f; + float mPtMax = 0.f; + float mEtaMin = -0.9; + float mEtaMax = 0.9; + float mPhiMin = 0; + float mPhiMax = o2::constants::math::TwoPI; +}; + +struct TrackBuilderProducts : o2::framework::ProducesGroup { + o2::framework::Produces producedTracks; + o2::framework::Produces producedTrackMasks; + o2::framework::Produces producedTrackDcas; + o2::framework::Produces producedTrackExtras; + o2::framework::Produces producedElectronPids; + o2::framework::Produces producedPionPids; + o2::framework::Produces producedKaonPids; + o2::framework::Produces producedProtonPids; + o2::framework::Produces producedDeuteronPids; + o2::framework::Produces producedTritonPids; + o2::framework::Produces producedHeliumPids; +}; + +struct ConfTrackTables : o2::framework::ConfigurableGroup { + std::string prefix = std::string("TrackTables"); + o2::framework::Configurable produceTracks{"produceTracks", -1, "Produce Tracks (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceTrackMasks{"produceTrackMasks", -1, "Produce TrackMasks (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceTrackDcas{"produceTrackDcas", -1, "Produce TrackDcas (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceTrackExtras{"produceTrackExtras", -1, "Produce TrackExtras (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceElectronPids{"produceElectronPids", -1, "Produce ElectronPids (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable producePionPids{"producePionPids", -1, "Produce PionPids (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceKaonPids{"produceKaonPids", -1, "Produce KaonPids (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceProtonPids{"produceProtonPids", -1, "Produce ProtonPids (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceDeuteronPids{"produceDeuteronPids", -1, "Produce DeuteronPids (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceTritonPids{"produceTritonPids", -1, "Produce TritonPids (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceHeliumPids{"produceHeliumPids", -1, "Produce HeliumPids (-1: auto; 0 off; 1 on)"}; +}; + +class TrackBuilder +{ + public: + TrackBuilder() {} + virtual ~TrackBuilder() = default; + + template + void init(T1& config, T2& filter, T3& table, T4& initContext) + { + trackSelection.configure(config, filter); + LOG(info) << "Initialize femto track builder..."; + + produceTracks = utils::enableTable("FTracks_001", table.produceTracks.value, initContext); + produceTrackMasks = utils::enableTable("FTrackMasks_001", table.produceTrackMasks.value, initContext); + produceTrackDcas = utils::enableTable("FTrackDcas_001", table.produceTrackDcas.value, initContext); + produceTrackExtras = utils::enableTable("FTrackExtras_001", table.produceTrackExtras.value, initContext); + produceElectronPids = utils::enableTable("FElectronPids_001", table.produceElectronPids.value, initContext); + producePionPids = utils::enableTable("FPionPids_001", table.producePionPids.value, initContext); + produceKaonPids = utils::enableTable("FKaonPids_001", table.produceKaonPids.value, initContext); + produceProtonPids = utils::enableTable("FProtonPids_001", table.produceProtonPids.value, initContext); + produceDeuteronPids = utils::enableTable("FDeuteronPids_001", table.produceDeuteronPids.value, initContext); + produceTritonPids = utils::enableTable("FTritonPids_001", table.produceTritonPids.value, initContext); + produceHeliumPids = utils::enableTable("FHeliumPids_001", table.produceHeliumPids.value, initContext); + + if (produceTracks || produceTrackMasks || produceTrackDcas || produceTrackExtras || produceElectronPids || producePionPids || produceKaonPids || produceProtonPids || produceDeuteronPids || produceTritonPids || produceHeliumPids) { + fillAnyTable = true; + trackSelection.printSelections(trackSelsName, trackSelsToString); + } else { + LOG(info) << "No tables configured"; + } + LOG(info) << "Initialization done..."; + } + + template + void fillTracks(T1 const& tracks, T2& trackProducts, T3& collisionProducts, T4& indexMap) + { + if (!fillAnyTable) { + return; + } + for (const auto& track : tracks) { + if (!trackSelection.checkFilters(track) || !trackSelection.hasTofAboveThreshold(track)) { + continue; + } + trackSelection.applySelections(track); + if (!trackSelection.passesAllRequiredSelections()) { + continue; + } + this->fillTrack(track, trackProducts, collisionProducts, indexMap); + } + } + + template + void fillTrack(T1 const& track, T2& trackProducts, T3& collisionProducts, T4& indexMap) + { + if (produceTracks) { + trackProducts.producedTracks(collisionProducts.producedCollision.lastIndex(), + track.pt() * track.sign(), + track.eta(), + track.phi()); + } + + if (produceTrackMasks) { + if constexpr (type == modes::Track::kPrimaryTrack) { + trackProducts.producedTrackMasks(trackSelection.getBitmask()); + } else { + trackProducts.producedTrackMasks(static_cast(0u)); + } + } + + if (produceTrackDcas) { + trackProducts.producedTrackDcas(track.dcaXY(), track.dcaZ()); + } + if (produceTrackExtras) { + trackProducts.producedTrackExtras(track.isPVContributor(), + track.itsNCls(), + track.itsNClsInnerBarrel(), + track.itsChi2NCl(), + track.itsClusterSizes(), + track.tpcSignal(), + track.tpcInnerParam(), + track.tpcNClsFound(), + track.tpcNClsCrossedRows(), + track.tpcNClsShared(), + track.beta(), + track.mass()); + } + + if (produceElectronPids) { + if constexpr (type == modes::Track::kPrimaryTrack) { + trackProducts.producedElectronPids(track.itsNSigmaEl(), track.tpcNSigmaEl(), track.tofNSigmaEl()); + } else { + trackProducts.producedElectronPids(0, track.tpcNSigmaEl(), track.tofNSigmaEl()); + } + } + if (producePionPids) { + if constexpr (type == modes::Track::kPrimaryTrack) { + trackProducts.producedPionPids(track.itsNSigmaPi(), track.tpcNSigmaPi(), track.tofNSigmaPi()); + } else { + trackProducts.producedPionPids(0, track.tpcNSigmaPi(), track.tofNSigmaPi()); + } + } + if (produceKaonPids) { + if constexpr (type == modes::Track::kPrimaryTrack) { + trackProducts.producedKaonPids(track.itsNSigmaKa(), track.tpcNSigmaKa(), track.tofNSigmaKa()); + } else { + trackProducts.producedKaonPids(0, track.tpcNSigmaKa(), track.tofNSigmaKa()); + } + } + if (produceProtonPids) { + if constexpr (type == modes::Track::kPrimaryTrack) { + trackProducts.producedProtonPids(track.itsNSigmaPr(), track.tpcNSigmaPr(), track.tofNSigmaPr()); + } else { + trackProducts.producedProtonPids(0, track.tpcNSigmaPr(), track.tofNSigmaPr()); + } + } + if (produceDeuteronPids) { + if constexpr (type == modes::Track::kPrimaryTrack) { + trackProducts.producedDeuteronPids(track.itsNSigmaDe(), track.tpcNSigmaDe(), track.tofNSigmaDe()); + } else { + trackProducts.producedDeuteronPids(0, track.tpcNSigmaDe(), track.tofNSigmaDe()); + } + } + if (produceTritonPids) { + if constexpr (type == modes::Track::kPrimaryTrack) { + trackProducts.producedTritonPids(track.itsNSigmaTr(), track.tpcNSigmaTr(), track.tofNSigmaTr()); + } else { + trackProducts.producedTritonPids(0, track.tpcNSigmaTr(), track.tofNSigmaTr()); + } + } + if (produceHeliumPids) { + if constexpr (type == modes::Track::kPrimaryTrack) { + trackProducts.producedHeliumPids(track.itsNSigmaHe(), track.tpcNSigmaHe(), track.tofNSigmaHe()); + } else { + trackProducts.producedHeliumPids(0, track.tpcNSigmaHe(), track.tofNSigmaHe()); + } + } + indexMap.emplace(track.globalIndex(), trackProducts.producedTracks.lastIndex()); + } + + template + int64_t getDaughterIndex(const T1& daughter, T2& trackProducts, T3& collisionProducts, T4& indexMap) + { + auto result = utils::getIndex(daughter.globalIndex(), indexMap); + if (result) { + return result.value(); + } else { + this->fillTrack(daughter, trackProducts, collisionProducts, indexMap); + int64_t idx = trackProducts.producedTracks.lastIndex(); + indexMap.emplace(daughter.globalIndex(), idx); + return idx; + } + } + + private: + TrackSelection trackSelection; + bool fillAnyTable = false; + bool produceTracks = false; + bool produceTrackMasks = false; + bool produceTrackDcas = false; + bool produceTrackExtras = false; + bool produceElectronPids = false; + bool producePionPids = false; + bool produceKaonPids = false; + bool produceProtonPids = false; + bool produceDeuteronPids = false; + bool produceTritonPids = false; + bool produceHeliumPids = false; +}; + +} // namespace trackbuilder +} // namespace o2::analysis::femto + +#endif // PWGCF_FEMTO_CORE_TRACKBUILDER_H_ diff --git a/PWGCF/Femto/Core/trackHistManager.h b/PWGCF/Femto/Core/trackHistManager.h new file mode 100644 index 00000000000..3f22459ac5b --- /dev/null +++ b/PWGCF/Femto/Core/trackHistManager.h @@ -0,0 +1,557 @@ +// 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 trackHistManager.h +/// \brief histogram manager for track histograms +/// \author Anton Riedel, TU München, anton.riedel@cern.ch + +#ifndef PWGCF_FEMTO_CORE_TRACKHISTMANAGER_H_ +#define PWGCF_FEMTO_CORE_TRACKHISTMANAGER_H_ + +#include "PWGCF/Femto/Core/femtoUtils.h" +#include "PWGCF/Femto/Core/histManager.h" +#include "PWGCF/Femto/Core/modes.h" + +#include "CommonConstants/MathConstants.h" +#include "Framework/Configurable.h" +#include "Framework/HistogramRegistry.h" +#include "Framework/HistogramSpec.h" + +#include +#include +#include +#include +#include + +namespace o2::analysis::femto +{ +namespace trackhistmanager +{ + +// enum for track histograms +enum TrackHist { + // kinemtics + kPt, + kEta, + kPhi, + kSign, + // qa variables + kItsCluster, + kItsClusterIb, + kTpcCrossedRows, + kTpcCluster, + kTpcClusterShared, + kTpcClusterFractionShared, + // kDcaxy, + // kDcaz, + // kDca, + // 2d qa + kPtVsEta, + kPtVsPhi, + kPhiVsEta, + kPtVsItsCluster, + kPtVsTpcCluster, + kPtVsTpcClusterShared, + kTpcClusterVsTpcClusterShared, + kPtVsDcaxy, + kPtVsDcaz, + kPtVsDca, + // its pid + kItsSignal, + kItsElectron, + kItsPion, + kItsKaon, + kItsProton, + kItsDeuteron, + kItsTriton, + kItsHelium, + // tpc pid + kTpcSignal, + kTpcElectron, + kTpcPion, + kTpcKaon, + kTpcProton, + kTpcDeuteron, + kTpcTriton, + kTpcHelium, + // tof pid + kTofBeta, + kTofMass, + kTofElectron, + kTofPion, + kTofKaon, + kTofProton, + kTofDeuteron, + kTofTriton, + kTofHelium, + // tpc+its pid + kTpcitsElectron, + kTpcitsPion, + kTpcitsKaon, + kTpcitsProton, + kTpcitsDeuteron, + kTpcitsTriton, + kTpcitsHelium, + // tpc+tof pid + kTpctofElectron, + kTpctofPion, + kTpctofKaon, + kTpctofProton, + kTpctofDeuteron, + kTpctofTriton, + kTpctofHelium, + kTrackHistLast +}; + +template +struct ConfTrackBinning : o2::framework::ConfigurableGroup { + std::string prefix = Prefix; + o2::framework::ConfigurableAxis pt{"pt", {{600, 0, 6}}, "Pt"}; + o2::framework::ConfigurableAxis eta{"eta", {{300, -1.5, 1.5}}, "Eta"}; + o2::framework::ConfigurableAxis phi{"phi", {{720, 0, 1.f * o2::constants::math::TwoPI}}, "Phi"}; + o2::framework::ConfigurableAxis sign{"sign", {{3, -1.5, 1.5}}, "Sign"}; +}; + +constexpr const char PrefixTrackBinning1[] = "TrackBinning1"; +constexpr const char PrefixTrackBinning2[] = "TrackBinning2"; +constexpr const char PrefixResonancePosDauBinning[] = "ResonancePosDauBinning"; +constexpr const char PrefixResonanceNegDauBinning[] = "ResonanceNegDauBinning"; +constexpr const char PrefixV0PosDauBinning[] = "V0PosDauBinning"; +constexpr const char PrefixV0NegDauBinning[] = "V0NegDauBinning"; +constexpr const char PrefixCascadePosDauBinning[] = "CascadePosDauBinning"; +constexpr const char PrefixCascadeNegDauBinning[] = "CascadeNegDauBinning"; +constexpr const char PrefixCascadeBachelorBinning[] = "CascadeBachelorBinning"; + +using ConfTrackBinning1 = ConfTrackBinning; +using ConfTrackBinning2 = ConfTrackBinning; +using ConfResonancePosDauBinning = ConfTrackBinning; +using ConfResonanceNegDauBinning = ConfTrackBinning; +using ConfV0PosDauBinning = ConfTrackBinning; +using ConfV0NegDauBinning = ConfTrackBinning; +using ConfCascadePosDauBinning = ConfTrackBinning; +using ConfCascadeNegDauBinning = ConfTrackBinning; +using ConfCascadeBachelorBinning = ConfTrackBinning; + +template +struct ConfTrackQaBinning : o2::framework::ConfigurableGroup { + std::string prefix = Prefix; + o2::framework::ConfigurableAxis itsCluster{"itsCluster", {{8, -0.5, 7.5}}, "ITS cluster"}; + o2::framework::ConfigurableAxis itsClusterIb{"itsClusterIb", {{4, -0.5, 3.5}}, "ITS cluster in inner barrel"}; + o2::framework::ConfigurableAxis tpcCrossedRows{"tpcCrossedRows", {{161, -0.5, 160.5}}, "TPC cluster"}; + o2::framework::ConfigurableAxis tpcCluster{"tpcCluster", {{161, -0.5, 160.5}}, "TPC cluster"}; + o2::framework::ConfigurableAxis tpcClusterShared{"tpcClusterShared", {{161, -0.5, 160.5}}, "TPC cluster shared"}; + o2::framework::ConfigurableAxis tpcClusterFractionShared{"tpcClusterFractionShared", {{60, 0, 1.2}}, "TPC cluster fraction shared"}; + o2::framework::ConfigurableAxis dcaXy{"dcaXy", {{300, -0.3, 0.3}}, "DCA_xy"}; + o2::framework::ConfigurableAxis dcaZ{"dcaZ", {{300, -0.3, 0.3}}, "DCA_Z"}; + o2::framework::ConfigurableAxis dca{"dca", {{300, 0, 0.3}}, "DCA"}; + o2::framework::ConfigurableAxis p{"p", {{300, 0, 6}}, "Momentum axis"}; + o2::framework::ConfigurableAxis itsSignal{"itsSignal", {{150, 0, 15}}, "ITS Signal"}; + o2::framework::ConfigurableAxis itsElectron{"itsElectron", {{300, -3, 3}}, "ITS PID for electron"}; + o2::framework::ConfigurableAxis itsPion{"itsPion", {{300, -3, 3}}, "ITS PID for pion"}; + o2::framework::ConfigurableAxis itsKaon{"itsKaon", {{300, -3, 3}}, "ITS PID for kaon"}; + o2::framework::ConfigurableAxis itsProton{"itsProton", {{300, -3, 3}}, "ITS PID for proton"}; + o2::framework::ConfigurableAxis itsDeuteron{"itsDeuteron", {{300, -3, 3}}, "ITS PID for deuteron"}; + o2::framework::ConfigurableAxis itsTriton{"itsTriton", {{300, -3, 3}}, "ITS PID for triton"}; + o2::framework::ConfigurableAxis itsHelium{"itsHelium", {{300, -3, 3}}, "ITS PID for helium"}; + o2::framework::ConfigurableAxis tpcSignal{"tpcSignal", {{150, 0, 150}}, "TPC Signal"}; + o2::framework::ConfigurableAxis tpcElectron{"tpcElectron", {{300, -3, 3}}, "TPC PID for electron"}; + o2::framework::ConfigurableAxis tpcPion{"tpcPion", {{300, -3, 3}}, "TPC PID for pion"}; + o2::framework::ConfigurableAxis tpcKaon{"tpcKaon", {{300, -3, 3}}, "TPC PID for kaon"}; + o2::framework::ConfigurableAxis tpcProton{"tpcProton", {{300, -3, 3}}, "TPC PID for proton"}; + o2::framework::ConfigurableAxis tpcDeuteron{"tpcDeuteron", {{300, -3, 3}}, "TPC PID for deuteron"}; + o2::framework::ConfigurableAxis tpcTriton{"tpcTriton", {{300, -3, 3}}, "TPC PID for triton"}; + o2::framework::ConfigurableAxis tpcHelium{"tpcHelium", {{300, -3, 3}}, "TPC PID for helium"}; + o2::framework::ConfigurableAxis tofBeta{"tofBeta", {{150, 0, 1.5}}, "TOF Signal"}; + o2::framework::ConfigurableAxis tofMass{"tofMass", {{150, 0, 1.5}}, "TOF Mass"}; + o2::framework::ConfigurableAxis tofElectron{"tofElectron", {{300, -3, 3}}, "TOF PID for electron"}; + o2::framework::ConfigurableAxis tofPion{"tofPion", {{300, -3, 3}}, "TOF PID for pion"}; + o2::framework::ConfigurableAxis tofKaon{"tofKaon", {{300, -3, 3}}, "TOF PID for kaon"}; + o2::framework::ConfigurableAxis tofProton{"tofProton", {{300, -3, 3}}, "TOF PID for proton"}; + o2::framework::ConfigurableAxis tofDeuteron{"tofDeuteron", {{300, -3, 3}}, "TOF PID for deuteron"}; + o2::framework::ConfigurableAxis tofTriton{"tofTriton", {{300, -3, 3}}, "TOF PID for triton"}; + o2::framework::ConfigurableAxis tofHelium{"tofHelium", {{300, -3, 3}}, "TOF PID for helium"}; + o2::framework::ConfigurableAxis tpcitsElectron{"tpcitsElectron", {{300, 0, 3}}, "tpcits PID for electron"}; + o2::framework::ConfigurableAxis tpcitsPion{"tpcitsPion", {{300, 0, 3}}, "TPCITS PID for pion"}; + o2::framework::ConfigurableAxis tpcitsKaon{"tpcitsKaon", {{300, 0, 3}}, "TPCITS PID for kaon"}; + o2::framework::ConfigurableAxis tpcitsProton{"tpcitsProton", {{300, 0, 3}}, "TPCITS PID for proton"}; + o2::framework::ConfigurableAxis tpcitsDeuteron{"tpcitsDeuteron", {{300, 0, 3}}, "TPCITS PID for deuteron"}; + o2::framework::ConfigurableAxis tpcitsTriton{"tpcitsTriton", {{300, 0, 3}}, "TPCITS PID for triton"}; + o2::framework::ConfigurableAxis tpcitsHelium{"tpcitsHelium", {{300, 0, 3}}, "TPCITS PID for helium"}; + o2::framework::ConfigurableAxis tpctofElectron{"tpctofElectron", {{300, 0, 3}}, "TPCTOF PID for electron"}; + o2::framework::ConfigurableAxis tpctofPion{"tpctofPion", {{300, 0, 3}}, "TPCTOF PID for pion"}; + o2::framework::ConfigurableAxis tpctofKaon{"tpctofKaon", {{300, 0, 3}}, "TPCTOF PID for kaon"}; + o2::framework::ConfigurableAxis tpctofProton{"tpctofProton", {{300, 0, 3}}, "TPCTOF PID for proton"}; + o2::framework::ConfigurableAxis tpctofDeuteron{"tpctofDeuteron", {{300, 0, 3}}, "TPCTOF PID for deuteron"}; + o2::framework::ConfigurableAxis tpctofTriton{"tpctofTriton", {{300, 0, 3}}, "TPCTOF PID for triton"}; + o2::framework::ConfigurableAxis tpctofHelium{"tpctofHelium", {{300, 0, 3}}, "TPCTOF PID for helium"}; +}; + +constexpr const char PrefixTrackQaBinning1[] = "TrackQaBinning1"; +constexpr const char PrefixTrackQaBinning2[] = "TrackQaBinning1"; +constexpr const char PrefixResonancePosDauQaBinning[] = "ResonancePosDauQaBinning"; +constexpr const char PrefixResonanceNegDauQaBinning[] = "ResonanceNegDauQaBinning"; +constexpr const char PrefixV0PosDauQaBinning[] = "V0PosDauQaBinning"; +constexpr const char PrefixV0NegDauQaBinning[] = "V0NegDauQaBinning"; +constexpr const char PrefixCascadePosDauQaBinning[] = "CascadePosDauQaBinning"; +constexpr const char PrefixCascadeNegDauQaBinning[] = "CascadeNegDauQaBinning"; +constexpr const char PrefixCascadeBachelorQaBinning[] = "CascadeBachelorQaBinning"; + +using ConfTrackQaBinning1 = ConfTrackQaBinning; +using ConfTrackQaBinning2 = ConfTrackQaBinning; +using ConfResonancePosDauQaBinning = ConfTrackQaBinning; +using ConfResonanceNegDauQaBinning = ConfTrackQaBinning; +using ConfV0PosDauQaBinning = ConfTrackQaBinning; +using ConfV0NegDauQaBinning = ConfTrackQaBinning; +using ConfCascadePosDauQaBinning = ConfTrackQaBinning; +using ConfCascadeNegDauQaBinning = ConfTrackQaBinning; +using ConfCascadeBachelorQaBinning = ConfTrackQaBinning; + +// must be in sync with enum TrackVariables +// the enum gives the correct index in the array +constexpr std::array, kTrackHistLast> HistTable = { + {{kPt, o2::framework::kTH1F, "hPt", "Transverse Momentum; p_{T} (GeV/#it{c}); Entries"}, + {kEta, o2::framework::kTH1F, "hEta", "Pseudorapdity; #eta; Entries"}, + {kPhi, o2::framework::kTH1F, "hPhi", "Azimuthal angle; #varphi; Entries"}, + {kSign, o2::framework::kTH1F, "hSign", "Sign of charge ; Sign; Entries"}, + {kItsCluster, o2::framework::kTH1F, "hItsCluster", "ITS cluster; ITS cluster; Entries"}, + {kItsClusterIb, o2::framework::kTH1F, "hItsClusterIb", "ITS cluster in inner barrel; ITS IB cluster; Entries"}, + {kTpcCrossedRows, o2::framework::kTH1F, "hTpcCrossedRows", "TPC crossed rows; TPC crossed rows; Entries"}, + {kTpcCluster, o2::framework::kTH1F, "hTpcCluster", "TPC cluster found; TPC cluster found; Entries"}, + {kTpcClusterShared, o2::framework::kTH1F, "hTpcClusterShared", "TPC cluster shared; TPC cluster shared ; Entries"}, + {kTpcClusterFractionShared, o2::framework::kTH1F, "hTpcClusterFractionShared", "TPC cluster fraction shared; TPC cluster found / TPC cluster shared ; Entries"}, + {kPtVsEta, o2::framework::kTH2F, "hPtVsEta", "p_{T} vs #eta; p_{T} (GeV/#it{c}) ; #eta"}, + {kPtVsPhi, o2::framework::kTH2F, "hPtVsPhi", "p_{T} vs #varphi; p_{T} (GeV/#it{c}) ; #varphi"}, + {kPhiVsEta, o2::framework::kTH2F, "hPhiVsEta", "#varphi vs #eta; #varphi ; #eta"}, + {kPtVsItsCluster, o2::framework::kTH2F, "hPtVsItsCluster", "p_{T} vs ITS cluster; p_{T} (GeV/#it{c}) ; ITS cluster"}, + {kPtVsTpcCluster, o2::framework::kTH2F, "hPtVsTpcCluster", "p_{T} vs TPC cluster found; p_{T} (GeV/#it{c}) ; TPC cluster found"}, + {kPtVsTpcClusterShared, o2::framework::kTH2F, "hPtVsTpcClusterShared", "p_{T} vs TPC cluster shared; p_{T} (GeV/#it{c}) ; TPC cluster shared"}, + {kTpcClusterVsTpcClusterShared, o2::framework::kTH2F, "hTpcClusterVsTpcClusterShared", "TPC cluster found vs TPC cluster shared; TPC cluster found; TPC cluster shared"}, + {kPtVsDcaxy, o2::framework::kTH2F, "hPtVsDcaxy", "p_{T} vs DCA_{XY}; p_{T} (GeV/#it{c}); DCA_{XY} (cm)"}, + {kPtVsDcaz, o2::framework::kTH2F, "hPtVsDcaz", "p_{T} vs DCA_{Z}; p_{T} (GeV/#it{c}); DCA_{Z} (cm)"}, + {kPtVsDca, o2::framework::kTH2F, "hPtVsDca", "p_{T} vs DCA; p_{T} (GeV/#it{c}); DCA (cm)"}, + {kItsSignal, o2::framework::kTH2F, "hItsSignal", "ITS Signal; p (GeV/#it{c}) ; x "}, + {kItsElectron, o2::framework::kTH2F, "hItsPidElectron", "TPC PID Electron; p (GeV/#it{c}) ; n#sigma_{TPC,el}"}, + {kItsPion, o2::framework::kTH2F, "hItsPidPion", "ITS PID Pion; p (GeV/#it{c}) ; n#sigma_{ITS,pi}"}, + {kItsKaon, o2::framework::kTH2F, "hItsPidKaon", "ITS PID Kaon; p (GeV/#it{c}) ; n#sigma_{ITS,ka}"}, + {kItsProton, o2::framework::kTH2F, "hItsPidProton", "ITS PID Proton; p (GeV/#it{c}) ; n#sigma_{ITS,pr}"}, + {kItsDeuteron, o2::framework::kTH2F, "hItsPidDeuteron", "ITS PID Deuteron; p (GeV/#it{c}) ; n#sigma_{ITS,de}"}, + {kItsTriton, o2::framework::kTH2F, "hItsPidTriton", "ITS PID Triton; p (GeV/#it{c}) ; n#sigma_{ITS,tr}"}, + {kItsHelium, o2::framework::kTH2F, "hItsPidHelium", "ITS PID Helium; p (GeV/#it{c}) ; n#sigma_{ITS,he}"}, + {kTpcSignal, o2::framework::kTH2F, "hTpcSignal", "TPC Signal; p (GeV/#it{c}) ; TPC Signal"}, + {kTpcElectron, o2::framework::kTH2F, "hTpcPidElectron", "TPC PID Electron; p (GeV/#it{c}) ; n#sigma_{TPC,el}"}, + {kTpcPion, o2::framework::kTH2F, "hTpcPidPion", "TPC PID Pion; p (GeV/#it{c}) ; n#sigma_{TPC,pi}"}, + {kTpcKaon, o2::framework::kTH2F, "hTpcPidKaon", "TPC PID Kaon; p (GeV/#it{c}) ; n#sigma_{TPC,ka}"}, + {kTpcProton, o2::framework::kTH2F, "hTpcPidProton", "TPC PID Proton; p (GeV/#it{c}) ; n#sigma_{TPC,pr}"}, + {kTpcDeuteron, o2::framework::kTH2F, "hTpcPidDeuteron", "TPC PID Deuteron; p (GeV/#it{c}) ; n#sigma_{TPC,de}"}, + {kTpcTriton, o2::framework::kTH2F, "hTpcPidTriton", "TPC PID Triton; p (GeV/#it{c}) ; n#sigma_{TPC,tr}"}, + {kTpcHelium, o2::framework::kTH2F, "hTpcPidHelium", "TPC PID Helium; p (GeV/#it{c}) ; n#sigma_{TPC,he}"}, + {kTofBeta, o2::framework::kTH2F, "hTofBeta", "TOF #beta; p (GeV/#it{c}) ; TOF #beta"}, + {kTofMass, o2::framework::kTH2F, "hTofMass", "TOF mass; p (GeV/#it{c}) ; m_{TOF} (GeV/#it{c}^{2})"}, + {kTofElectron, o2::framework::kTH2F, "hTofPidElectron", "TOF PID Electron; p (GeV/#it{c}) ; n#sigma_{TOF,el}"}, + {kTofPion, o2::framework::kTH2F, "hTofPidPion", "TOF PID Pion; p (GeV/#it{c}) ; n#sigma_{TOF,pi}"}, + {kTofKaon, o2::framework::kTH2F, "hTofPidKaon", "TOF PID Kaon; p (GeV/#it{c}) ; n#sigma_{TOF,ka}"}, + {kTofProton, o2::framework::kTH2F, "hTofPidProton", "TOF PID Proton; p (GeV/#it{c}) ; n#sigma_{TOF,pr}"}, + {kTofDeuteron, o2::framework::kTH2F, "hTofPidDeuteron", "TOF PID Deuteron; p (GeV/#it{c}) ; n#sigma_{TOF,de}"}, + {kTofTriton, o2::framework::kTH2F, "hTofPidTriton", "TOF PID Triton; p (GeV/#it{c}) ; n#sigma_{TOF,tr}"}, + {kTofHelium, o2::framework::kTH2F, "hTofPidHelium", "TOF PID Helium; p (GeV/#it{c}) ; n#sigma_{TOF,he}"}, + {kTpcitsElectron, o2::framework::kTH2F, "hTpcitsPidElectron", "its PID Electron; p (GeV/#it{c}) ; n#sigma_{its,el}"}, + {kTpcitsPion, o2::framework::kTH2F, "hTpcitsPidPion", "TPC+ITS PID Pion; p (GeV/#it{c}) ; #sqrt{n#sigma_{TPC,pi}^{2}+n#sigma_{its,pi}^{2}}"}, + {kTpcitsKaon, o2::framework::kTH2F, "hTpcitsPidKaon", "TPC+ITS PID Kaon; p (GeV/#it{c}) ; #sqrt{n#sigma_{TPC,ka}^{2}+n#sigma_{its,ka}^{2}}"}, + {kTpcitsProton, o2::framework::kTH2F, "hTpcitsPidProton", "TPC+ITS PID Proton; p (GeV/#it{c}) ; #sqrt{n#sigma_{TPC,pr}^{2}+n#sigma_{its,pr}^{2}}"}, + {kTpcitsDeuteron, o2::framework::kTH2F, "hTpcitsPidDeuteron", "TPC+ITS PID Deuteron; p (GeV/#it{c}) ; #sqrt{n#sigma_{TPC,de}^{2}+n#sigma_{its,de}^{2}}"}, + {kTpcitsTriton, o2::framework::kTH2F, "hTpcitsPidTriton", "TPC+ITS PID Triton; p (GeV/#it{c}) ; #sqrt{n#sigma_{TPC,tr}^{2}+n#sigma_{its,tr}^{2}}"}, + {kTpcitsHelium, o2::framework::kTH2F, "hTpcitsPidHelium", "TPC+ITS PID Helium; p (GeV/#it{c}) ; #sqrt{n#sigma_{TPC,he}^{2}+n#sigma_{its,he}^{2}}"}, + {kTpctofElectron, o2::framework::kTH2F, "hTpctofPidElectron", "TOF PID Electron; p (GeV/#it{c}) ; n#sigma_{TOF,el}"}, + {kTpctofPion, o2::framework::kTH2F, "hTpctofPidPion", "TPC+TOF PID Pion; p (GeV/#it{c}) ; #sqrt{n#sigma_{TPC,pi}^{2}+n#sigma_{TOF,pi}^{2}}"}, + {kTpctofKaon, o2::framework::kTH2F, "hTpctofPidKaon", "TPC+TOF PID Kaon; p (GeV/#it{c}) ; #sqrt{n#sigma_{TPC,ka}^{2}+n#sigma_{TOF,ka}^{2}}"}, + {kTpctofProton, o2::framework::kTH2F, "hTpctofPidProton", "TPC+TOF PID Proton; p (GeV/#it{c}) ; #sqrt{n#sigma_{TPC,pr}^{2}+n#sigma_{TOF,pr}^{2}}"}, + {kTpctofDeuteron, o2::framework::kTH2F, "hTpctofPidDeuteron", "TPC+TOF PID Deuteron; p (GeV/#it{c}) ; #sqrt{n#sigma_{TPC,de}^{2}+n#sigma_{TOF,de}^{2}}"}, + {kTpctofTriton, o2::framework::kTH2F, "hTpctofPidTriton", "TPC+TOF PID Triton; p (GeV/#it{c}) ; #sqrt{n#sigma_{TPC,tr}^{2}+n#sigma_{TOF,tr}^{2}}"}, + {kTpctofHelium, o2::framework::kTH2F, "hTpctofPidHelium", "TPC+TOF PID Helium; p (GeV/#it{c}) ; #sqrt{n#sigma_{TPC,he}^{2}+n#sigma_{TOF,he}^{2}}"}}}; + +template +auto makeTrackHistSpecMap(const T& confBinningAnalysis) +{ + return std::map>{ + {kPt, {confBinningAnalysis.pt}}, + {kEta, {confBinningAnalysis.eta}}, + {kPhi, {confBinningAnalysis.phi}}, + {kSign, {confBinningAnalysis.sign}}}; +}; + +template +auto makeTrackQaHistSpecMap(const T1& confBinningAnalysis, const T2 confiBinningQa) +{ + return std::map>{ + {kPt, {confBinningAnalysis.pt}}, + {kEta, {confBinningAnalysis.eta}}, + {kPhi, {confBinningAnalysis.phi}}, + {kSign, {confBinningAnalysis.sign}}, + {kItsCluster, {confiBinningQa.itsCluster}}, + {kItsClusterIb, {confiBinningQa.itsClusterIb}}, + {kPtVsEta, {confBinningAnalysis.pt, confBinningAnalysis.eta}}, + {kPtVsPhi, {confBinningAnalysis.pt, confBinningAnalysis.phi}}, + {kPhiVsEta, {confBinningAnalysis.phi, confBinningAnalysis.eta}}, + {kPtVsItsCluster, {confBinningAnalysis.pt, confiBinningQa.itsCluster}}, + {kPtVsTpcCluster, {confBinningAnalysis.pt, confiBinningQa.tpcCluster}}, + {kPtVsTpcClusterShared, {confBinningAnalysis.pt, confiBinningQa.tpcClusterShared}}, + {kTpcClusterVsTpcClusterShared, {confiBinningQa.tpcCluster, confiBinningQa.tpcClusterShared}}, + {kTpcCrossedRows, {confiBinningQa.tpcCrossedRows}}, + {kTpcCluster, {confiBinningQa.tpcCluster}}, + {kTpcClusterShared, {confiBinningQa.tpcClusterShared}}, + {kTpcClusterFractionShared, {confiBinningQa.tpcClusterFractionShared}}, + {kPtVsDcaxy, {confBinningAnalysis.pt, confiBinningQa.dcaXy}}, + {kPtVsDcaz, {confBinningAnalysis.pt, confiBinningQa.dcaZ}}, + {kPtVsDca, {confBinningAnalysis.pt, confiBinningQa.dca}}, + {kItsSignal, {confiBinningQa.p, confiBinningQa.itsSignal}}, + {kItsElectron, {confiBinningQa.p, confiBinningQa.itsElectron}}, + {kItsPion, {confiBinningQa.p, confiBinningQa.itsPion}}, + {kItsKaon, {confiBinningQa.p, confiBinningQa.itsKaon}}, + {kItsProton, {confiBinningQa.p, confiBinningQa.itsProton}}, + {kItsDeuteron, {confiBinningQa.p, confiBinningQa.itsDeuteron}}, + {kItsTriton, {confiBinningQa.p, confiBinningQa.itsTriton}}, + {kItsHelium, {confiBinningQa.p, confiBinningQa.itsHelium}}, + {kTpcSignal, {confiBinningQa.p, confiBinningQa.tpcSignal}}, + {kTpcElectron, {confiBinningQa.p, confiBinningQa.tpcElectron}}, + {kTpcPion, {confiBinningQa.p, confiBinningQa.tpcPion}}, + {kTpcKaon, {confiBinningQa.p, confiBinningQa.tpcKaon}}, + {kTpcProton, {confiBinningQa.p, confiBinningQa.tpcProton}}, + {kTpcDeuteron, {confiBinningQa.p, confiBinningQa.tpcDeuteron}}, + {kTpcTriton, {confiBinningQa.p, confiBinningQa.tpcTriton}}, + {kTpcHelium, {confiBinningQa.p, confiBinningQa.tpcHelium}}, + {kTofBeta, {confiBinningQa.p, confiBinningQa.tofBeta}}, + {kTofMass, {confiBinningQa.p, confiBinningQa.tofMass}}, + {kTofElectron, {confiBinningQa.p, confiBinningQa.tofElectron}}, + {kTofPion, {confiBinningQa.p, confiBinningQa.tofPion}}, + {kTofKaon, {confiBinningQa.p, confiBinningQa.tofKaon}}, + {kTofProton, {confiBinningQa.p, confiBinningQa.tofProton}}, + {kTofDeuteron, {confiBinningQa.p, confiBinningQa.tofDeuteron}}, + {kTofTriton, {confiBinningQa.p, confiBinningQa.tofTriton}}, + {kTofHelium, {confiBinningQa.p, confiBinningQa.tofHelium}}, + {kTpcitsElectron, {confiBinningQa.p, confiBinningQa.tpcitsElectron}}, + {kTpcitsPion, {confiBinningQa.p, confiBinningQa.tpcitsPion}}, + {kTpcitsKaon, {confiBinningQa.p, confiBinningQa.tpcitsKaon}}, + {kTpcitsProton, {confiBinningQa.p, confiBinningQa.tpcitsProton}}, + {kTpcitsDeuteron, {confiBinningQa.p, confiBinningQa.tpcitsDeuteron}}, + {kTpcitsTriton, {confiBinningQa.p, confiBinningQa.tpcitsTriton}}, + {kTpcitsHelium, {confiBinningQa.p, confiBinningQa.tpcitsHelium}}, + {kTpctofElectron, {confiBinningQa.p, confiBinningQa.tpctofElectron}}, + {kTpctofPion, {confiBinningQa.p, confiBinningQa.tpctofPion}}, + {kTpctofKaon, {confiBinningQa.p, confiBinningQa.tpctofKaon}}, + {kTpctofProton, {confiBinningQa.p, confiBinningQa.tpctofProton}}, + {kTpctofDeuteron, {confiBinningQa.p, confiBinningQa.tpctofDeuteron}}, + {kTpctofTriton, {confiBinningQa.p, confiBinningQa.tpctofTriton}}, + {kTpctofHelium, {confiBinningQa.p, confiBinningQa.tpctofHelium}}}; +}; + +constexpr char PrefixTrackQa[] = "TrackQA/"; +constexpr char PrefixTrack1[] = "Track1/"; +constexpr char PrefixTrack2[] = "Track2/"; +constexpr char PrefixTrack3[] = "Track3/"; + +constexpr char PrefixResonancePosDaughter[] = "ResonancePosDau/"; +constexpr char PrefixResonanceNegDaughter[] = "ResonanceNegDau/"; +constexpr char PrefixResonancePosDaughterQa[] = "ResonancePosDauQa/"; +constexpr char PrefixResonanceNegDaughterQa[] = "ResonanceNegDauQa/"; + +constexpr char PrefixV0PosDaughter[] = "V0PosDau/"; +constexpr char PrefixV0NegDaughter[] = "V0NegDau/"; +constexpr char PrefixV0PosDaughterQa[] = "V0PosDauQa/"; +constexpr char PrefixV0NegDaughterQa[] = "V0NegDauQa/"; + +constexpr char PrefixCascadePosDaughter[] = "CascadePosDau/"; +constexpr char PrefixCascadeNegDaughter[] = "CascadeNegDau/"; +constexpr char PrefixCascadeBachelor[] = "CascadeBachelor/"; +constexpr char PrefixCascadePosDaughterQa[] = "CascadePosDauQa/"; +constexpr char PrefixCascadeNegDaughterQa[] = "CascadeNegDauQa/"; +constexpr char PrefixCascadeBachelorQa[] = "CascadeBachelorQa/"; + +constexpr std::string_view AnalysisDir = "Kinematics/"; +constexpr std::string_view QaDir = "QA/"; +constexpr std::string_view PidDir = "PID/"; + +/// \class FemtoDreamEventHisto +/// \brief Class for histogramming event properties +// template +template +class TrackHistManager +{ + public: + /// Destructor + virtual ~TrackHistManager() = default; + + void init(o2::framework::HistogramRegistry* registry, std::map> Specs) + { + mHistogramRegistry = registry; + + if constexpr (isFlagSet(mode, modes::Mode::kAnalysis)) { + std::string analysisDir = std::string(prefix) + std::string(AnalysisDir); + + mHistogramRegistry->add(analysisDir + GetHistNamev2(kPt, HistTable), GetHistDesc(kPt, HistTable), GetHistType(kPt, HistTable), {Specs[kPt]}); + mHistogramRegistry->add(analysisDir + GetHistNamev2(kEta, HistTable), GetHistDesc(kEta, HistTable), GetHistType(kEta, HistTable), {Specs[kEta]}); + mHistogramRegistry->add(analysisDir + GetHistNamev2(kPhi, HistTable), GetHistDesc(kPhi, HistTable), GetHistType(kPhi, HistTable), {Specs[kPhi]}); + mHistogramRegistry->add(analysisDir + GetHistNamev2(kSign, HistTable), GetHistDesc(kSign, HistTable), GetHistType(kSign, HistTable), {Specs[kSign]}); + } + + if constexpr (isFlagSet(mode, modes::Mode::kQa)) { + std::string qaDir = std::string(prefix) + std::string(QaDir); + + mHistogramRegistry->add(qaDir + GetHistNamev2(kItsCluster, HistTable), GetHistDesc(kItsCluster, HistTable), GetHistType(kItsCluster, HistTable), {Specs[kItsCluster]}); + mHistogramRegistry->add(qaDir + GetHistNamev2(kItsClusterIb, HistTable), GetHistDesc(kItsClusterIb, HistTable), GetHistType(kItsClusterIb, HistTable), {Specs[kItsClusterIb]}); + mHistogramRegistry->add(qaDir + GetHistNamev2(kTpcCrossedRows, HistTable), GetHistDesc(kTpcCrossedRows, HistTable), GetHistType(kTpcCrossedRows, HistTable), {Specs[kTpcCrossedRows]}); + mHistogramRegistry->add(qaDir + GetHistNamev2(kTpcCluster, HistTable), GetHistDesc(kTpcCluster, HistTable), GetHistType(kTpcCluster, HistTable), {Specs[kTpcCluster]}); + mHistogramRegistry->add(qaDir + GetHistNamev2(kTpcClusterShared, HistTable), GetHistDesc(kTpcClusterShared, HistTable), GetHistType(kTpcClusterShared, HistTable), {Specs[kTpcClusterShared]}); + mHistogramRegistry->add(qaDir + GetHistNamev2(kTpcClusterFractionShared, HistTable), GetHistDesc(kTpcClusterFractionShared, HistTable), GetHistType(kTpcClusterFractionShared, HistTable), {Specs[kTpcClusterFractionShared]}); + + // qa 2d + mHistogramRegistry->add(qaDir + GetHistNamev2(kPtVsEta, HistTable), GetHistDesc(kPtVsEta, HistTable), GetHistType(kPtVsEta, HistTable), {Specs[kPtVsEta]}); + mHistogramRegistry->add(qaDir + GetHistNamev2(kPtVsPhi, HistTable), GetHistDesc(kPtVsPhi, HistTable), GetHistType(kPtVsPhi, HistTable), {Specs[kPtVsPhi]}); + mHistogramRegistry->add(qaDir + GetHistNamev2(kPhiVsEta, HistTable), GetHistDesc(kPhiVsEta, HistTable), GetHistType(kPhiVsEta, HistTable), {Specs[kPhiVsEta]}); + mHistogramRegistry->add(qaDir + GetHistNamev2(kPtVsItsCluster, HistTable), GetHistDesc(kPtVsItsCluster, HistTable), GetHistType(kPtVsItsCluster, HistTable), {Specs[kPtVsItsCluster]}); + mHistogramRegistry->add(qaDir + GetHistNamev2(kPtVsTpcCluster, HistTable), GetHistDesc(kPtVsTpcCluster, HistTable), GetHistType(kPtVsTpcCluster, HistTable), {Specs[kPtVsTpcCluster]}); + mHistogramRegistry->add(qaDir + GetHistNamev2(kPtVsTpcClusterShared, HistTable), GetHistDesc(kPtVsTpcClusterShared, HistTable), GetHistType(kPtVsTpcClusterShared, HistTable), {Specs[kPtVsTpcClusterShared]}); + mHistogramRegistry->add(qaDir + GetHistNamev2(kTpcClusterVsTpcClusterShared, HistTable), GetHistDesc(kTpcClusterVsTpcClusterShared, HistTable), GetHistType(kTpcClusterVsTpcClusterShared, HistTable), {Specs[kTpcClusterVsTpcClusterShared]}); + + // dca + mHistogramRegistry->add(qaDir + GetHistNamev2(kPtVsDcaxy, HistTable), GetHistDesc(kPtVsDcaxy, HistTable), GetHistType(kPtVsDcaxy, HistTable), {Specs[kPtVsDcaxy]}); + mHistogramRegistry->add(qaDir + GetHistNamev2(kPtVsDcaz, HistTable), GetHistDesc(kPtVsDcaz, HistTable), GetHistType(kPtVsDcaz, HistTable), {Specs[kPtVsDcaz]}); + mHistogramRegistry->add(qaDir + GetHistNamev2(kPtVsDca, HistTable), GetHistDesc(kPtVsDca, HistTable), GetHistType(kPtVsDca, HistTable), {Specs[kPtVsDca]}); + + std::string pidDir = std::string(prefix) + std::string(PidDir); + + mHistogramRegistry->add(pidDir + GetHistNamev2(kItsSignal, HistTable), GetHistDesc(kItsSignal, HistTable), GetHistType(kItsSignal, HistTable), {Specs[kItsSignal]}); + mHistogramRegistry->add(pidDir + GetHistNamev2(kItsElectron, HistTable), GetHistDesc(kItsElectron, HistTable), GetHistType(kItsElectron, HistTable), {Specs[kItsElectron]}); + mHistogramRegistry->add(pidDir + GetHistNamev2(kItsPion, HistTable), GetHistDesc(kItsPion, HistTable), GetHistType(kItsPion, HistTable), {Specs[kItsPion]}); + mHistogramRegistry->add(pidDir + GetHistNamev2(kItsKaon, HistTable), GetHistDesc(kItsKaon, HistTable), GetHistType(kItsKaon, HistTable), {Specs[kItsKaon]}); + mHistogramRegistry->add(pidDir + GetHistNamev2(kItsProton, HistTable), GetHistDesc(kItsProton, HistTable), GetHistType(kItsProton, HistTable), {Specs[kItsProton]}); + mHistogramRegistry->add(pidDir + GetHistNamev2(kItsDeuteron, HistTable), GetHistDesc(kItsDeuteron, HistTable), GetHistType(kItsDeuteron, HistTable), {Specs[kItsDeuteron]}); + mHistogramRegistry->add(pidDir + GetHistNamev2(kItsTriton, HistTable), GetHistDesc(kItsTriton, HistTable), GetHistType(kItsTriton, HistTable), {Specs[kItsTriton]}); + mHistogramRegistry->add(pidDir + GetHistNamev2(kItsHelium, HistTable), GetHistDesc(kItsHelium, HistTable), GetHistType(kItsHelium, HistTable), {Specs[kItsHelium]}); + + mHistogramRegistry->add(pidDir + GetHistNamev2(kTpcSignal, HistTable), GetHistDesc(kTpcSignal, HistTable), GetHistType(kTpcSignal, HistTable), {Specs[kTpcSignal]}); + mHistogramRegistry->add(pidDir + GetHistNamev2(kTpcElectron, HistTable), GetHistDesc(kTpcElectron, HistTable), GetHistType(kTpcElectron, HistTable), {Specs[kTpcElectron]}); + mHistogramRegistry->add(pidDir + GetHistNamev2(kTpcPion, HistTable), GetHistDesc(kTpcPion, HistTable), GetHistType(kTpcPion, HistTable), {Specs[kTpcPion]}); + mHistogramRegistry->add(pidDir + GetHistNamev2(kTpcKaon, HistTable), GetHistDesc(kTpcKaon, HistTable), GetHistType(kTpcKaon, HistTable), {Specs[kTpcKaon]}); + mHistogramRegistry->add(pidDir + GetHistNamev2(kTpcProton, HistTable), GetHistDesc(kTpcProton, HistTable), GetHistType(kTpcProton, HistTable), {Specs[kTpcProton]}); + mHistogramRegistry->add(pidDir + GetHistNamev2(kTpcDeuteron, HistTable), GetHistDesc(kTpcDeuteron, HistTable), GetHistType(kTpcDeuteron, HistTable), {Specs[kTpcDeuteron]}); + mHistogramRegistry->add(pidDir + GetHistNamev2(kTpcTriton, HistTable), GetHistDesc(kTpcTriton, HistTable), GetHistType(kTpcTriton, HistTable), {Specs[kTpcTriton]}); + mHistogramRegistry->add(pidDir + GetHistNamev2(kTpcHelium, HistTable), GetHistDesc(kTpcHelium, HistTable), GetHistType(kTpcHelium, HistTable), {Specs[kTpcHelium]}); + + mHistogramRegistry->add(pidDir + GetHistNamev2(kTofBeta, HistTable), GetHistDesc(kTofBeta, HistTable), GetHistType(kTofBeta, HistTable), {Specs[kTofBeta]}); + mHistogramRegistry->add(pidDir + GetHistNamev2(kTofMass, HistTable), GetHistDesc(kTofMass, HistTable), GetHistType(kTofMass, HistTable), {Specs[kTofMass]}); + mHistogramRegistry->add(pidDir + GetHistNamev2(kTofElectron, HistTable), GetHistDesc(kTofElectron, HistTable), GetHistType(kTofElectron, HistTable), {Specs[kTofElectron]}); + mHistogramRegistry->add(pidDir + GetHistNamev2(kTofPion, HistTable), GetHistDesc(kTofPion, HistTable), GetHistType(kTofPion, HistTable), {Specs[kTofPion]}); + mHistogramRegistry->add(pidDir + GetHistNamev2(kTofKaon, HistTable), GetHistDesc(kTofKaon, HistTable), GetHistType(kTofKaon, HistTable), {Specs[kTofKaon]}); + mHistogramRegistry->add(pidDir + GetHistNamev2(kTofProton, HistTable), GetHistDesc(kTofProton, HistTable), GetHistType(kTofProton, HistTable), {Specs[kTofProton]}); + mHistogramRegistry->add(pidDir + GetHistNamev2(kTofDeuteron, HistTable), GetHistDesc(kTofDeuteron, HistTable), GetHistType(kTofDeuteron, HistTable), {Specs[kTofDeuteron]}); + mHistogramRegistry->add(pidDir + GetHistNamev2(kTofTriton, HistTable), GetHistDesc(kTofTriton, HistTable), GetHistType(kTofTriton, HistTable), {Specs[kTofTriton]}); + mHistogramRegistry->add(pidDir + GetHistNamev2(kTofHelium, HistTable), GetHistDesc(kTofHelium, HistTable), GetHistType(kTofHelium, HistTable), {Specs[kTofHelium]}); + + mHistogramRegistry->add(pidDir + GetHistNamev2(kTpcitsElectron, HistTable), GetHistDesc(kTpcitsElectron, HistTable), GetHistType(kTpcitsElectron, HistTable), {Specs[kTpcitsElectron]}); + mHistogramRegistry->add(pidDir + GetHistNamev2(kTpcitsPion, HistTable), GetHistDesc(kTpcitsPion, HistTable), GetHistType(kTpcitsPion, HistTable), {Specs[kTpcitsPion]}); + mHistogramRegistry->add(pidDir + GetHistNamev2(kTpcitsKaon, HistTable), GetHistDesc(kTpcitsKaon, HistTable), GetHistType(kTpcitsKaon, HistTable), {Specs[kTpcitsKaon]}); + mHistogramRegistry->add(pidDir + GetHistNamev2(kTpcitsProton, HistTable), GetHistDesc(kTpcitsProton, HistTable), GetHistType(kTpcitsProton, HistTable), {Specs[kTpcitsProton]}); + mHistogramRegistry->add(pidDir + GetHistNamev2(kTpcitsDeuteron, HistTable), GetHistDesc(kTpcitsDeuteron, HistTable), GetHistType(kTpcitsDeuteron, HistTable), {Specs[kTpcitsDeuteron]}); + mHistogramRegistry->add(pidDir + GetHistNamev2(kTpcitsTriton, HistTable), GetHistDesc(kTpcitsTriton, HistTable), GetHistType(kTpcitsTriton, HistTable), {Specs[kTpcitsTriton]}); + mHistogramRegistry->add(pidDir + GetHistNamev2(kTpcitsHelium, HistTable), GetHistDesc(kTpcitsHelium, HistTable), GetHistType(kTpcitsHelium, HistTable), {Specs[kTpcitsHelium]}); + + mHistogramRegistry->add(pidDir + GetHistNamev2(kTpctofElectron, HistTable), GetHistDesc(kTpctofElectron, HistTable), GetHistType(kTpctofElectron, HistTable), {Specs[kTpctofElectron]}); + mHistogramRegistry->add(pidDir + GetHistNamev2(kTpctofPion, HistTable), GetHistDesc(kTpctofPion, HistTable), GetHistType(kTpctofPion, HistTable), {Specs[kTpctofPion]}); + mHistogramRegistry->add(pidDir + GetHistNamev2(kTpctofKaon, HistTable), GetHistDesc(kTpctofKaon, HistTable), GetHistType(kTpctofKaon, HistTable), {Specs[kTpctofKaon]}); + mHistogramRegistry->add(pidDir + GetHistNamev2(kTpctofProton, HistTable), GetHistDesc(kTpctofProton, HistTable), GetHistType(kTpctofProton, HistTable), {Specs[kTpctofProton]}); + mHistogramRegistry->add(pidDir + GetHistNamev2(kTpctofDeuteron, HistTable), GetHistDesc(kTpctofDeuteron, HistTable), GetHistType(kTpctofDeuteron, HistTable), {Specs[kTpctofDeuteron]}); + mHistogramRegistry->add(pidDir + GetHistNamev2(kTpctofTriton, HistTable), GetHistDesc(kTpctofTriton, HistTable), GetHistType(kTpctofTriton, HistTable), {Specs[kTpctofTriton]}); + mHistogramRegistry->add(pidDir + GetHistNamev2(kTpctofHelium, HistTable), GetHistDesc(kTpctofHelium, HistTable), GetHistType(kTpctofHelium, HistTable), {Specs[kTpctofHelium]}); + } + } + + template + void fill(T const& track) + { + if constexpr (isFlagSet(mode, modes::Mode::kAnalysis)) { + mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(GetHistName(kPt, HistTable)), track.pt()); + mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(GetHistName(kEta, HistTable)), track.eta()); + mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(GetHistName(kPhi, HistTable)), track.phi()); + mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(GetHistName(kSign, HistTable)), track.sign()); + } + + if constexpr (isFlagSet(mode, modes::Mode::kQa)) { + mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(GetHistName(kItsCluster, HistTable)), static_cast(track.itsNCls())); + mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(GetHistName(kItsClusterIb, HistTable)), static_cast(track.itsNClsInnerBarrel())); + mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(GetHistName(kTpcCrossedRows, HistTable)), static_cast(track.tpcNClsCrossedRows())); + mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(GetHistName(kTpcCluster, HistTable)), static_cast(track.tpcNClsFound())); + mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(GetHistName(kTpcClusterShared, HistTable)), static_cast(track.tpcNClsShared())); + mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(GetHistName(kTpcClusterFractionShared, HistTable)), track.tpcSharedOverFound()); + + mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(GetHistName(kPtVsEta, HistTable)), track.pt(), track.eta()); + mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(GetHistName(kPtVsPhi, HistTable)), track.pt(), track.phi()); + mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(GetHistName(kPhiVsEta, HistTable)), track.phi(), track.eta()); + mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(GetHistName(kPtVsItsCluster, HistTable)), track.pt(), static_cast(track.itsNCls())); + mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(GetHistName(kPtVsTpcCluster, HistTable)), track.pt(), static_cast(track.tpcNClsFound())); + mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(GetHistName(kPtVsTpcClusterShared, HistTable)), track.pt(), static_cast(track.tpcNClsShared())); + mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(GetHistName(kTpcClusterVsTpcClusterShared, HistTable)), static_cast(track.tpcNClsFound()), static_cast(track.tpcNClsShared())); + + mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(GetHistName(kPtVsDcaxy, HistTable)), track.pt(), track.dcaXY()); + mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(GetHistName(kPtVsDcaz, HistTable)), track.pt(), track.dcaZ()); + mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(GetHistName(kPtVsDca, HistTable)), track.pt(), track.dca()); + + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(GetHistName(kItsSignal, HistTable)), track.p(), o2::analysis::femto::utils::itsSignal(track)); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(GetHistName(kItsElectron, HistTable)), track.p(), track.itsNSigmaEl()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(GetHistName(kItsPion, HistTable)), track.p(), track.itsNSigmaPi()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(GetHistName(kItsKaon, HistTable)), track.p(), track.itsNSigmaKa()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(GetHistName(kItsProton, HistTable)), track.p(), track.itsNSigmaPr()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(GetHistName(kItsDeuteron, HistTable)), track.p(), track.itsNSigmaDe()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(GetHistName(kItsTriton, HistTable)), track.p(), track.itsNSigmaTr()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(GetHistName(kItsHelium, HistTable)), track.p(), track.itsNSigmaHe()); + + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(GetHistName(kTpcSignal, HistTable)), track.p(), track.tpcSignal()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(GetHistName(kTpcElectron, HistTable)), track.p(), track.tpcNSigmaEl()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(GetHistName(kTpcPion, HistTable)), track.p(), track.tpcNSigmaPi()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(GetHistName(kTpcKaon, HistTable)), track.p(), track.tpcNSigmaKa()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(GetHistName(kTpcProton, HistTable)), track.p(), track.tpcNSigmaPr()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(GetHistName(kTpcDeuteron, HistTable)), track.p(), track.tpcNSigmaDe()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(GetHistName(kTpcTriton, HistTable)), track.p(), track.tpcNSigmaTr()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(GetHistName(kTpcHelium, HistTable)), track.p(), track.tpcNSigmaHe()); + + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(GetHistName(kTofBeta, HistTable)), track.p(), track.tofBeta()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(GetHistName(kTofMass, HistTable)), track.p(), track.tofMass()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(GetHistName(kTofElectron, HistTable)), track.p(), track.tofNSigmaEl()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(GetHistName(kTofPion, HistTable)), track.p(), track.tofNSigmaPi()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(GetHistName(kTofKaon, HistTable)), track.p(), track.tofNSigmaKa()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(GetHistName(kTofProton, HistTable)), track.p(), track.tofNSigmaPr()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(GetHistName(kTofDeuteron, HistTable)), track.p(), track.tofNSigmaDe()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(GetHistName(kTofTriton, HistTable)), track.p(), track.tofNSigmaTr()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(GetHistName(kTofHelium, HistTable)), track.p(), track.tofNSigmaHe()); + + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(GetHistName(kTpcitsElectron, HistTable)), track.p(), track.tpcitsNSigmaEl()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(GetHistName(kTpcitsPion, HistTable)), track.p(), track.tpcitsNSigmaPi()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(GetHistName(kTpcitsKaon, HistTable)), track.p(), track.tpcitsNSigmaKa()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(GetHistName(kTpcitsProton, HistTable)), track.p(), track.tpcitsNSigmaPr()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(GetHistName(kTpcitsDeuteron, HistTable)), track.p(), track.tpcitsNSigmaDe()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(GetHistName(kTpcitsTriton, HistTable)), track.p(), track.tpcitsNSigmaTr()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(GetHistName(kTpcitsHelium, HistTable)), track.p(), track.tpcitsNSigmaHe()); + + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(GetHistName(kTpctofElectron, HistTable)), track.p(), track.tpctofNSigmaEl()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(GetHistName(kTpctofPion, HistTable)), track.p(), track.tpctofNSigmaPi()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(GetHistName(kTpctofKaon, HistTable)), track.p(), track.tpctofNSigmaKa()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(GetHistName(kTpctofProton, HistTable)), track.p(), track.tpctofNSigmaPr()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(GetHistName(kTpctofDeuteron, HistTable)), track.p(), track.tpctofNSigmaDe()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(GetHistName(kTpctofTriton, HistTable)), track.p(), track.tpctofNSigmaTr()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(GetHistName(kTpctofHelium, HistTable)), track.p(), track.tpctofNSigmaHe()); + } + } + + private: + o2::framework::HistogramRegistry* mHistogramRegistry; +}; +}; // namespace trackhistmanager +}; // namespace o2::analysis::femto +#endif // PWGCF_FEMTO_CORE_TRACKHISTMANAGER_H_ diff --git a/PWGCF/Femto/Core/twoTrackResonanceBuilder.h b/PWGCF/Femto/Core/twoTrackResonanceBuilder.h new file mode 100644 index 00000000000..e4c4dbf7501 --- /dev/null +++ b/PWGCF/Femto/Core/twoTrackResonanceBuilder.h @@ -0,0 +1,608 @@ +// 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 twoTrackResonanceBuilder.h +/// \brief two track resonance builder +/// \author anton.riedel@tum.de, TU München, anton.riedel@tum.de + +#ifndef PWGCF_FEMTO_CORE_TWOTRACKRESONANCEBUILDER_H_ +#define PWGCF_FEMTO_CORE_TWOTRACKRESONANCEBUILDER_H_ + +#include "RecoDecay.h" + +#include "PWGCF/Femto/Core/baseSelection.h" +#include "PWGCF/Femto/Core/dataTypes.h" +#include "PWGCF/Femto/Core/femtoUtils.h" +#include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/Core/selectionContainer.h" +#include "PWGCF/Femto/DataModel/FemtoTables.h" + +#include "CommonConstants/MathConstants.h" +#include "CommonConstants/PhysicsConstants.h" +#include "Framework/AnalysisHelpers.h" +#include "Framework/Configurable.h" + +#include "Math/Vector4Dfwd.h" + +#include "fairlogger/Logger.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace o2::analysis::femto +{ +namespace twotrackresonancebuilder +{ + +struct ConfTwoTrackResonanceDaughterFilters : o2::framework::ConfigurableGroup { + std::string prefix = std::string("TwoTrackResonanceDaughterFilter"); + o2::framework::Configurable ptMin{"ptMin", 0.2f, "Minimum pT of daughters"}; + o2::framework::Configurable ptMax{"ptMax", 6.f, "Maximum pT of daughters"}; + o2::framework::Configurable etaMin{"etaMin", -0.9f, "Minimum eta of daughters"}; + o2::framework::Configurable etaMax{"etaMax", 0.9f, "Maximum eta of daughters"}; + o2::framework::Configurable phiMin{"phiMin", 0.f, "Minimum phi of daughters"}; + o2::framework::Configurable phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi of daughters"}; +}; + +template +struct ConfTwoTrackResonanceFilters : o2::framework::ConfigurableGroup { + std::string prefix = Prefix; + o2::framework::Configurable ptMin{"ptMin", 0.2f, "Minimum pT"}; + o2::framework::Configurable ptMax{"ptMax", 6.f, "Maximum pT"}; + o2::framework::Configurable etaMin{"etaMin", -0.9f, "Minimum eta"}; + o2::framework::Configurable etaMax{"etaMax", 0.9f, "Maximum eta"}; + o2::framework::Configurable phiMin{"phiMin", 0.f, "Minimum phi"}; + o2::framework::Configurable phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"}; + o2::framework::Configurable massMin{"massMin", 0.f, "Minimum invariant mass for Resonance"}; + o2::framework::Configurable massMax{"massMax", 6.f, "Maximum invariant mass for Resonance"}; +}; +constexpr const char PrefixRhoFilters[] = "Rho0Filters1"; +constexpr const char PrefixPhiFilters[] = "PhiFilters1"; +constexpr const char PrefixKstarFilters[] = "Kstar0Filters1"; +using ConfRhoFilters = ConfTwoTrackResonanceFilters; +using ConfPhiFilters = ConfTwoTrackResonanceFilters; +using ConfKstarFilters = ConfTwoTrackResonanceFilters; + +#define TWOTRACKRESONANCE_DEFAULT_BITS(posThres, negThres) \ + o2::framework::Configurable> dauEtaMax{"dauEtaMax", {0.8f}, "Maximum |eta| "}; \ + o2::framework::Configurable> dauTpcClustersMin{"dauTpcClustersMin", {90.f}, "Minimum number of clusters in TPC"}; \ + o2::framework::Configurable> dauDcaxyMax{"dauDcaxyMax", {"0.004 + 0.013*pow(x, -1)"}, "Maximum |dca_xy| as a function of pT"}; \ + o2::framework::Configurable> dauDcazMax{"dauDcazMax", {"0.004 + 0.013*pow(x, -1)"}, "Maximum |dca_z| as a function of pT"}; \ + o2::framework::Configurable> posDauPtMin{"posDauPtMin", {0.2f}, "Minimum pT of positive daughter "}; \ + o2::framework::Configurable> posDauPtMax{"posDauPtMax", {6.f}, "Maximum pT of the positive daughter"}; \ + o2::framework::Configurable> negDauPtMin{"negDauPtMin", {0.2f}, "Minimum pT of negative daughter "}; \ + o2::framework::Configurable> negDauPtMax{"negDauPtMax", {6.f}, "Maximum pT of the negative daughter"}; \ + o2::framework::Configurable> posDauMinMomForTof{"posDauMinMomForTof", {posThres}, "Minimum momentum to require TOF PID (positive daughters)"}; \ + o2::framework::Configurable> negDauMinMomForTof{"negDauMinMomForTof", {negThres}, "Minimum momentum to require TOF PID (negative daughters)"}; + +#define TWOTRACKRESONANCE_PIONPID_BITS \ + o2::framework::Configurable> posDauTpcPion{"posDauTpcPion", {3.f}, "Maximum |nsimga_Pion| TPC for positive daughter tracks"}; \ + o2::framework::Configurable> posDauTofPion{"posDauTofPion", {}, "Maximum |nsimga_Pion| TOF for positive daughter tracks"}; \ + o2::framework::Configurable> posDauTpctofPion{"posDauTpctofPion", {3.f}, "Maximum |nsimga_Pion| TPCTOF for positive daughter tracks"}; \ + o2::framework::Configurable> negDauTpcPion{"negDauTpcPion", {3.f}, "Maximum |nsimga_Pion| TPC for negative daughter tracks"}; \ + o2::framework::Configurable> negDauTofPion{"negDauTofPion", {}, "Maximum |nsimga_Pion| TOF for negative daughter tracks"}; \ + o2::framework::Configurable> negDauTpctofPion{"negDauTpctofPion", {3.f}, "Maximum |nsimga_Pion| TPCTOF for negative daughter tracks"}; + +#define TWOTRACKRESONANCE_KAONPID_BITS \ + o2::framework::Configurable> posDauTpcKaon{"posDauTpcKaon", {3.f}, "Maximum |nsimga_Kaon| TPC for positive daughter tracks"}; \ + o2::framework::Configurable> posDauTofKaon{"posDauTofKaon", {}, "Maximum |nsimga_Kaon| TOF for positive daughter tracks"}; \ + o2::framework::Configurable> posDauTpctofKaon{"posDauTpctofKaon", {3.f}, "Maximum |nsimga_Kaon| TPCTOF for positive daughter tracks"}; \ + o2::framework::Configurable> negDauTpcKaon{"negDauTpcKaon", {3.f}, "Maximum |nsimga_Kaon| TPC for negative daughter tracks"}; \ + o2::framework::Configurable> negDauTofKaon{"negDauTofKaon", {}, "Maximum |nsimga_Kaon| TOF for negative daughter tracks"}; \ + o2::framework::Configurable> negDauTpctofKaon{"negDauTpctofKaon", {3.f}, "Maximum |nsimga_Kaon| TPCTOF for negative daughter tracks"}; + +struct ConfPhiBits : o2::framework::ConfigurableGroup { + std::string prefix = std::string("PhiBits"); + TWOTRACKRESONANCE_DEFAULT_BITS(0.4f, 0.4f) + TWOTRACKRESONANCE_KAONPID_BITS +}; + +struct ConfRho0Bits : o2::framework::ConfigurableGroup { + std::string prefix = std::string("Rho0Bits"); + TWOTRACKRESONANCE_DEFAULT_BITS(0.5f, 0.5f) + TWOTRACKRESONANCE_PIONPID_BITS +}; + +struct ConfKstar0Bits : o2::framework::ConfigurableGroup { + std::string prefix = std::string("Kstar0Bits"); + TWOTRACKRESONANCE_DEFAULT_BITS(0.5f, 0.4f) + TWOTRACKRESONANCE_PIONPID_BITS + TWOTRACKRESONANCE_KAONPID_BITS +}; + +#undef TWOTRACKRESONANCE_DEFAULT_BITS +#undef TWOTRACKRESONANCE_KAONPID_BITS +#undef TWOTRACKRESONANCE_PIONPID_BITS + +#define TWOTRACKRESONANCE_DEFAULT_SELECTION(defaultMassMin, defaultMassMax) \ + o2::framework::Configurable ptMin{"ptMin", 0.f, "Minimum pT"}; \ + o2::framework::Configurable ptMax{"ptMax", 6.f, "Maximum pT"}; \ + o2::framework::Configurable etaMin{"etaMin", -0.9f, "Minimum eta"}; \ + o2::framework::Configurable etaMax{"etaMax", 0.9f, "Maximum eta"}; \ + o2::framework::Configurable phiMin{"phiMin", 0.f, "Minimum phi"}; \ + o2::framework::Configurable phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"}; \ + o2::framework::Configurable massMin{"massMin", defaultMassMin, "Minimum invariant mass for Resonance"}; \ + o2::framework::Configurable massMax{"massMax", defaultMassMax, "Maximum invariant mass for Resonance"}; \ + o2::framework::Configurable posDauBitForThres{"posDauBitForThres", 32u, "Bit marking momentum threshold for positive daughter"}; \ + o2::framework::Configurable posDauMaskBelowThres{"posDauMaskBelowThres", 16u, "Bitmask for resonance selection"}; \ + o2::framework::Configurable posDauMaskAboveThres{"posDauMaskAboveThres", 8u, "Bitmask for resonance selection"}; \ + o2::framework::Configurable negDauBitForThres{"negDauBitForThres", 4u, "Bit marking resonance selection for negative daughter"}; \ + o2::framework::Configurable negDauMaskBelowThres{"negDauMaskBelowThres", 2u, "Bitmask for resonance selection"}; \ + o2::framework::Configurable negDauMaskAboveThres{"negDauMaskAboveThres", 1u, "Bitmask for resonance selection"}; + +struct ConfPhiSelection : o2::framework::ConfigurableGroup { + std::string prefix = std::string("PhiSelection"); + TWOTRACKRESONANCE_DEFAULT_SELECTION(0.95f, 1.05f) +}; + +struct ConfRho0Selection : o2::framework::ConfigurableGroup { + std::string prefix = std::string("RhoSelection"); + TWOTRACKRESONANCE_DEFAULT_SELECTION(0.7f, 0.84f) +}; + +struct ConfKstar0Selection : o2::framework::ConfigurableGroup { + std::string prefix = std::string("Kstar0Selection"); + o2::framework::Configurable sign{"sign", 1, "Sign (+1 for Kstar0 and -1 for Kstar0Bar) "}; + TWOTRACKRESONANCE_DEFAULT_SELECTION(0.8f, 1.0f) +}; + +#undef TWOTRACKRESONANCE_DEFAULT_SELECTION + +/// The different selections this task is capable of doing +enum TwoTrackResonanceSels { + + // common selections for both daughters + kDauEtaAbsMax, ///< max |eta| + kDauTpcClusterMin, ///< min number of TPC cluster + kDauDcaxyAbsMax, ///< max |DCA_xy| + kDauDcazAbsMax, ///< max |DCA_z| + + // selection for positive daughter + // add one bit for the momentum threshold + // when the partition for a resonance is build, we do not have information about the daughter tracks so have to store everything needed for the selection here + kPosDauMinMomForTof, ///< min p for TOF + kPosDauPtMin, ///< min pt + kPosDauPtMax, ///< max pt + kPosDauTpcPion, /// < max |nsigma_TPC| for pion + kPosDauTofPion, /// < max |nsigma_TOF| for pion + kPosDauTpctofPion, /// < max |nsigma_TPC+TOF| for pion + kPosDauTpcKaon, /// < max |nsigma_TPC| for kaon + kPosDauTofKaon, /// < max |nsigma_TOF| for kaon + kPosDauTpctofKaon, /// < max |nsigma_TPC+TOF| for kaon + + // selection for negative daughter + kNegDauMinMomForTof, ///< min p for TOF + kNegDauPtMin, ///< min pt + kNegDauPtMax, ///< max pt + kNegDauTpcPion, /// < max |nsigma_TPC| for pion + kNegDauTofPion, /// < max |nsigma_TOF| for pion + kNegDauTpctofPion, /// < max |nsigma_TPC+TOF| for pion + kNegDauTpcKaon, /// < max |nsigma_TPC| for kaon + kNegDauTofKaon, /// < max |nsigma_TOF| for kaon + kNegDauTpctofKaon, /// < max |nsigma_TPC+TOF| for kaon + + kResonanceSelsMax +}; + +const char twoTrackResonanceSelsName[] = "TwoTrackResonance Selection Object"; +const std::unordered_map twoTrackResonanceSelsToString = { + {kDauEtaAbsMax, "Max. |eta| of daughters"}, + {kDauTpcClusterMin, "Min. number of TPC clusters of daughters"}, + {kDauDcaxyAbsMax, "Max. |DCA_xy| of daughters"}, + {kDauDcazAbsMax, "Max. |DCA_z| of the daughters"}, + {kPosDauMinMomForTof, "Min. p of TOF PID of positive daughter"}, + {kPosDauPtMin, "Min. pt of positive daughter"}, + {kPosDauPtMax, "Max. pt of positive daughter"}, + {kPosDauTpcPion, "Max. |sigma_TPC| for pion of positive daughter"}, + {kPosDauTofPion, "Max. |sigma_TOF| for pion of positive daughter"}, + {kPosDauTpctofPion, "Max. |sigma_TPCTOF| for pion of positive daughter"}, + {kPosDauTpcKaon, "Max. |sigma_TPC| for kaon of positive daughter"}, + {kPosDauTofKaon, "Max. |sigma_TOF| for kaon of positive daughter"}, + {kPosDauTpctofKaon, "Max. |sigma_TPCTOF| for kaon of positive daughter"}, + {kNegDauMinMomForTof, "Min. p for TOF PID of negative daughter"}, + {kNegDauPtMin, "Min. pt of negative daughter"}, + {kNegDauPtMax, "Max. pt of negative daughter"}, + {kNegDauTpcPion, "Max. |sigma_TPC| for pion of negative daughter"}, + {kNegDauTofPion, "Max. |sigma_TOF| for pion of negative daughter"}, + {kNegDauTpctofPion, "Max. |sigma_TPCTOF| for pion of negative daughter"}, + {kNegDauTpcKaon, "Max. |sigma_TPC| for kaon of negative daughter"}, + {kNegDauTofKaon, "Max. |sigma_TOF| for kaon of negative daughter"}, + {kNegDauTpctofKaon, "Max. |sigma_TPCTOF| for kaon of negative daughter"}}; + +/// \class FemtoDreamTrackCuts +/// \brief Cut class to contain and execute all cuts applied to tracks +template +class TwoTrackResonanceSelection : public BaseSelection +{ + public: + TwoTrackResonanceSelection() {} + virtual ~TwoTrackResonanceSelection() = default; + + template + void configure(T1& config, T2& filter, T3& daughterFilter) + { + if constexpr (modes::isEqual(resoType, modes::TwoTrackResonance::kPhi)) { + mPosDaughterMass = o2::constants::physics::MassKPlus; + mNegDaughterMass = o2::constants::physics::MassKMinus; + this->addSelection(config.posDauTpcKaon.value, kPosDauTpcKaon, limits::kAbsUpperLimit, false, false); + this->addSelection(config.posDauTofKaon.value, kPosDauTofKaon, limits::kAbsUpperLimit, false, false); + this->addSelection(config.posDauTpctofKaon.value, kPosDauTpctofKaon, limits::kUpperLimit, false, false); + this->addSelection(config.negDauTpcKaon.value, kNegDauTpcKaon, limits::kAbsUpperLimit, false, false); + this->addSelection(config.negDauTofKaon.value, kNegDauTofKaon, limits::kAbsUpperLimit, false, false); + this->addSelection(config.negDauTpctofKaon.value, kNegDauTpctofKaon, limits::kUpperLimit, false, false); + } + if constexpr (modes::isEqual(resoType, modes::TwoTrackResonance::kRho0)) { + mPosDaughterMass = o2::constants::physics::MassPiPlus; + mNegDaughterMass = o2::constants::physics::MassPiMinus; + this->addSelection(config.posDauTpcPion.value, kPosDauTpcPion, limits::kAbsUpperLimit, false, false); + this->addSelection(config.posDauTofPion.value, kPosDauTofPion, limits::kAbsUpperLimit, false, false); + this->addSelection(config.posDauTpctofPion.value, kPosDauTpctofPion, limits::kUpperLimit, false, false); + this->addSelection(config.negDauTpcPion.value, kNegDauTpcPion, limits::kAbsUpperLimit, false, false); + this->addSelection(config.negDauTofPion.value, kNegDauTofPion, limits::kAbsUpperLimit, false, false); + this->addSelection(config.negDauTpctofPion.value, kNegDauTpctofPion, limits::kUpperLimit, false, false); + } + if constexpr (modes::isEqual(resoType, modes::TwoTrackResonance::kKstar0)) { + mPosDaughterMass = o2::constants::physics::MassKPlus; + mNegDaughterMass = o2::constants::physics::MassPiMinus; + this->addSelection(config.posDauTpcKaon.value, kPosDauTpcKaon, limits::kAbsUpperLimit, false, false); + this->addSelection(config.posDauTofKaon.value, kPosDauTofKaon, limits::kAbsUpperLimit, false, false); + this->addSelection(config.posDauTpctofKaon.value, kPosDauTpctofKaon, limits::kUpperLimit, false, false); + this->addSelection(config.negDauTpcPion.value, kNegDauTpcPion, limits::kAbsUpperLimit, false, false); + this->addSelection(config.negDauTofPion.value, kNegDauTofPion, limits::kAbsUpperLimit, false, false); + this->addSelection(config.negDauTpctofPion.value, kNegDauTpctofPion, limits::kUpperLimit, false, false); + } + if constexpr (modes::isEqual(resoType, modes::TwoTrackResonance::kKstar0Bar)) { + mPosDaughterMass = o2::constants::physics::MassPiPlus; + mNegDaughterMass = o2::constants::physics::MassKMinus; + this->addSelection(config.posDauTpcPion.value, kPosDauTpcPion, limits::kAbsUpperLimit, false, false); + this->addSelection(config.posDauTofPion.value, kPosDauTofPion, limits::kAbsUpperLimit, false, false); + this->addSelection(config.posDauTpctofPion.value, kPosDauTpctofPion, limits::kUpperLimit, false, false); + this->addSelection(config.negDauTpcKaon.value, kNegDauTpcKaon, limits::kAbsUpperLimit, false, false); + this->addSelection(config.negDauTofKaon.value, kNegDauTofKaon, limits::kAbsUpperLimit, false, false); + this->addSelection(config.negDauTpctofKaon.value, kNegDauTpctofKaon, limits::kUpperLimit, false, false); + } + + mMassMin = filter.massMin.value; + mMassMax = filter.massMax.value; + mPtMin = filter.ptMin.value; + mPtMax = filter.ptMax.value; + mEtaMin = filter.etaMin.value; + mEtaMax = filter.etaMax.value; + mPhiMin = filter.phiMin.value; + mPhiMax = filter.phiMax.value; + + this->addSelection(config.dauEtaMax.value, kDauEtaAbsMax, limits::kAbsUpperLimit, true, true); + this->addSelection(config.dauTpcClustersMin.value, kDauTpcClusterMin, limits::kLowerLimit, true, true); + this->addSelection(config.dauDcaxyMax.name, daughterFilter.ptMin.value, daughterFilter.ptMax.value, config.dauDcaxyMax.value, kDauDcaxyAbsMax, limits::kAbsUpperFunctionLimit, true, true); + this->addSelection(config.dauDcazMax.name, daughterFilter.ptMin.value, daughterFilter.ptMax.value, config.dauDcazMax.value, kDauDcazAbsMax, limits::kAbsUpperFunctionLimit, true, true); + + this->addSelection(config.posDauMinMomForTof.value, kPosDauMinMomForTof, limits::kLowerLimit, false, false); // momentum threshold for TOF is no minimal cut + this->addSelection(config.posDauPtMin.value, kPosDauPtMin, limits::kLowerLimit, true, true); + this->addSelection(config.posDauPtMax.value, kPosDauPtMax, limits::kUpperLimit, true, true); + + this->addSelection(config.negDauMinMomForTof.value, kNegDauMinMomForTof, limits::kLowerLimit, false, false); // momentum threshold for TOF is no minimal cut + this->addSelection(config.negDauPtMin.value, kNegDauPtMin, limits::kLowerLimit, true, true); + this->addSelection(config.negDauPtMax.value, kNegDauPtMax, limits::kUpperLimit, true, true); + }; + + template + void reconstructResonance(Tracks const& posDaughter, Tracks const& negDaughter) + { + + ROOT::Math::PtEtaPhiMVector vecPosDaughter{posDaughter.pt(), posDaughter.eta(), posDaughter.phi(), mPosDaughterMass}; + ROOT::Math::PtEtaPhiMVector vecNegDaughter{negDaughter.pt(), negDaughter.eta(), negDaughter.phi(), mNegDaughterMass}; + ROOT::Math::PtEtaPhiMVector vecResonance = vecPosDaughter + vecNegDaughter; + + // cache kinematics + mMass = vecResonance.M(); + mPt = vecResonance.Pt(); + mEta = vecResonance.Eta(); + mPhi = RecoDecay::constrainAngle(vecResonance.Phi()); + } + + bool checkFilters() const + { + return ((mMass > mMassMin && mMass < mMassMax) && + (mPt > mPtMin && mPt < mPtMax) && + (mEta > mEtaMin && mEta < mEtaMax) && + (mPhi > mPhiMin && mPhi < mPhiMax)); + } + + template + bool hasTofAboveThreshold(T const& positiveDaughter, T const& negativeDaughter) + { + bool posDauHasTofAboveThreshold = !this->passesOptionalSelection(kPosDauMinMomForTof) || positiveDaughter.hasTOF(); // is always true if momentum is below threshold + bool negDauHasTofAboveThreshold = !this->passesOptionalSelection(kNegDauMinMomForTof) || negativeDaughter.hasTOF(); // is always true if momentum is below threshold + return posDauHasTofAboveThreshold && negDauHasTofAboveThreshold; + } + + float getPt() const { return mPt; } + float getEta() const { return mEta; } + float getPhi() const { return mPhi; } + float getMass() const { return mMass; } + + template + void applySelections(Tracks const& posDaughter, Tracks const& negDaughter) + { + this->reset(); + // for resoanace topological selectsion are in general not possible, so only selections on the daughters are performed + + // common daugher selections + std::array etaDaughters = {std::fabs(posDaughter.eta()), std::fabs(negDaughter.eta())}; + this->evaluateObservable(kDauEtaAbsMax, *std::max_element(etaDaughters.begin(), etaDaughters.end())); + std::array tpcClusterDaughters = {1.f * posDaughter.tpcNClsFound(), 1.f * negDaughter.tpcNClsFound()}; + this->evaluateObservable(kDauTpcClusterMin, *std::min_element(tpcClusterDaughters.begin(), tpcClusterDaughters.end())); + + // check pt dependend dca cut on both daughters + // we apply the same cut to both daughters so we only want to store the result were both daughters survive the cut + // since momenta of daughters are different, we compute the bitmask for both, combine them with logical AND and keep the result + uint64_t bitmaskDcaPos, bitmaskDcaNeg, bitmaskDca; + this->updateLimits(kDauDcaxyAbsMax, posDaughter.pt()); + this->evaluateObservable(kDauDcaxyAbsMax, posDaughter.dcaXY()); + bitmaskDcaPos = this->getBitmask(kDauDcaxyAbsMax); + this->updateLimits(kDauDcaxyAbsMax, negDaughter.pt()); + this->evaluateObservable(kDauDcaxyAbsMax, negDaughter.dcaXY()); + bitmaskDcaNeg = this->getBitmask(kDauDcaxyAbsMax); + bitmaskDca = bitmaskDcaPos & bitmaskDcaNeg; + this->setBitmask(kDauDcaxyAbsMax, bitmaskDca); + + this->updateLimits(kDauDcazAbsMax, posDaughter.pt()); + this->evaluateObservable(kDauDcazAbsMax, posDaughter.dcaZ()); + bitmaskDcaPos = this->getBitmask(kDauDcazAbsMax); + this->updateLimits(kDauDcazAbsMax, negDaughter.pt()); + this->evaluateObservable(kDauDcazAbsMax, negDaughter.dcaZ()); + bitmaskDcaNeg = this->getBitmask(kDauDcazAbsMax); + bitmaskDca = bitmaskDcaPos & bitmaskDcaNeg; + this->setBitmask(kDauDcazAbsMax, bitmaskDca); + + // positive daughter selections + this->evaluateObservable(kPosDauMinMomForTof, posDaughter.p()); + this->evaluateObservable(kPosDauPtMin, posDaughter.pt()); + this->evaluateObservable(kPosDauPtMax, posDaughter.pt()); + + this->evaluateObservable(kPosDauTpcPion, posDaughter.tpcNSigmaPi()); + this->evaluateObservable(kPosDauTofPion, posDaughter.tofNSigmaPi()); + this->evaluateObservable(kPosDauTpctofPion, std::hypot(posDaughter.tpcNSigmaPi(), posDaughter.tofNSigmaPi())); + + this->evaluateObservable(kPosDauTpcKaon, posDaughter.tpcNSigmaKa()); + this->evaluateObservable(kPosDauTofKaon, posDaughter.tofNSigmaKa()); + this->evaluateObservable(kPosDauTpctofKaon, std::hypot(posDaughter.tpcNSigmaKa(), posDaughter.tofNSigmaKa())); + + // negative daughter selections + this->evaluateObservable(kNegDauMinMomForTof, negDaughter.p()); + this->evaluateObservable(kNegDauPtMin, negDaughter.pt()); + this->evaluateObservable(kNegDauPtMax, negDaughter.pt()); + + this->evaluateObservable(kNegDauTpcPion, negDaughter.tpcNSigmaPi()); + this->evaluateObservable(kNegDauTofPion, negDaughter.tofNSigmaPi()); + this->evaluateObservable(kNegDauTpctofPion, std::hypot(negDaughter.tpcNSigmaPi(), negDaughter.tofNSigmaPi())); + + this->evaluateObservable(kNegDauTpcKaon, negDaughter.tpcNSigmaKa()); + this->evaluateObservable(kNegDauTofKaon, negDaughter.tofNSigmaKa()); + this->evaluateObservable(kNegDauTpctofKaon, std::hypot(negDaughter.tpcNSigmaKa(), negDaughter.tofNSigmaKa())); + + this->assembleBitmask(); + }; + + bool checkHypothesis() + { + if constexpr (modes::isEqual(resoType, modes::TwoTrackResonance::kRho0)) { + return (this->passesOptionalSelection(kPosDauTpcPion) || this->passesOptionalSelection(kPosDauTofPion) || this->passesOptionalSelection(kPosDauTpctofPion)) && + (this->passesOptionalSelection(kNegDauTpcPion) || this->passesOptionalSelection(kNegDauTofPion) || this->passesOptionalSelection(kNegDauTpctofPion)); + } + if constexpr (modes::isEqual(resoType, modes::TwoTrackResonance::kPhi)) { + return (this->passesOptionalSelection(kPosDauTpcKaon) || this->passesOptionalSelection(kPosDauTofKaon) || this->passesOptionalSelection(kPosDauTpctofKaon)) && + (this->passesOptionalSelection(kNegDauTpcKaon) || this->passesOptionalSelection(kNegDauTofKaon) || this->passesOptionalSelection(kNegDauTpctofKaon)); + } + if constexpr (modes::isEqual(resoType, modes::TwoTrackResonance::kKstar0)) { + return (this->passesOptionalSelection(kPosDauTpcKaon) || this->passesOptionalSelection(kPosDauTofKaon) || this->passesOptionalSelection(kPosDauTpctofKaon)) && + (this->passesOptionalSelection(kNegDauTpcPion) || this->passesOptionalSelection(kNegDauTofPion) || this->passesOptionalSelection(kNegDauTpctofPion)); + } + if constexpr (modes::isEqual(resoType, modes::TwoTrackResonance::kKstar0Bar)) { + return (this->passesOptionalSelection(kPosDauTpcPion) || this->passesOptionalSelection(kPosDauTofPion) || this->passesOptionalSelection(kPosDauTpctofPion)) && + (this->passesOptionalSelection(kNegDauTpcKaon) || this->passesOptionalSelection(kNegDauTofKaon) || this->passesOptionalSelection(kNegDauTpctofKaon)); + } + return false; + } + + protected: + // (cached) kinematic variables of the resonance + float mPt = 0.f; + float mEta = 0.f; + float mPhi = 0.f; + float mMass = 0.f; + + // kinematic selections of the resonance + float mMassMin = 0.f; + float mMassMax = 6.f; + float mPtMin = 0.f; + float mPtMax = 6.f; + float mEtaMin = -0.9f; + float mEtaMax = 0.9f; + float mPhiMin = 0.f; + float mPhiMax = o2::constants::math::TwoPI; + + // daughter masses + float mPosDaughterMass = 0.f; + float mNegDaughterMass = 0.f; +}; + +struct TwoTrackResonanceBuilderProducts : o2::framework::ProducesGroup { + o2::framework::Produces producedPhis; + o2::framework::Produces producedPhiMasks; + o2::framework::Produces producedKstars; + o2::framework::Produces producedKstarMasks; + o2::framework::Produces producedRhos; + o2::framework::Produces producedRhoMasks; +}; + +struct ConfTwoTrackResonanceTables : o2::framework::ConfigurableGroup { + std::string prefix = std::string("TwoTrackResonanceTables"); + o2::framework::Configurable producePhis{"producePhis", -1, "Produce Phis (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable producePhiMasks{"producePhiMasks", -1, "Produce PhiMasks (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceKstar0s{"produceKstar0s", -1, "Produce K0stars (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceKstar0Masks{"produceKstar0Masks", -1, "Produce Kstar0Masks (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceRho0s{"produceRho0s", -1, "Produce Rho0s (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceRho0Masks{"produceRho0Masks", -1, "Produce Rho0Masks (-1: auto; 0 off; 1 on)"}; +}; + +template +class TwoTrackResonanceBuilder +{ + public: + TwoTrackResonanceBuilder() {} + virtual ~TwoTrackResonanceBuilder() = default; + + template + void init(T1& config, T2& filter, T3& daughterFilter, T4& table, T5 initContext) + { + twoTrackResonanceSelection.configure(config, filter, daughterFilter); + if constexpr (modes::isEqual(resoType, modes::TwoTrackResonance::kPhi)) { + LOG(info) << "Initialize femto Phi builder..."; + producePhis = utils::enableTable("FPhis_001", table.producePhis.value, initContext); + producePhiMasks = utils::enableTable("FPhiMasks_001", table.producePhiMasks.value, initContext); + } + if constexpr (modes::isEqual(resoType, modes::TwoTrackResonance::kKstar0) || modes::isEqual(resoType, modes::TwoTrackResonance::kKstar0Bar)) { + if constexpr (modes::isEqual(resoType, modes::TwoTrackResonance::kKstar0)) { + LOG(info) << "Initialize femto Kstar0 builder..."; + } + if constexpr (modes::isEqual(resoType, modes::TwoTrackResonance::kKstar0Bar)) { + LOG(info) << "Initialize femto Kstar0Bar builder..."; + } + produceKstar0s = utils::enableTable("FKstar0s_001", table.produceKstar0s.value, initContext); + produceKstar0Masks = utils::enableTable("FKstar0Masks_001", table.produceKstar0Masks.value, initContext); + } + if constexpr (modes::isEqual(resoType, modes::TwoTrackResonance::kRho0)) { + LOG(info) << "Initialize femto Rho0 builder..."; + produceRho0s = utils::enableTable("FRho0s_001", table.produceRho0s.value, initContext); + produceRho0Masks = utils::enableTable("FRho0Masks_001", table.produceRho0Masks.value, initContext); + } + + if (producePhis || producePhiMasks || produceKstar0s || produceKstar0Masks || produceRho0s || produceRho0Masks) { + fillAnyTable = true; + twoTrackResonanceSelection.printSelections(twoTrackResonanceSelsName, twoTrackResonanceSelsToString); + } else { + LOG(info) << "No tables configured"; + } + LOG(info) << "Initialization done..."; + } + + template + void fillResonances(T1& collisionProducts, T2& trackProducts, T3& resonanceProducts, T4& groupPositiveTracks, T5& groupNegativeTracks, T6& trackBuilder, T7& indexMap) + { + if (!fillAnyTable) { + return; + } + for (auto const& positiveTrack : groupPositiveTracks) { + for (auto const& negativeTrack : groupNegativeTracks) { + this->fillResonance(collisionProducts, trackProducts, resonanceProducts, positiveTrack, negativeTrack, trackBuilder, indexMap); + } + } + } + + template + void fillResonance(T1& collisionProducts, T2& trackProducts, T3& resonanceProducts, T4 const& posDaughter, T4 const& negDaughter, T5& trackBuilder, T6& indexMap) + { + + twoTrackResonanceSelection.applySelections(posDaughter, negDaughter); // for resonances selection are only applied to daughter tracks + if (!twoTrackResonanceSelection.hasTofAboveThreshold(posDaughter, negDaughter) || !twoTrackResonanceSelection.passesAllRequiredSelections()) { + return; + } + twoTrackResonanceSelection.reconstructResonance(posDaughter, negDaughter); + if (!twoTrackResonanceSelection.checkFilters() || !twoTrackResonanceSelection.checkHypothesis()) { + return; + } + int64_t posDaughterIndex = 0; + int64_t negDaughterIndex = 0; + posDaughterIndex = trackBuilder.template getDaughterIndex(posDaughter, trackProducts, collisionProducts, indexMap); + negDaughterIndex = trackBuilder.template getDaughterIndex(negDaughter, trackProducts, collisionProducts, indexMap); + + if constexpr (modes::isEqual(resoType, modes::TwoTrackResonance::kRho0)) { + if (produceRho0s) { + resonanceProducts.producedRhos( + collisionProducts.producedCollision.lastIndex(), + twoTrackResonanceSelection.getPt(), + twoTrackResonanceSelection.getEta(), + twoTrackResonanceSelection.getPhi(), + twoTrackResonanceSelection.getMass(), + posDaughterIndex, + negDaughterIndex); + } + if (produceRho0Masks) { + resonanceProducts.producedRhoMasks(twoTrackResonanceSelection.getBitmask()); + } + } + if constexpr (modes::isEqual(resoType, modes::TwoTrackResonance::kPhi)) { + if (producePhis) { + resonanceProducts.producedPhis( + collisionProducts.producedCollision.lastIndex(), + twoTrackResonanceSelection.getPt(), + twoTrackResonanceSelection.getEta(), + twoTrackResonanceSelection.getPhi(), + twoTrackResonanceSelection.getMass(), + posDaughterIndex, + negDaughterIndex); + } + if (producePhiMasks) { + resonanceProducts.producedPhiMasks(twoTrackResonanceSelection.getBitmask()); + } + } + if constexpr (modes::isEqual(resoType, modes::TwoTrackResonance::kKstar0)) { + if (produceKstar0s) { + resonanceProducts.producedKstars( + collisionProducts.producedCollision.lastIndex(), + twoTrackResonanceSelection.getPt(), + twoTrackResonanceSelection.getEta(), + twoTrackResonanceSelection.getPhi(), + twoTrackResonanceSelection.getMass(), + posDaughterIndex, + negDaughterIndex); + } + if (produceKstar0Masks) { + resonanceProducts.producedKstarMasks(twoTrackResonanceSelection.getBitmask()); + } + } + if constexpr (modes::isEqual(resoType, modes::TwoTrackResonance::kKstar0Bar)) { + if (produceKstar0s) { + resonanceProducts.producedKstars( + collisionProducts.producedCollision.lastIndex(), + -1.f * twoTrackResonanceSelection.getPt(), + twoTrackResonanceSelection.getEta(), + twoTrackResonanceSelection.getPhi(), + twoTrackResonanceSelection.getMass(), + posDaughterIndex, + negDaughterIndex); + } + if (produceKstar0Masks) { + resonanceProducts.producedKstarMasks(twoTrackResonanceSelection.getBitmask()); + } + } + } + + private: + TwoTrackResonanceSelection twoTrackResonanceSelection; + bool fillAnyTable = false; + bool producePhis = false; + bool producePhiMasks = false; + bool produceKstar0s = false; + bool produceKstar0Masks = false; + bool produceRho0s = false; + bool produceRho0Masks = false; +}; // namespace twotrackresonancebuilder + +} // namespace twotrackresonancebuilder +} // namespace o2::analysis::femto +#endif // PWGCF_FEMTO_CORE_TWOTRACKRESONANCEBUILDER_H_ diff --git a/PWGCF/Femto/Core/twoTrackResonanceHistManager.h b/PWGCF/Femto/Core/twoTrackResonanceHistManager.h new file mode 100644 index 00000000000..b6e429b434a --- /dev/null +++ b/PWGCF/Femto/Core/twoTrackResonanceHistManager.h @@ -0,0 +1,200 @@ +// Copyright 2019-2022 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 twoTrackResonanceHistManager.h +/// \brief histogram manager for two track resonances +/// \author Anton Riedel, TU München, anton.riedel@cern.ch + +#ifndef PWGCF_FEMTO_CORE_TWOTRACKRESONANCEHISTMANAGER_H_ +#define PWGCF_FEMTO_CORE_TWOTRACKRESONANCEHISTMANAGER_H_ + +#include "PWGCF/Femto/Core/histManager.h" +#include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/Core/trackHistManager.h" + +#include "CommonConstants/MathConstants.h" +#include "Framework/Configurable.h" +#include "Framework/HistogramRegistry.h" +#include "Framework/HistogramSpec.h" + +#include +#include +#include +#include +#include + +namespace o2::analysis::femto +{ +namespace twotrackresonancehistmanager +{ +// enum for track histograms +enum TwoTrackResonanceHist { + // analysis + kPt, + kEta, + kPhi, + kMass, + kSign, + // 2d qa + kPtVsEta, + kPtVsPhi, + kPhiVsEta, + kPtVsMass, + kTwoTrackResonanceHistLast +}; + +#define TWOTRACKRESONANCE_DEFAULT_BINNING(defaultMassMin, defaultMassMax) \ + o2::framework::ConfigurableAxis pt{"pt", {{600, 0, 6}}, "Pt"}; \ + o2::framework::ConfigurableAxis eta{"eta", {{300, -1.5, 1.5}}, "Eta"}; \ + o2::framework::ConfigurableAxis phi{"phi", {{720, 0, 1.f * o2::constants::math::TwoPI}}, "Phi"}; \ + o2::framework::ConfigurableAxis mass{"mass", {{200, defaultMassMin, defaultMassMax}}, "Mass"}; \ + o2::framework::ConfigurableAxis sign{"sign", {{3, -1.5, 1.5}}, "Sign"}; + +struct ConfPhiBinning : o2::framework::ConfigurableGroup { + std::string prefix = std::string("PhiBinning"); + TWOTRACKRESONANCE_DEFAULT_BINNING(0.8f, 1.2f) +}; + +struct ConfRho0Binning : o2::framework::ConfigurableGroup { + std::string prefix = std::string("Rho0Binning"); + TWOTRACKRESONANCE_DEFAULT_BINNING(0.5f, 1.f) +}; + +struct ConfKstar0Binning : o2::framework::ConfigurableGroup { + std::string prefix = std::string("Kstar0Binning"); + TWOTRACKRESONANCE_DEFAULT_BINNING(0.6f, 1.f) +}; +#undef TWOTRACKRESONANCE_DEFAULT_BINNING + +constexpr std::array, kTwoTrackResonanceHistLast> HistTable = { + {{kPt, o2::framework::kTH1F, "hPt", "Transverse Momentum; p_{T} (GeV/#it{c}); Entries"}, + {kEta, o2::framework::kTH1F, "hEta", "Pseudorapdity; #eta; Entries"}, + {kPhi, o2::framework::kTH1F, "hPhi", "Azimuthal angle; #varphi; Entries"}, + {kMass, o2::framework::kTH1F, "hMass", "Invariant mass; m (GeV/#it{c}^{2}); Entries"}, + {kSign, o2::framework::kTH1F, "hSign", "Sign (-1 -> antiparticle, 0 -> self conjugate, +1 -> particle); sign; Entries"}, + {kPtVsEta, o2::framework::kTH2F, "hPtVsEta", "p_{T} vs #eta; p_{T} (GeV/#it{c}) ; #eta"}, + {kPtVsPhi, o2::framework::kTH2F, "hPtVsPhi", "p_{T} vs #varphi;p_{T} (GeV/#it{c});#varphi"}, + {kPhiVsEta, o2::framework::kTH2F, "hPhiVsEta", "#varphi vs #eta; #varphi ; #eta"}, + {kPtVsMass, o2::framework::kTH2F, "hPtVsMass", "p_{T} vs invariant mass; p_{T} (GeV/#it{c}); m (GeV/#it{c}^{2})"}}}; + +template +std::map> makeTwoTrackResonanceHistSpecMap(const T& confBinningAnalysis) +{ + return std::map>{ + {kPt, {confBinningAnalysis.pt}}, + {kEta, {confBinningAnalysis.eta}}, + {kPhi, {confBinningAnalysis.phi}}, + {kMass, {confBinningAnalysis.mass}}, + {kSign, {confBinningAnalysis.sing}}}; +}; + +template +auto makeTwoTrackResonanceQaHistSpecMap(const T& confBinningAnalysis) +{ + return std::map>{ + {kPt, {confBinningAnalysis.pt}}, + {kEta, {confBinningAnalysis.eta}}, + {kPhi, {confBinningAnalysis.phi}}, + {kMass, {confBinningAnalysis.mass}}, + {kSign, {confBinningAnalysis.sign}}, + {kPtVsEta, {confBinningAnalysis.pt, confBinningAnalysis.eta}}, + {kPtVsPhi, {confBinningAnalysis.pt, confBinningAnalysis.phi}}, + {kPhiVsEta, {confBinningAnalysis.phi, confBinningAnalysis.eta}}, + {kPtVsMass, {confBinningAnalysis.pt, confBinningAnalysis.mass}}}; +}; + +constexpr char PrefixRho[] = "Rho0/"; +constexpr char PrefixPhi[] = "Phi/"; +constexpr char PrefixKstar[] = "Kstar0/"; + +constexpr std::string_view AnalysisDir = "Kinematics/"; +constexpr std::string_view QaDir = "QA/"; + +/// \class FemtoDreamEventHisto +/// \brief Class for histogramming event properties +// template +template +class TwoTrackResonanceHistManager +{ + public: + /// Destructor + virtual ~TwoTrackResonanceHistManager() = default; + /// Initializes histograms for the task + /// \param registry Histogram registry to be passed + /// + void init(o2::framework::HistogramRegistry* registry, + std::map> ResoSpecs, + std::map> PosDauSpecs, + std::map> NegDauSpecs) + { + mHistogramRegistry = registry; + mPosDauManager.init(registry, PosDauSpecs); + mNegDauManager.init(registry, NegDauSpecs); + + if constexpr (modes::isFlagSet(mode, modes::Mode::kAnalysis)) { + std::string analysisDir = std::string(resoPrefix) + std::string(AnalysisDir); + + mHistogramRegistry->add(analysisDir + GetHistNamev2(kPt, HistTable), GetHistDesc(kPt, HistTable), GetHistType(kPt, HistTable), {ResoSpecs[kPt]}); + mHistogramRegistry->add(analysisDir + GetHistNamev2(kEta, HistTable), GetHistDesc(kEta, HistTable), GetHistType(kEta, HistTable), {ResoSpecs[kEta]}); + mHistogramRegistry->add(analysisDir + GetHistNamev2(kPhi, HistTable), GetHistDesc(kPhi, HistTable), GetHistType(kPhi, HistTable), {ResoSpecs[kPhi]}); + mHistogramRegistry->add(analysisDir + GetHistNamev2(kMass, HistTable), GetHistDesc(kMass, HistTable), GetHistType(kMass, HistTable), {ResoSpecs[kMass]}); + mHistogramRegistry->add(analysisDir + GetHistNamev2(kSign, HistTable), GetHistDesc(kSign, HistTable), GetHistType(kSign, HistTable), {ResoSpecs[kSign]}); + } + + if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { + std::string qaDir = std::string(resoPrefix) + std::string(QaDir); + + mHistogramRegistry->add(qaDir + GetHistNamev2(kPtVsEta, HistTable), GetHistDesc(kPtVsEta, HistTable), GetHistType(kPtVsEta, HistTable), {ResoSpecs[kPtVsEta]}); + mHistogramRegistry->add(qaDir + GetHistNamev2(kPtVsPhi, HistTable), GetHistDesc(kPtVsPhi, HistTable), GetHistType(kPtVsPhi, HistTable), {ResoSpecs[kPtVsPhi]}); + mHistogramRegistry->add(qaDir + GetHistNamev2(kPhiVsEta, HistTable), GetHistDesc(kPhiVsEta, HistTable), GetHistType(kPhiVsEta, HistTable), {ResoSpecs[kPhiVsEta]}); + mHistogramRegistry->add(qaDir + GetHistNamev2(kPtVsMass, HistTable), GetHistDesc(kPtVsMass, HistTable), GetHistType(kPtVsMass, HistTable), {ResoSpecs[kPtVsMass]}); + } + } + + template + void fill(T1 const& resonance, T2 const& /*tracks*/) + { + auto posDaughter = resonance.template posDau_as(); + mPosDauManager.fill(posDaughter); + auto negDaughter = resonance.template negDau_as(); + mNegDauManager.fill(negDaughter); + + if constexpr (modes::isFlagSet(mode, modes::Mode::kAnalysis)) { + mHistogramRegistry->fill(HIST(resoPrefix) + HIST(AnalysisDir) + HIST(GetHistName(kPt, HistTable)), resonance.pt()); + mHistogramRegistry->fill(HIST(resoPrefix) + HIST(AnalysisDir) + HIST(GetHistName(kEta, HistTable)), resonance.eta()); + mHistogramRegistry->fill(HIST(resoPrefix) + HIST(AnalysisDir) + HIST(GetHistName(kPhi, HistTable)), resonance.phi()); + mHistogramRegistry->fill(HIST(resoPrefix) + HIST(AnalysisDir) + HIST(GetHistName(kMass, HistTable)), resonance.mass()); + + if constexpr (modes::isEqual(reso, modes::TwoTrackResonance::kPhi) || modes::isEqual(reso, modes::TwoTrackResonance::kRho0)) { + mHistogramRegistry->fill(HIST(resoPrefix) + HIST(AnalysisDir) + HIST(GetHistName(kSign, HistTable)), 0); + } + if constexpr (modes::isEqual(reso, modes::TwoTrackResonance::kKstar0) || modes::isEqual(reso, modes::TwoTrackResonance::kKstar0Bar)) { + mHistogramRegistry->fill(HIST(resoPrefix) + HIST(AnalysisDir) + HIST(GetHistName(kSign, HistTable)), resonance.sign()); + } + } + + if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { + mHistogramRegistry->fill(HIST(resoPrefix) + HIST(QaDir) + HIST(GetHistName(kPtVsEta, HistTable)), resonance.pt(), resonance.eta()); + mHistogramRegistry->fill(HIST(resoPrefix) + HIST(QaDir) + HIST(GetHistName(kPtVsPhi, HistTable)), resonance.pt(), resonance.phi()); + mHistogramRegistry->fill(HIST(resoPrefix) + HIST(QaDir) + HIST(GetHistName(kPhiVsEta, HistTable)), resonance.phi(), resonance.eta()); + mHistogramRegistry->fill(HIST(resoPrefix) + HIST(QaDir) + HIST(GetHistName(kPtVsMass, HistTable)), resonance.pt(), resonance.mass()); + } + } + + private: + o2::framework::HistogramRegistry* mHistogramRegistry = nullptr; + + trackhistmanager::TrackHistManager mPosDauManager; + trackhistmanager::TrackHistManager mNegDauManager; +}; +}; // namespace twotrackresonancehistmanager +}; // namespace o2::analysis::femto +#endif // PWGCF_FEMTO_CORE_TWOTRACKRESONANCEHISTMANAGER_H_ diff --git a/PWGCF/Femto/Core/v0Builder.h b/PWGCF/Femto/Core/v0Builder.h new file mode 100644 index 00000000000..300a996c05e --- /dev/null +++ b/PWGCF/Femto/Core/v0Builder.h @@ -0,0 +1,471 @@ +// 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 v0Builder.h +/// \brief v0 builder +/// \author Anton Riedel, TU München, anton.riedel@cern.ch + +#ifndef PWGCF_FEMTO_CORE_V0BUILDER_H_ +#define PWGCF_FEMTO_CORE_V0BUILDER_H_ + +#include "PWGCF/Femto/Core/baseSelection.h" +#include "PWGCF/Femto/Core/dataTypes.h" +#include "PWGCF/Femto/Core/femtoUtils.h" +#include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/Core/selectionContainer.h" +#include "PWGCF/Femto/DataModel/FemtoTables.h" + +#include "CommonConstants/MathConstants.h" +#include "Framework/AnalysisHelpers.h" +#include "Framework/Configurable.h" + +#include "fairlogger/Logger.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace o2::analysis::femto +{ +namespace v0builder +{ + +// filters applied in the producer task +struct ConfV0Filters : o2::framework::ConfigurableGroup { + std::string prefix = std::string("V0Filters"); + o2::framework::Configurable ptMin{"ptMin", 0.f, "Minimum pT"}; + o2::framework::Configurable ptMax{"ptMax", 99.f, "Maximum pT"}; + o2::framework::Configurable etaMin{"etaMin", -10.f, "Minimum eta"}; + o2::framework::Configurable etaMax{"etaMax", 10.f, "Maximum eta"}; + o2::framework::Configurable phiMin{"phiMin", 0.f, "Minimum phi"}; + o2::framework::Configurable phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"}; + o2::framework::Configurable massMinLambda{"massMinLambda", 1.f, "Minimum mass for Lambda hypothesis"}; + o2::framework::Configurable massMaxLambda{"massMaxLambda", 1.2f, "Maximum mass for Lambda hypothesis"}; + o2::framework::Configurable massMinK0short{"massMinK0short", 0.45f, "Minimum mass for K0Short hypothesis"}; + o2::framework::Configurable massMaxK0short{"massMaxK0short", 0.53f, "Maximum mass for K0Short hypothesis"}; + o2::framework::Configurable rejectMassMinLambda{"rejectMassMinLambda", 1.11f, "Minimum mass to rejection K0short hypothesis for Lambda candidates"}; + o2::framework::Configurable rejectMassMaxLambda{"rejectMassMaxLambda", 1.12f, "Maximum mass to rejection K0short hypothesis for Lambda candidates"}; + o2::framework::Configurable rejectMassMinK0short{"rejectMassMinK0short", 0.48f, "Minimum mass to rejection K0short hypothesis for Lambda candidates"}; + o2::framework::Configurable rejectMassMaxK0short{"rejectMassMaxK0short", 0.5f, "Maximum mass to rejection K0short hypothesis for Lambda candidates"}; +}; + +// selections bits for all v0s +#define V0_DEFAULT_BITS \ + o2::framework::Configurable> dcaDauMax{"dcaDauMax", {1.5f}, "Maximum DCA between the daughters at decay vertex (cm)"}; \ + o2::framework::Configurable> cpaMin{"cpaMin", {0.99f}, "Minimum cosine of pointing angle"}; \ + o2::framework::Configurable> transRadMin{"transRadMin", {0.2f}, "Minimum transverse radius (cm)"}; \ + o2::framework::Configurable> transRadMax{"transRadMax", {100.f}, "Maximum transverse radius (cm)"}; \ + o2::framework::Configurable> decayVtxMax{"decayVtxMax", {100.f}, "Maximum distance in x,y,z of the decay vertex from primary vertex (cm)"}; \ + o2::framework::Configurable> dauAbsEtaMax{"dauAbsEtaMax", {0.8f}, "Maximum |eta| for daughter tracks"}; \ + o2::framework::Configurable> dauDcaMin{"dauDcaMin", {0.05f}, "Minimum DCA of the daughters from primary vertex (cm)"}; \ + o2::framework::Configurable> dauTpcClustersMin{"dauTpcClustersMin", {80.f}, "Minimum number of TPC clusters for daughter tracks"}; + +// derived selection bits for lambda +struct ConfLambdaBits : o2::framework::ConfigurableGroup { + std::string prefix = std::string("LambdaBits"); + V0_DEFAULT_BITS + o2::framework::Configurable> posDauTpcPion{"posDauTpcPion", {5.f}, "Maximum |nsimga_Pion| TPC for positive daughter tracks"}; + o2::framework::Configurable> posDauTpcProton{"posDauTpcProton", {5.f}, "Maximum |nsimga_Proton| TPC for positive daughter tracks"}; + o2::framework::Configurable> negDauTpcPion{"negDauTpcPion", {5.f}, "Maximum |nsimga_Pion| TPC for negative daughter tracks"}; + o2::framework::Configurable> negDauTpcProton{"negDauTpcProton", {5.f}, "Maximum |nsimga_Proton| TPC negative for daughter tracks"}; +}; + +// derived selection bits for K0Short +struct ConfK0shortBits : o2::framework::ConfigurableGroup { + std::string prefix = std::string("K0shortBits"); + V0_DEFAULT_BITS + o2::framework::Configurable> posDauTpcPion{"posDauTpcPion", {5.f}, "Maximum |nsimga_Pion| TPC for positive daughter tracks"}; + o2::framework::Configurable> negDauTpcPion{"negDauTpcPion", {5.f}, "Maximum |nsimga_Pion| TPC for negative daughter tracks"}; +}; + +#undef V0_DEFAULT_BITS + +// base selection for analysis task for v0s +#define V0_DEFAULT_SELECTIONS(defaultMassMin, defaultMassMax, defaultPdgCode) \ + o2::framework::Configurable pdgCode{"pdgCode", defaultPdgCode, "V0 PDG code"}; \ + o2::framework::Configurable ptMin{"ptMin", 0.f, "Minimum pT"}; \ + o2::framework::Configurable ptMax{"ptMax", 999.f, "Maximum pT"}; \ + o2::framework::Configurable etaMin{"etaMin", -10.f, "Minimum eta"}; \ + o2::framework::Configurable etaMax{"etaMax", 10.f, "Maximum eta"}; \ + o2::framework::Configurable phiMin{"phiMin", 0.f, "Minimum eta"}; \ + o2::framework::Configurable phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"}; \ + o2::framework::Configurable massMin{"massMin", defaultMassMin, "Minimum invariant mass for Lambda"}; \ + o2::framework::Configurable massMax{"massMax", defaultMassMax, "Maximum invariant mass for Lambda"}; \ + o2::framework::Configurable mask{"mask", 0, "Bitmask for v0 selection"}; + +// base selection for analysis task for lambdas +template +struct ConfLambdaSelection : o2::framework::ConfigurableGroup { + std::string prefix = Prefix; + V0_DEFAULT_SELECTIONS(1.0, 1.2, 3122) + o2::framework::Configurable sign{"sign", 1, "Sign of the Lambda (+1 for Lambda and -1 for Antilambda"}; +}; + +// base selection for analysis task for k0short +template +struct ConfK0shortSelection : o2::framework::ConfigurableGroup { + std::string prefix = Prefix; + V0_DEFAULT_SELECTIONS(0.47, 0.51, 310) +}; + +#undef V0_DEFAULT_SELECTIONS + +constexpr const char PrefixLambdaSelection1[] = "LambdaSelection1"; +using ConfLambdaSelection1 = ConfLambdaSelection; +constexpr const char PrefixK0shortSelection1[] = "K0shortSelection1"; +using ConfK0shortSelection1 = ConfK0shortSelection; + +/// The different selections for v0s +enum V0Seles { + // selections for lambdas + kCpaMin, ///< Min. CPA (cosine pointing angle) + kDcaDaughMax, ///< Max. DCA of the daughters at decay vertex + kDecayVtxMax, ///< Max. distance of decay vertex in x,y,z + kTransRadMin, ///< Min. transverse radius + kTransRadMax, ///< max. transverse radius + + // selection for daughter + kDauAbsEtaMax, ///< Max. absolute pseudo rapidity + kDauDcaMin, ///< Min. DCA of the positive daughters at primary vertex + kDauTpcClsMin, ///< Min. number of TPC clusters of positive daughter + + // pid selection for daughters + kPosDaughTpcPion, ///< TPC Pion PID for positive daughter + kPosDaughTpcProton, ///< TPC Proton PID for positive daughter + kNegDaughTpcPion, ///< TPC Pion PID for negative daughter + kNegDaughTpcProton, ///< TPC Proton PID for negative daughter + + kV0SelsMax +}; + +const char v0SelsName[] = "K0short selection object"; +const std::unordered_map v0SelsToStrings = { + {kCpaMin, "Min. CPA (cosine pointing angle)"}, + {kDcaDaughMax, "Max. DCA of the daughters at decay vertex"}, + {kDecayVtxMax, "Max. distance of decay vertex in x,y,z"}, + {kTransRadMin, "Min. transverse radius"}, + {kTransRadMax, "Max. transverse radius"}, + + {kDauAbsEtaMax, "Max. absolute pseudo rapidity"}, + {kDauDcaMin, "Min. DCA of the positive daughters at primary vertex"}, + {kDauTpcClsMin, "Min. number of TPC clusters of positive daughter"}, + + {kPosDaughTpcPion, "TPC Pion PID for positive daughter"}, + {kPosDaughTpcProton, "TPC Proton PID for positive daughter"}, + {kNegDaughTpcPion, "TPC Pion PID for negative daughter"}, + {kNegDaughTpcProton, "TPC Proton PID for negative daughter"}}; + +/// \class FemtoDreamTrackCuts +/// \brief Cut class to contain and execute all cuts applied to tracks +template +class V0Selection : public BaseSelection +{ + public: + V0Selection() {} + virtual ~V0Selection() = default; + + template + void configure(T1& config, T2& filter) + { + mPtMin = filter.ptMin.value; + mPtMax = filter.ptMax.value; + mEtaMin = filter.etaMin.value; + mEtaMax = filter.etaMax.value; + mPhiMin = filter.phiMin.value; + mPhiMax = filter.phiMax.value; + + if constexpr (modes::isEqual(v0Type, modes::V0::kLambda) || modes::isEqual(v0Type, modes::V0::kAntiLambda)) { + mMassLambdaLowerLimit = filter.massMinLambda.value; + mMassLambdaUpperLimit = filter.massMaxLambda.value; + mMassK0shortLowerLimit = filter.rejectMassMinK0short.value; + mMassK0shortUpperLimit = filter.rejectMassMaxK0short.value; + + if constexpr (modes::isEqual(v0Type, modes::V0::kLambda)) { + this->addSelection(config.posDauTpcProton.value, kPosDaughTpcProton, limits::kAbsUpperLimit, true, true); + this->addSelection(config.negDauTpcPion.value, kNegDaughTpcPion, limits::kAbsUpperLimit, true, true); + } + + if constexpr (modes::isEqual(v0Type, modes::V0::kAntiLambda)) { + this->addSelection(config.posDauTpcPion.value, kPosDaughTpcPion, limits::kAbsUpperLimit, true, true); + this->addSelection(config.negDauTpcProton.value, kNegDaughTpcProton, limits::kAbsUpperLimit, true, true); + } + } + if constexpr (modes::isEqual(v0Type, modes::V0::kK0short)) { + mMassK0shortLowerLimit = filter.massMinK0short.value; + mMassK0shortUpperLimit = filter.massMaxK0short.value; + mMassLambdaLowerLimit = filter.rejectMassMinLambda.value; + mMassLambdaUpperLimit = filter.rejectMassMaxLambda.value; + this->addSelection(config.posDauTpcPion.value, kPosDaughTpcPion, limits::kAbsUpperLimit, true, true); + this->addSelection(config.negDauTpcPion.value, kNegDaughTpcPion, limits::kAbsUpperLimit, true, true); + } + + this->addSelection(config.dcaDauMax.value, kDcaDaughMax, limits::kAbsUpperLimit, true, true); + this->addSelection(config.cpaMin.value, kCpaMin, limits::kLowerLimit, true, true); + this->addSelection(config.transRadMin.value, kTransRadMin, limits::kLowerLimit, true, true); + this->addSelection(config.transRadMax.value, kTransRadMax, limits::kUpperLimit, true, true); + this->addSelection(config.dauAbsEtaMax.value, kDauAbsEtaMax, limits::kAbsUpperLimit, true, true); + this->addSelection(config.dauDcaMin.value, kDauDcaMin, limits::kAbsLowerFunctionLimit, true, true); + this->addSelection(config.dauTpcClustersMin.value, kDauTpcClsMin, limits::kLowerLimit, true, true); + } + + template + void applySelections(T1 const& v0candidate, T2 const& /*tracks*/) + { + this->reset(); + // v0 selections + this->evaluateObservable(kCpaMin, v0candidate.v0cosPA()); + this->evaluateObservable(kDcaDaughMax, v0candidate.dcaV0daughters()); + // for decay vertex, the x,y and z coordinate have to be below a certain threshold + // compare the largest of the 3 to the limit set by the bit + std::array decayCoordinates = {std::fabs(v0candidate.x()), std::fabs(v0candidate.y()), std::fabs(v0candidate.z())}; + this->evaluateObservable(kDecayVtxMax, *std::max_element(decayCoordinates.begin(), decayCoordinates.end())); + this->evaluateObservable(kTransRadMin, v0candidate.v0radius()); + this->evaluateObservable(kTransRadMax, v0candidate.v0radius()); + + // daughter selection + // for daughter selections, both have to fit the same track quality selection, so we store only one bit for both + // take largest/smallest from both daughters and evaluate the observable with this value + auto posDaughter = v0candidate.template posTrack_as(); + auto negDaughter = v0candidate.template negTrack_as(); + + std::array etaDaughters = {std::fabs(posDaughter.eta()), std::fabs(negDaughter.eta())}; + this->evaluateObservable(kDauAbsEtaMax, *std::max_element(etaDaughters.begin(), etaDaughters.end())); + + std::array dcaDaughters = {std::hypot(posDaughter.dcaXY(), posDaughter.dcaZ()), std::hypot(negDaughter.dcaXY(), negDaughter.dcaZ())}; + this->evaluateObservable(kDauDcaMin, *std::min_element(dcaDaughters.begin(), dcaDaughters.end())); + + std::array clustersDaughters = {1.f * posDaughter.tpcNClsFound(), 1.f * negDaughter.tpcNClsFound()}; + this->evaluateObservable(kDauTpcClsMin, *std::min_element(clustersDaughters.begin(), clustersDaughters.end())); + + // daughter pid selections + this->evaluateObservable(kPosDaughTpcPion, posDaughter.tpcNSigmaPi()); + this->evaluateObservable(kPosDaughTpcProton, posDaughter.tpcNSigmaPr()); + this->evaluateObservable(kNegDaughTpcPion, negDaughter.tpcNSigmaPi()); + this->evaluateObservable(kNegDaughTpcProton, negDaughter.tpcNSigmaPr()); + + this->assembleBitmask(); + } + + template + bool checkFilters(const T& v0) const + { + return ((v0.pt() > mPtMin && v0.pt() < mPtMax) && + (v0.eta() > mEtaMin && v0.eta() < mEtaMax) && + (v0.phi() > mPhiMin && v0.phi() < mPhiMax)); + } + + template + bool checkHypothesis(T const& v0candidate) const + { + // no need to check PID of the daughters here, they are set as minimal cuts + if constexpr (modes::isEqual(v0Type, modes::V0::kLambda)) { + return (v0candidate.mLambda() > mMassLambdaLowerLimit && v0candidate.mLambda() < mMassLambdaUpperLimit) && // inside Lambda window + (v0candidate.mK0Short() < mMassK0shortLowerLimit || v0candidate.mK0Short() > mMassK0shortUpperLimit); // outside K0short window + } + if constexpr (modes::isEqual(v0Type, modes::V0::kAntiLambda)) { + return // check PID for daughters + (v0candidate.mAntiLambda() > mMassLambdaLowerLimit && v0candidate.mAntiLambda() < mMassLambdaUpperLimit) && // inside AntiLambda window + (v0candidate.mK0Short() < mMassK0shortLowerLimit || v0candidate.mK0Short() > mMassK0shortUpperLimit); // outside K0short window + } + if constexpr (modes::isEqual(v0Type, modes::V0::kK0short)) { + return (v0candidate.mK0Short() > mMassK0shortLowerLimit && v0candidate.mK0Short() < mMassK0shortUpperLimit) && // inside K0short window + (v0candidate.mLambda() < mMassLambdaLowerLimit || v0candidate.mLambda() > mMassLambdaUpperLimit) && // outside Lambda window + (v0candidate.mAntiLambda() < mMassLambdaLowerLimit || v0candidate.mAntiLambda() > mMassLambdaUpperLimit); // outside AntiLambda window + } + return false; + } + + protected: + float mMassK0shortLowerLimit = 0.483f; + float mMassK0shortUpperLimit = 0.503f; + + float mMassLambdaLowerLimit = 1.105f; + float mMassLambdaUpperLimit = 1.125f; + + // kinematic filters + float mPtMin = 0.f; + float mPtMax = 6.f; + float mEtaMin = -1.f; + float mEtaMax = 1.f; + float mPhiMin = 0.f; + float mPhiMax = o2::constants::math::TwoPI; +}; + +struct V0BuilderProducts : o2::framework::ProducesGroup { + o2::framework::Produces producedLambdas; + o2::framework::Produces producedLambdaMasks; + o2::framework::Produces producedLambdaExtras; + o2::framework::Produces producedK0shorts; + o2::framework::Produces producedK0shortMasks; + o2::framework::Produces producedK0shortExtras; +}; + +struct ConfV0Tables : o2::framework::ConfigurableGroup { + std::string prefix = std::string("V0Tables"); + o2::framework::Configurable produceLambdas{"produceLambdas", -1, "Produce Lambdas (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceLambdaMasks{"produceLambdaMasks", -1, "Produce LambdaMasks (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceLambdaExtras{"produceLambdaExtras", -1, "Produce LambdaExtras (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceK0shorts{"produceK0shorts", -1, "Produce K0shorts (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceK0shortMasks{"produceK0shortMasks", -1, "Produce K0shortMasks (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceK0shortExtras{"produceK0shortExtras", -1, "Produce K0shortExtras (-1: auto; 0 off; 1 on)"}; +}; + +template +class V0Builder +{ + public: + V0Builder() {} + virtual ~V0Builder() = default; + + template + void init(T1& config, T2& filter, T3& table, T4& initContext) + { + v0Selection.configure(config, filter); + if constexpr (modes::isEqual(v0Type, modes::V0::kLambda) || modes::isEqual(v0Type, modes::V0::kAntiLambda)) { + if constexpr (modes::isEqual(v0Type, modes::V0::kLambda)) { + LOG(info) << "Initialize femto Lambda builder..."; + } + if constexpr (modes::isEqual(v0Type, modes::V0::kAntiLambda)) { + LOG(info) << "Initialize femto AntiLambda builder..."; + } + produceLambdas = utils::enableTable("FLambdas_001", table.produceLambdas.value, initContext); + produceLambdaMasks = utils::enableTable("FLambdaMasks_001", table.produceLambdaMasks.value, initContext); + produceLambdaExtras = utils::enableTable("FLambdaExtras_001", table.produceLambdaExtras.value, initContext); + } + if constexpr (modes::isEqual(v0Type, modes::V0::kK0short)) { + LOG(info) << "Initialize femto K0short builder..."; + produceK0shorts = utils::enableTable("FK0shorts_001", table.produceK0shorts.value, initContext); + produceK0shortMasks = utils::enableTable("FK0shortMasks_001", table.produceK0shortMasks.value, initContext); + produceK0shortExtras = utils::enableTable("FK0shortExtras_001", table.produceK0shortExtras.value, initContext); + } + if (produceLambdas || produceLambdaMasks || produceLambdaExtras || produceK0shorts || produceK0shortMasks || produceK0shortExtras) { + mFillAnyTable = true; + v0Selection.printSelections(v0SelsName, v0SelsToStrings); + } else { + LOG(info) << "No tables configured"; + } + LOG(info) << "Initialization done..."; + } + + template + void fillV0s(T1& collisionProducts, T2& trackProducts, T3& v0products, T4 const& v0s, T5 const& tracks, T6& trackBuilder, T7& indexMap) + { + if (!mFillAnyTable) { + return; + } + int64_t posDaughterIndex = 0; + int64_t negDaughterIndex = 0; + for (const auto& v0 : v0s) { + if (!v0Selection.checkFilters(v0)) { + continue; + } + v0Selection.applySelections(v0, tracks); + if (v0Selection.passesAllRequiredSelections() && v0Selection.checkHypothesis(v0)) { + auto posDaughter = v0.template posTrack_as(); + auto negDaughter = v0.template negTrack_as(); + posDaughterIndex = trackBuilder.template getDaughterIndex(posDaughter, trackProducts, collisionProducts, indexMap); + negDaughterIndex = trackBuilder.template getDaughterIndex(negDaughter, trackProducts, collisionProducts, indexMap); + if constexpr (modes::isEqual(v0Type, modes::V0::kLambda)) { + fillLambda(collisionProducts, v0products, v0, 1.f, posDaughterIndex, negDaughterIndex); + } + if constexpr (modes::isEqual(v0Type, modes::V0::kAntiLambda)) { + fillLambda(collisionProducts, v0products, v0, -1.f, posDaughterIndex, negDaughterIndex); + } + if constexpr (modes::isEqual(v0Type, modes::V0::kK0short)) { + fillK0short(collisionProducts, v0products, v0, posDaughterIndex, negDaughterIndex); + } + } + } + } + + template + void fillLambda(T1& collisionProducts, T2& v0products, T3 const& v0, float sign, int posDaughterIndex, int negDaughterIndex) + { + float mass, massAnti; + if (sign > 0.f) { + mass = v0.mLambda(); + massAnti = v0.mAntiLambda(); + } else { + mass = v0.mAntiLambda(); + massAnti = v0.mLambda(); + } + if (produceLambdas) { + v0products.producedLambdas(collisionProducts.producedCollision.lastIndex(), + sign * v0.pt(), + v0.eta(), + v0.phi(), + mass, + posDaughterIndex, + negDaughterIndex); + } + if (produceLambdaMasks) { + v0products.producedLambdaMasks(v0Selection.getBitmask()); + } + if (produceLambdaExtras) { + v0products.producedLambdaExtras( + massAnti, + v0.mK0Short(), + v0.v0cosPA(), + v0.dcaV0daughters(), + v0.v0radius(), + v0.x(), + v0.y(), + v0.z()); + } + } + + template + void fillK0short(T1& collisionProducts, T2& v0products, T3 const& v0, int posDaughterIndex, int negDaughterIndex) + { + if (produceK0shorts) { + v0products.producedK0shorts(collisionProducts.producedCollision.lastIndex(), + v0.pt(), + v0.eta(), + v0.phi(), + v0.mK0Short(), + posDaughterIndex, + negDaughterIndex); + } + if (produceK0shortMasks) { + v0products.producedK0shortMasks(v0Selection.getBitmask()); + } + if (produceK0shortExtras) { + v0products.producedK0shortExtras( + v0.mLambda(), + v0.mAntiLambda(), + v0.v0cosPA(), + v0.dcaV0daughters(), + v0.v0radius(), + v0.x(), + v0.y(), + v0.z()); + } + } + + bool fillAnyTable() { return mFillAnyTable; } + + private: + V0Selection v0Selection; + bool mFillAnyTable = false; + bool produceLambdas = false; + bool produceLambdaMasks = false; + bool produceLambdaExtras = false; + bool produceK0shorts = false; + bool produceK0shortMasks = false; + bool produceK0shortExtras = false; +}; +} // namespace v0builder +} // namespace o2::analysis::femto +#endif // PWGCF_FEMTO_CORE_V0BUILDER_H_ diff --git a/PWGCF/Femto/Core/v0HistManager.h b/PWGCF/Femto/Core/v0HistManager.h new file mode 100644 index 00000000000..7dc732b84b1 --- /dev/null +++ b/PWGCF/Femto/Core/v0HistManager.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 v0HistManager.h +/// \brief histogram manager for vzero histograms +/// \author Anton Riedel, TU München, anton.riedel@cern.ch + +#ifndef PWGCF_FEMTO_CORE_V0HISTMANAGER_H_ +#define PWGCF_FEMTO_CORE_V0HISTMANAGER_H_ + +#include "PWGCF/Femto/Core/histManager.h" +#include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/Core/trackHistManager.h" + +#include "CommonConstants/MathConstants.h" +#include "Framework/Configurable.h" +#include "Framework/HistogramRegistry.h" +#include "Framework/HistogramSpec.h" + +#include +#include +#include +#include +#include + +namespace o2::analysis::femto +{ +namespace v0histmanager +{ +// enum for track histograms +enum V0Hist { + // analysis + kPt, + kEta, + kPhi, + kMass, + kSign, + // qa variables + kMassLambda, + kMassAntiLambda, + kMassK0short, + kCosPa, + kDecayDauDca, + kDecayVtxX, + kDecayVtxY, + kDecayVtxZ, + kDecayVtx, + kTransRadius, + // 2d qa + kPtVsEta, + kPtVsPhi, + kPhiVsEta, + kPtVsCosPa, + kPtVsLambdaMass, + kPtVsAntiLambdaMass, + kPtVsK0shortMass, + kLambdaMassVsAntiLambdaMass, + kK0shortMassVsLambdaMass, + kK0shortMassVsAntiLambdaMass, + kV0HistLast +}; + +#define V0_DEFAULT_BINNING(defaultMassMin, defaultMassMax) \ + o2::framework::ConfigurableAxis pt{"pt", {{600, 0, 6}}, "Pt"}; \ + o2::framework::ConfigurableAxis eta{"eta", {{300, -1.5, 1.5}}, "Eta"}; \ + o2::framework::ConfigurableAxis phi{"phi", {{720, 0, 1.f * o2::constants::math::TwoPI}}, "Phi"}; \ + o2::framework::ConfigurableAxis mass{"mass", {{200, defaultMassMin, defaultMassMax}}, "Mass"}; \ + o2::framework::ConfigurableAxis sign{"sign", {{3, -1.5, 1.5}}, "Sign"}; + +template +struct ConfLambdaBinning : o2::framework::ConfigurableGroup { + std::string prefix = Prefix; + V0_DEFAULT_BINNING(1.0, 1.2) +}; +template +struct ConfK0shortBinning : o2::framework::ConfigurableGroup { + std::string prefix = Prefix; + V0_DEFAULT_BINNING(0.475, 0.515) +}; +#undef V0_DEFAULT_BINNING + +constexpr const char PrefixLambdaBinning1[] = "LambdaBinning1"; +using ConfLambdaBinning1 = ConfLambdaBinning; +constexpr const char PrefixK0shortBinning1[] = "K0shortBinning1"; +using ConfK0shortBinning1 = ConfK0shortBinning; + +template +struct ConfV0QaBinning : o2::framework::ConfigurableGroup { + std::string prefix = Prefix; + o2::framework::ConfigurableAxis cosPa{"cosPa", {{100, 0.9, 1}}, "Cosine of poiting angle"}; + o2::framework::ConfigurableAxis dauDcaAtDecay{"dauDcaAtDecay", {{150, 0, 1.5}}, "Daughter DCA at decay vertex"}; + o2::framework::ConfigurableAxis decayVertex{"decayVertex", {{100, 0, 100}}, "Decay vertex"}; + o2::framework::ConfigurableAxis transRadius{"transRadius", {{100, 0, 100}}, "Transverse radius"}; + o2::framework::ConfigurableAxis massLambda{"massLambda", {{200, 1, 1.2}}, "mass for antiparticle hypothesis"}; + o2::framework::ConfigurableAxis massAntiLambda{"massAntiLambda", {{100, 1, 1.2}}, "mass for antiparticle hypothesis"}; + o2::framework::ConfigurableAxis massK0short{"massK0short", {{200, 0.45, 0.55}}, "Mass for k0short hypothesis"}; +}; + +constexpr const char PrefixLambdaQaBinning1[] = "LambdaQaBinning1"; +using ConfLambdaQaBinning1 = ConfV0QaBinning; + +constexpr const char PrefixK0shortQaBinning1[] = "K0shortQaBinning1"; +using ConfK0shortQaBinning1 = ConfV0QaBinning; + +// must be in sync with enum TrackVariables +// the enum gives the correct index in the array +constexpr std::array, kV0HistLast> HistTable = { + {{kPt, o2::framework::kTH1F, "hPt", "Transverse Momentum; p_{T} (GeV/#it{c}); Entries"}, + {kEta, o2::framework::kTH1F, "hEta", "Pseudorapdity; #eta; Entries"}, + {kPhi, o2::framework::kTH1F, "hPhi", "Azimuthal angle; #varphi; Entries"}, + {kMass, o2::framework::kTH1F, "hMass", "Invariant Mass; m_{Inv} (GeV/#it{c}^{2}); Entries"}, + {kSign, o2::framework::kTH1F, "hSign", "Sign (-1 -> antiparticle, 0 -> self conjugate, +1 -> particle); sign; Entries"}, + {kMassLambda, o2::framework::kTH1F, "hMassLambda", "#Lambda mass; m_{p#pi^{-}} (GeV/#it{c}^{2}); Entries"}, + {kMassAntiLambda, o2::framework::kTH1F, "hMassAntiLambda", "#bar{#Lambda} mass; m_{#bar{p}#pi^{+}} (GeV/#it{c}^{2}); Entries"}, + {kMassK0short, o2::framework::kTH1F, "hMassK0short", "K^{0}_{s} mass; m_{#pi^{+}#pi^{-}} (GeV/#it{c}^{2}); Entries"}, + {kCosPa, o2::framework::kTH1F, "hCosPa", "Cosine of pointing angle; coa(#alpha); Entries"}, + {kDecayDauDca, o2::framework::kTH1F, "hDauDca", "Daughter DCA at decay vertex ; DCA_{Decay vertex} (cm); Entries"}, + {kDecayVtxX, o2::framework::kTH1F, "hDecayVtxX", "X coordinate of decay vertex ; DV_{X} (cm); Entries"}, + {kDecayVtxY, o2::framework::kTH1F, "hDecayVtxY", "Y coordinate of decay vertex ; DV_{Y} (cm); Entries"}, + {kDecayVtxZ, o2::framework::kTH1F, "hDecayVtxZ", "Z coordinate of decay vertex ; DV_{Z} (cm); Entries"}, + {kDecayVtx, o2::framework::kTH1F, "hDecayVtx", "Distance of decay vertex from primary vertex ; DV (cm); Entries"}, + {kTransRadius, o2::framework::kTH1F, "hTransRadius", "Transverse radius ; r_{xy} (cm); Entries"}, + {kPtVsEta, o2::framework::kTH2F, "hPtVsEta", "p_{T} vs #eta; p_{T} (GeV/#it{c}) ; #eta"}, + {kPtVsPhi, o2::framework::kTH2F, "hPtVsPhi", "p_{T} vs #varphi; p_{T} (GeV/#it{c}) ; #varphi"}, + {kPhiVsEta, o2::framework::kTH2F, "hPhiVsEta", "#varphi vs #eta; #varphi ; #eta"}, + {kPtVsCosPa, o2::framework::kTH2F, "hPtVsCosPa", "Cosine of poiting angle vs p_{T}; cos(#alpha); p_{T} (GeV/#it{c})"}, + {kPtVsLambdaMass, o2::framework::kTH2F, "hPtVsLambdaMass", "p_{T} vs #Lambda mass; p_{T} (GeV/#it{c}); m_{p#pi^{-}} (GeV/#it{c}^{2})"}, + {kPtVsAntiLambdaMass, o2::framework::kTH2F, "hPtVsAntiLambdaMass", "p_{T} vs #bar{#Lambda} mass; p_{T} (GeV/#it{c}); m_{#bar{p}#pi^{+}} (GeV/#it{c}^{2})"}, + {kPtVsK0shortMass, o2::framework::kTH2F, "hPtVsK0shortMass", "p_{T} vs K^{0}_{S} mass; p_{T} (GeV/#it{c}); m_{#pi^{+}#pi^{-}} (GeV/#it{c}^{2})"}, + {kK0shortMassVsLambdaMass, o2::framework::kTH2F, "hK0shortMassVsLambdaMass", " K^{0}_{S} mass vs #Lambda mass; m_{#pi^{+}#pi^{-}} (GeV/#it{c}^{2}); m_{p#pi^{-}} (GeV/#it{c}^{2})"}, + {kK0shortMassVsAntiLambdaMass, o2::framework::kTH2F, "hK0shortMassVsAntiLambdaMass", "K^{0}_{S} mass vs #bar{#Lambda} mass; m_{#pi^{+}#pi^{-}} (GeV/#it{c}^{2}); m_{#bar{p}#pi^{+}} (GeV/#it{c}^{2})"}, + {kLambdaMassVsAntiLambdaMass, o2::framework::kTH2F, "hLambdaMassVsAntiLambdaMass", "#Lambda mass vs #bar{#Lambda}; m_{p#pi^{-}} (GeV/#it{c}^{2}); m_{#bar{p}#pi^{+}} (GeV/#it{c}^{2})"}}}; + +template +auto makeV0HistSpecMap(const T& confBinningAnalysis) +{ + return std::map>{ + {kPt, {confBinningAnalysis.pt}}, + {kEta, {confBinningAnalysis.eta}}, + {kPhi, {confBinningAnalysis.phi}}, + {kMass, {confBinningAnalysis.mass}}, + {kSign, {confBinningAnalysis.sign}}}; +} + +template +std::map> makeV0QaHistSpecMap(T1 const& confBinningAnalysis, T2 const& confBinningQa) +{ + return std::map>{ + {kPt, {confBinningAnalysis.pt}}, + {kEta, {confBinningAnalysis.eta}}, + {kPhi, {confBinningAnalysis.phi}}, + {kMass, {confBinningAnalysis.mass}}, + {kSign, {confBinningAnalysis.sign}}, + {kCosPa, {confBinningQa.cosPa}}, + {kDecayDauDca, {confBinningQa.dauDcaAtDecay}}, + {kDecayVtxX, {confBinningQa.decayVertex}}, + {kDecayVtxY, {confBinningQa.decayVertex}}, + {kDecayVtxZ, {confBinningQa.decayVertex}}, + {kDecayVtx, {confBinningQa.decayVertex}}, + {kTransRadius, {confBinningQa.transRadius}}, + {kPtVsEta, {confBinningAnalysis.pt, confBinningAnalysis.eta}}, + {kPtVsPhi, {confBinningAnalysis.pt, confBinningAnalysis.phi}}, + {kPhiVsEta, {confBinningAnalysis.phi, confBinningAnalysis.eta}}, + {kPtVsCosPa, {confBinningAnalysis.pt, confBinningQa.cosPa}}, + {kMassLambda, {confBinningQa.massLambda}}, + {kMassAntiLambda, {confBinningQa.massAntiLambda}}, + {kMassK0short, {confBinningQa.massK0short}}, + {kPtVsLambdaMass, {confBinningAnalysis.pt, confBinningQa.massLambda}}, + {kPtVsAntiLambdaMass, {confBinningAnalysis.pt, confBinningQa.massAntiLambda}}, + {kPtVsK0shortMass, {confBinningAnalysis.pt, confBinningQa.massK0short}}, + {kLambdaMassVsAntiLambdaMass, {confBinningQa.massLambda, confBinningQa.massAntiLambda}}, + {kK0shortMassVsLambdaMass, {confBinningQa.massK0short, confBinningQa.massLambda}}, + {kK0shortMassVsAntiLambdaMass, {confBinningQa.massK0short, confBinningQa.massAntiLambda}}}; +}; + +constexpr char PrefixLambdaQa[] = "LambdaQA/"; +constexpr char PrefixLambda[] = "Lambda/"; +constexpr char PrefixK0shortQa[] = "K0shortQa/"; +constexpr char PrefixK0short[] = "K0short/"; + +constexpr char PrefixLambdaCascade[] = "LambdaCascadeQa/"; + +constexpr std::string_view AnalysisDir = "Kinematics/"; +constexpr std::string_view QaDir = "QA/"; + +/// \class FemtoDreamEventHisto +/// \brief Class for histogramming event properties +// template +template +class V0HistManager +{ + public: + /// Destructor + virtual ~V0HistManager() = default; + /// Initializes histograms for the task + /// \param registry Histogram registry to be passed + /// + void init(o2::framework::HistogramRegistry* registry, + std::map> V0Specs, + std::map> PosDauSpecs, + std::map> NegDauSpecs) + { + mHistogramRegistry = registry; + mPosDauManager.init(registry, PosDauSpecs); + mNegDauManager.init(registry, NegDauSpecs); + + if constexpr (modes::isFlagSet(mode, modes::Mode::kAnalysis)) { + std::string analysisDir = std::string(v0Prefix) + std::string(AnalysisDir); + mHistogramRegistry->add(analysisDir + GetHistNamev2(kPt, HistTable), GetHistDesc(kPt, HistTable), GetHistType(kPt, HistTable), {V0Specs[kPt]}); + mHistogramRegistry->add(analysisDir + GetHistNamev2(kEta, HistTable), GetHistDesc(kEta, HistTable), GetHistType(kEta, HistTable), {V0Specs[kEta]}); + mHistogramRegistry->add(analysisDir + GetHistNamev2(kPhi, HistTable), GetHistDesc(kPhi, HistTable), GetHistType(kPhi, HistTable), {V0Specs[kPhi]}); + mHistogramRegistry->add(analysisDir + GetHistNamev2(kMass, HistTable), GetHistDesc(kMass, HistTable), GetHistType(kMass, HistTable), {V0Specs[kMass]}); + mHistogramRegistry->add(analysisDir + GetHistNamev2(kSign, HistTable), GetHistDesc(kSign, HistTable), GetHistType(kSign, HistTable), {V0Specs[kSign]}); + } + + if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { + std::string qaDir = std::string(v0Prefix) + std::string(QaDir); + + mHistogramRegistry->add(qaDir + GetHistNamev2(kCosPa, HistTable), GetHistDesc(kCosPa, HistTable), GetHistType(kCosPa, HistTable), {V0Specs[kCosPa]}); + mHistogramRegistry->add(qaDir + GetHistNamev2(kDecayDauDca, HistTable), GetHistDesc(kDecayDauDca, HistTable), GetHistType(kDecayDauDca, HistTable), {V0Specs[kDecayDauDca]}); + mHistogramRegistry->add(qaDir + GetHistNamev2(kDecayVtxX, HistTable), GetHistDesc(kDecayVtxX, HistTable), GetHistType(kDecayVtxX, HistTable), {V0Specs[kDecayVtxX]}); + mHistogramRegistry->add(qaDir + GetHistNamev2(kDecayVtxY, HistTable), GetHistDesc(kDecayVtxY, HistTable), GetHistType(kDecayVtxY, HistTable), {V0Specs[kDecayVtxY]}); + mHistogramRegistry->add(qaDir + GetHistNamev2(kDecayVtxZ, HistTable), GetHistDesc(kDecayVtxZ, HistTable), GetHistType(kDecayVtxZ, HistTable), {V0Specs[kDecayVtxZ]}); + mHistogramRegistry->add(qaDir + GetHistNamev2(kDecayVtx, HistTable), GetHistDesc(kDecayVtx, HistTable), GetHistType(kDecayVtx, HistTable), {V0Specs[kDecayVtx]}); + mHistogramRegistry->add(qaDir + GetHistNamev2(kTransRadius, HistTable), GetHistDesc(kTransRadius, HistTable), GetHistType(kTransRadius, HistTable), {V0Specs[kTransRadius]}); + + // qa 2d + mHistogramRegistry->add(qaDir + GetHistNamev2(kPtVsEta, HistTable), GetHistDesc(kPtVsEta, HistTable), GetHistType(kPtVsEta, HistTable), {V0Specs[kPtVsEta]}); + mHistogramRegistry->add(qaDir + GetHistNamev2(kPtVsPhi, HistTable), GetHistDesc(kPtVsPhi, HistTable), GetHistType(kPtVsPhi, HistTable), {V0Specs[kPtVsPhi]}); + mHistogramRegistry->add(qaDir + GetHistNamev2(kPhiVsEta, HistTable), GetHistDesc(kPhiVsEta, HistTable), GetHistType(kPhiVsEta, HistTable), {V0Specs[kPhiVsEta]}); + mHistogramRegistry->add(qaDir + GetHistNamev2(kPtVsCosPa, HistTable), GetHistDesc(kPtVsCosPa, HistTable), GetHistType(kPtVsCosPa, HistTable), {V0Specs[kPtVsCosPa]}); + + mHistogramRegistry->add(qaDir + GetHistNamev2(kMassLambda, HistTable), GetHistDesc(kMassLambda, HistTable), GetHistType(kMassLambda, HistTable), {V0Specs[kMassLambda]}); + mHistogramRegistry->add(qaDir + GetHistNamev2(kMassAntiLambda, HistTable), GetHistDesc(kMassAntiLambda, HistTable), GetHistType(kMassAntiLambda, HistTable), {V0Specs[kMassAntiLambda]}); + mHistogramRegistry->add(qaDir + GetHistNamev2(kMassK0short, HistTable), GetHistDesc(kMassK0short, HistTable), GetHistType(kMassK0short, HistTable), {V0Specs[kMassK0short]}); + mHistogramRegistry->add(qaDir + GetHistNamev2(kPtVsLambdaMass, HistTable), GetHistDesc(kPtVsLambdaMass, HistTable), GetHistType(kPtVsLambdaMass, HistTable), {V0Specs[kPtVsLambdaMass]}); + mHistogramRegistry->add(qaDir + GetHistNamev2(kPtVsAntiLambdaMass, HistTable), GetHistDesc(kPtVsAntiLambdaMass, HistTable), GetHistType(kPtVsAntiLambdaMass, HistTable), {V0Specs[kPtVsAntiLambdaMass]}); + mHistogramRegistry->add(qaDir + GetHistNamev2(kPtVsK0shortMass, HistTable), GetHistDesc(kPtVsK0shortMass, HistTable), GetHistType(kPtVsK0shortMass, HistTable), {V0Specs[kPtVsK0shortMass]}); + mHistogramRegistry->add(qaDir + GetHistNamev2(kLambdaMassVsAntiLambdaMass, HistTable), GetHistDesc(kLambdaMassVsAntiLambdaMass, HistTable), GetHistType(kLambdaMassVsAntiLambdaMass, HistTable), {V0Specs[kLambdaMassVsAntiLambdaMass]}); + mHistogramRegistry->add(qaDir + GetHistNamev2(kK0shortMassVsLambdaMass, HistTable), GetHistDesc(kK0shortMassVsLambdaMass, HistTable), GetHistType(kK0shortMassVsLambdaMass, HistTable), {V0Specs[kK0shortMassVsLambdaMass]}); + mHistogramRegistry->add(qaDir + GetHistNamev2(kK0shortMassVsAntiLambdaMass, HistTable), GetHistDesc(kK0shortMassVsAntiLambdaMass, HistTable), GetHistType(kK0shortMassVsAntiLambdaMass, HistTable), {V0Specs[kK0shortMassVsAntiLambdaMass]}); + } + } + + template + void fill(T1 const& v0candidate, T2 const& /*tracks*/) + { + + auto posDaughter = v0candidate.template posDau_as(); + mPosDauManager.fill(posDaughter); + auto negDaughter = v0candidate.template negDau_as(); + mNegDauManager.fill(negDaughter); + + if constexpr (modes::isFlagSet(mode, modes::Mode::kAnalysis)) { + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(AnalysisDir) + HIST(GetHistName(kPt, HistTable)), v0candidate.pt()); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(AnalysisDir) + HIST(GetHistName(kEta, HistTable)), v0candidate.eta()); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(AnalysisDir) + HIST(GetHistName(kPhi, HistTable)), v0candidate.phi()); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(AnalysisDir) + HIST(GetHistName(kMass, HistTable)), v0candidate.mass()); + + if constexpr (modes::isEqual(v0, modes::V0::kLambda) || modes::isEqual(v0, modes::V0::kAntiLambda)) { + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(AnalysisDir) + HIST(GetHistName(kSign, HistTable)), v0candidate.sign()); + } + if constexpr (modes::isEqual(v0, modes::V0::kK0short)) { + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(AnalysisDir) + HIST(GetHistName(kSign, HistTable)), 0); + } + } + + if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(GetHistName(kCosPa, HistTable)), v0candidate.cosPa()); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(GetHistName(kDecayDauDca, HistTable)), v0candidate.dauDca()); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(GetHistName(kDecayVtxX, HistTable)), v0candidate.decayVtxX()); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(GetHistName(kDecayVtxY, HistTable)), v0candidate.decayVtxY()); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(GetHistName(kDecayVtxZ, HistTable)), v0candidate.decayVtxZ()); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(GetHistName(kDecayVtx, HistTable)), v0candidate.decayVtx()); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(GetHistName(kTransRadius, HistTable)), v0candidate.transRadius()); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(GetHistName(kPtVsEta, HistTable)), v0candidate.pt(), v0candidate.eta()); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(GetHistName(kPtVsPhi, HistTable)), v0candidate.pt(), v0candidate.phi()); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(GetHistName(kPhiVsEta, HistTable)), v0candidate.phi(), v0candidate.eta()); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(GetHistName(kPtVsCosPa, HistTable)), v0candidate.pt(), v0candidate.cosPa()); + + if constexpr (modes::isEqual(v0, modes::V0::kLambda) || modes::isEqual(v0, modes::V0::kAntiLambda)) { + float massLambda, massAntiLambda; + if (v0candidate.sign() > 0) { + massLambda = v0candidate.mass(); + massAntiLambda = v0candidate.massAnti(); + } else { + massLambda = v0candidate.massAnti(); + massAntiLambda = v0candidate.mass(); + } + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(GetHistName(kMassLambda, HistTable)), massLambda); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(GetHistName(kMassAntiLambda, HistTable)), massAntiLambda); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(GetHistName(kMassK0short, HistTable)), v0candidate.massK0short()); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(GetHistName(kPtVsLambdaMass, HistTable)), v0candidate.pt(), massLambda); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(GetHistName(kPtVsAntiLambdaMass, HistTable)), v0candidate.pt(), massAntiLambda); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(GetHistName(kPtVsK0shortMass, HistTable)), v0candidate.pt(), v0candidate.massK0short()); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(GetHistName(kLambdaMassVsAntiLambdaMass, HistTable)), massLambda, massAntiLambda); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(GetHistName(kK0shortMassVsLambdaMass, HistTable)), v0candidate.massK0short(), massLambda); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(GetHistName(kK0shortMassVsAntiLambdaMass, HistTable)), v0candidate.massK0short(), massAntiLambda); + } + if constexpr (modes::isEqual(v0, modes::V0::kK0short)) { + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(GetHistName(kMassLambda, HistTable)), v0candidate.massLambda()); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(GetHistName(kMassAntiLambda, HistTable)), v0candidate.massAntiLambda()); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(GetHistName(kMassK0short, HistTable)), v0candidate.mass()); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(GetHistName(kPtVsLambdaMass, HistTable)), v0candidate.pt(), v0candidate.massLambda()); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(GetHistName(kPtVsAntiLambdaMass, HistTable)), v0candidate.pt(), v0candidate.massAntiLambda()); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(GetHistName(kPtVsK0shortMass, HistTable)), v0candidate.pt(), v0candidate.mass()); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(GetHistName(kLambdaMassVsAntiLambdaMass, HistTable)), v0candidate.massLambda(), v0candidate.massAntiLambda()); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(GetHistName(kK0shortMassVsLambdaMass, HistTable)), v0candidate.mass(), v0candidate.massLambda()); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(GetHistName(kK0shortMassVsAntiLambdaMass, HistTable)), v0candidate.mass(), v0candidate.massAntiLambda()); + } + } + } + + private: + o2::framework::HistogramRegistry* mHistogramRegistry = nullptr; + + trackhistmanager::TrackHistManager mPosDauManager; + trackhistmanager::TrackHistManager mNegDauManager; +}; +}; // namespace v0histmanager +}; // namespace o2::analysis::femto +#endif // PWGCF_FEMTO_CORE_V0HISTMANAGER_H_ diff --git a/PWGCF/Femto/DataModel/CMakeLists.txt b/PWGCF/Femto/DataModel/CMakeLists.txt new file mode 100644 index 00000000000..4c182222bf2 --- /dev/null +++ b/PWGCF/Femto/DataModel/CMakeLists.txt @@ -0,0 +1,10 @@ +# Copyright 2019-2024 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. diff --git a/PWGCF/Femto/DataModel/FemtoTables.h b/PWGCF/Femto/DataModel/FemtoTables.h new file mode 100644 index 00000000000..e96708d657d --- /dev/null +++ b/PWGCF/Femto/DataModel/FemtoTables.h @@ -0,0 +1,575 @@ +// 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 FemtoTables.h +/// \brief Datamodel for femto analysis +/// \author Anton Riedel, TU München, anton.riedel@cern.ch + +#ifndef PWGCF_FEMTO_DATAMODEL_FEMTOTABLES_H_ +#define PWGCF_FEMTO_DATAMODEL_FEMTOTABLES_H_ + +#include "PWGCF/Femto/Core/dataTypes.h" + +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" + +#include "Framework/ASoA.h" +#include "Framework/AnalysisDataModel.h" +#include "Framework/Expressions.h" + +#include +#include + +namespace o2::aod +{ + +namespace femtocollisions +{ +DECLARE_SOA_COLUMN(PosX, posX, float); //! x coordinate of vertex +DECLARE_SOA_COLUMN(PosY, posY, float); //! y coordinate of vertex +DECLARE_SOA_COLUMN(PosZ, posZ, float); //! z coordinate of vertex +DECLARE_SOA_COLUMN(Mult, mult, float); //! Multiplicity estimator set by producer +DECLARE_SOA_COLUMN(Cent, cent, float); //! Centrality (~= multiplicity percentile) estimator set by producer +DECLARE_SOA_COLUMN(MagField, magField, float); //! Magnetic field of the event +DECLARE_SOA_COLUMN(Sphericity, sphericity, float); //! Sphericity of the event +DECLARE_SOA_COLUMN(Occupancy, occupancy, float); //! occupancy of the event +DECLARE_SOA_COLUMN(Qn, qn, float); //! qn bins for dividing events +} // namespace femtocollisions + +// table for basic collision information +DECLARE_SOA_TABLE_STAGED_VERSIONED(FCols_001, "FCOL", 1, //! femto collisions + o2::soa::Index<>, + femtocollisions::PosZ, + femtocollisions::Mult, + femtocollisions::Cent, + femtocollisions::Sphericity, + femtocollisions::MagField); +using FCols = FCols_001; + +// table for occupancy +DECLARE_SOA_TABLE_STAGED_VERSIONED(FColOccs_001, "FCOLOCC", 1, //! occupancy + femtocollisions::Occupancy); +using FColOccs = FColOccs_001; + +// table for qn values +DECLARE_SOA_TABLE_STAGED_VERSIONED(FColQns_001, "FCOLQN", 1, //! qn vector + femtocollisions::Qn); +using FColQns = FColQns_001; + +// table for primary vertex location +DECLARE_SOA_TABLE_STAGED_VERSIONED(FColPos_001, "FCOLPOS", 1, //! full vertex position + femtocollisions::PosX, + femtocollisions::PosY); +using FColPos = FColPos_001; + +// table for different multiplicity estimators +DECLARE_SOA_TABLE_STAGED_VERSIONED(FColMults_001, "FCOLMULT", 1, //! multiplicities + mult::MultFT0A, mult::MultFT0C, //! FIT detectors + mult::MultNTracksPVeta1, //! number of PV contribs total + mult::MultNTracksPVetaHalf, //! global track multiplicities + evsel::NumTracksInTimeRange, //! occupancy (number of track in time range) + evsel::SumAmpFT0CInTimeRange); //! occupancy (FT0C amplitude in time range) +using FColMults = FColMults_001; + +// table for different centrality (multiplicity percentile) estimators +DECLARE_SOA_TABLE_STAGED_VERSIONED(FColCents_001, "FCOLCENT", 1, //! centralities + cent::CentFT0A, //! centrality from FT0A + cent::CentFT0C); //! centrality from FT0C +using FColCents = FColCents_001; + +namespace femtobase +// all "basic" information to perform femto analysis, i.e. collision index and kinematics +// split kinematics in stored, i.e. stored in derived data, and dynmaic, i.e. can be computed on the fly +{ +namespace stored +{ +// static columns +DECLARE_SOA_INDEX_COLUMN(Collision, collision); //! collision index +DECLARE_SOA_COLUMN(SignedPt, signedPt, float); //! signed pt +DECLARE_SOA_COLUMN(Pt, pt, float); //! pt +DECLARE_SOA_COLUMN(Eta, eta, float); //! eta +DECLARE_SOA_COLUMN(Phi, phi, float); //! phi +DECLARE_SOA_COLUMN(Mass, mass, float); //! mass of particle +DECLARE_SOA_COLUMN(MassAnti, massAnti, float); //! mass of antiparticle +} // namespace stored + +namespace dynamic +{ +// dynamic columns +DECLARE_SOA_DYNAMIC_COLUMN(Sign, sign, //! sign of the track + [](float signedPt) -> int { + return signedPt > 0.f ? 1 : -1; + }); +DECLARE_SOA_DYNAMIC_COLUMN(Pt, pt, //! transverse momentum + [](float signedPt) -> float { + return std::fabs(signedPt); + }); +// use fabs for pt so it can also be used with signed pt +DECLARE_SOA_DYNAMIC_COLUMN(Px, px, //! momentum in x + [](float pt, float phi) -> float { + return std::fabs(pt) * std::sin(phi); + }); +DECLARE_SOA_DYNAMIC_COLUMN(Py, py, //! momentum in y + [](float pt, float phi) -> float { + return std::fabs(pt) * std::cos(phi); + }); +DECLARE_SOA_DYNAMIC_COLUMN(Pz, pz, //! momentum in z + [](float pt, float eta) -> float { + return std::fabs(pt) * std::sinh(eta); + }); +DECLARE_SOA_DYNAMIC_COLUMN(P, p, //! momentum + [](float pt, float eta) -> float { + return std::fabs(pt) * std::cosh(eta); + }); +DECLARE_SOA_DYNAMIC_COLUMN(Theta, theta, //! theta + [](float eta) -> float { + return 2.f * std::atan(std::exp(-eta)); + }); +} // namespace dynamic +} // namespace femtobase + +namespace femtotracks +{ +// columns for track selections +DECLARE_SOA_COLUMN(TrackMask, trackMask, femtodatatypes::TrackMaskType); //! Bitmask for track selections + +// columns for DCA +DECLARE_SOA_COLUMN(DcaXY, dcaXY, float); //! Dca in XY plane +DECLARE_SOA_COLUMN(DcaZ, dcaZ, float); //! Dca in Z direction +DECLARE_SOA_DYNAMIC_COLUMN(Dca, dca, [](float dcaXY, float dcaZ) -> float { return std::hypot(dcaXY, dcaZ); }); //! Dca + +// its related information +DECLARE_SOA_COLUMN(IsPVContributor, isPVContributor, bool); //! True if track is PV contributer +DECLARE_SOA_COLUMN(ItsNCls, itsNCls, uint8_t); //! Number of Its clusters (max 7) +DECLARE_SOA_COLUMN(ItsNClsInnerBarrel, itsNClsInnerBarrel, uint8_t); //! Number of Its clusters in the inner barrel (max 3) +DECLARE_SOA_COLUMN(ItsChi2NCl, itsChi2NCl, float); //! Its chi2 / cluster +DECLARE_SOA_COLUMN(ItsClusterSizes, itsClusterSizes, uint32_t); //! Its cluster sizes (4 bits per layer) + +// tpc related information +DECLARE_SOA_COLUMN(TpcSignal, tpcSignal, float); //! Tpc signal +DECLARE_SOA_COLUMN(TpcInnerParam, tpcInnerParam, bool); //! Momentum at inner wall of Tpc +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_DYNAMIC_COLUMN(TpcCrossedRowsOverFound, tpcCrossedRowsOverFound, //! Number of crossed rows over found Tpc clusters + [](uint8_t tpcNClsFound, uint8_t tpcNClsCrossedRows) -> float { return static_cast(tpcNClsCrossedRows) / static_cast(tpcNClsFound); }); +DECLARE_SOA_COLUMN(TpcNClsShared, tpcNClsShared, uint8_t); //! Number of shared Tpc clusters +DECLARE_SOA_DYNAMIC_COLUMN(TpcSharedOverFound, tpcSharedOverFound, //! Number of crossed rows over found Tpc clusters + [](uint8_t tpcNclsFound, uint8_t tpcNClsShared) -> float { return static_cast(tpcNClsShared) / static_cast(tpcNclsFound); }); +DECLARE_SOA_COLUMN(TpcChi2NCl, tpcChi2NCl, float); //! Tpc chi2 + +// tof related information +DECLARE_SOA_COLUMN(TofBeta, tofBeta, float); //! Tof beta +DECLARE_SOA_COLUMN(TofMass, tofMass, float); //! Tof mass + +// PID information +// ITS PID information +DECLARE_SOA_COLUMN(ItsNSigmaEl, itsNSigmaEl, float); //! Nsigma separation with the Its for electron +DECLARE_SOA_COLUMN(ItsNSigmaPi, itsNSigmaPi, float); //! Nsigma separation with the Its for pion +DECLARE_SOA_COLUMN(ItsNSigmaKa, itsNSigmaKa, float); //! Nsigma separation with the Its for kaon +DECLARE_SOA_COLUMN(ItsNSigmaPr, itsNSigmaPr, float); //! Nsigma separation with the Its for proton +DECLARE_SOA_COLUMN(ItsNSigmaDe, itsNSigmaDe, float); //! Nsigma separation with the Its for deuteron +DECLARE_SOA_COLUMN(ItsNSigmaTr, itsNSigmaTr, float); //! Nsigma separation with the Its for triton +DECLARE_SOA_COLUMN(ItsNSigmaHe, itsNSigmaHe, float); //! Nsigma separation with the Its for helium3 + +// TPC PID information +DECLARE_SOA_COLUMN(TpcNSigmaEl, tpcNSigmaEl, float); //! Nsigma separation with the Tpc for electron +DECLARE_SOA_COLUMN(TpcNSigmaPi, tpcNSigmaPi, float); //! Nsigma separation with the Tpc for pion +DECLARE_SOA_COLUMN(TpcNSigmaKa, tpcNSigmaKa, float); //! Nsigma separation with the Tpc for kaon +DECLARE_SOA_COLUMN(TpcNSigmaPr, tpcNSigmaPr, float); //! Nsigma separation with the Tpc for proton +DECLARE_SOA_COLUMN(TpcNSigmaDe, tpcNSigmaDe, float); //! Nsigma separation with the Tpc for deuteron +DECLARE_SOA_COLUMN(TpcNSigmaTr, tpcNSigmaTr, float); //! Nsigma separation with the Tpc for triton +DECLARE_SOA_COLUMN(TpcNSigmaHe, tpcNSigmaHe, float); //! Nsigma separation with the Tpc for helium3 + +// TOF PID information +DECLARE_SOA_COLUMN(TofNSigmaEl, tofNSigmaEl, float); //! Nsigma separation with the Tof for electron +DECLARE_SOA_COLUMN(TofNSigmaPi, tofNSigmaPi, float); //! Nsigma separation with the Tof for pion +DECLARE_SOA_COLUMN(TofNSigmaKa, tofNSigmaKa, float); //! Nsigma separation with the Tof for kaon +DECLARE_SOA_COLUMN(TofNSigmaPr, tofNSigmaPr, float); //! Nsigma separation with the Tof for proton +DECLARE_SOA_COLUMN(TofNSigmaDe, tofNSigmaDe, float); //! Nsigma separation with the Tof for deuteron +DECLARE_SOA_COLUMN(TofNSigmaTr, tofNSigmaTr, float); //! Nsigma separation with the Tof for triton +DECLARE_SOA_COLUMN(TofNSigmaHe, tofNSigmaHe, float); //! Nsigma separation with the Tof for helium3 + +DECLARE_SOA_DYNAMIC_COLUMN(TpcitsNSigmaEl, tpcitsNSigmaEl, [](float tpc, float its) -> float { return std::hypot(tpc, its); }); //! Combined Nsigma separation with Tpc and Its for electon +DECLARE_SOA_DYNAMIC_COLUMN(TpcitsNSigmaPi, tpcitsNSigmaPi, [](float tpc, float its) -> float { return std::hypot(tpc, its); }); //! Combined Nsigma separation with Tpc and Its for pion +DECLARE_SOA_DYNAMIC_COLUMN(TpcitsNSigmaKa, tpcitsNSigmaKa, [](float tpc, float its) -> float { return std::hypot(tpc, its); }); //! Combined Nsigma separation with Tpc and Its for kaon +DECLARE_SOA_DYNAMIC_COLUMN(TpcitsNSigmaPr, tpcitsNSigmaPr, [](float tpc, float its) -> float { return std::hypot(tpc, its); }); //! Combined Nsigma separation with Tpc and Its for proton +DECLARE_SOA_DYNAMIC_COLUMN(TpcitsNSigmaDe, tpcitsNSigmaDe, [](float tpc, float its) -> float { return std::hypot(tpc, its); }); //! Combined Nsigma separation with Tpc and Its for deuteron +DECLARE_SOA_DYNAMIC_COLUMN(TpcitsNSigmaTr, tpcitsNSigmaTr, [](float tpc, float its) -> float { return std::hypot(tpc, its); }); //! Combined Nsigma separation with Tpc and Its for trition +DECLARE_SOA_DYNAMIC_COLUMN(TpcitsNSigmaHe, tpcitsNSigmaHe, [](float tpc, float its) -> float { return std::hypot(tpc, its); }); //! Combined Nsigma separation with Tpc and Its for helium3 + +DECLARE_SOA_DYNAMIC_COLUMN(TpctofNSigmaEl, tpctofNSigmaEl, [](float tpc, float tof) -> float { return std::hypot(tpc, tof); }); //! Combined Nsigma separation with Tpc and Tof for electons +DECLARE_SOA_DYNAMIC_COLUMN(TpctofNSigmaPi, tpctofNSigmaPi, [](float tpc, float tof) -> float { return std::hypot(tpc, tof); }); //! Combined Nsigma separation with Tpc and Tof for pion +DECLARE_SOA_DYNAMIC_COLUMN(TpctofNSigmaKa, tpctofNSigmaKa, [](float tpc, float tof) -> float { return std::hypot(tpc, tof); }); //! Combined Nsigma separation with Tpc and Tof for kaon +DECLARE_SOA_DYNAMIC_COLUMN(TpctofNSigmaPr, tpctofNSigmaPr, [](float tpc, float tof) -> float { return std::hypot(tpc, tof); }); //! Combined Nsigma separation with Tpc and Tof for proton +DECLARE_SOA_DYNAMIC_COLUMN(TpctofNSigmaDe, tpctofNSigmaDe, [](float tpc, float tof) -> float { return std::hypot(tpc, tof); }); //! Combined Nsigma separation with Tpc and Tof for deuteron +DECLARE_SOA_DYNAMIC_COLUMN(TpctofNSigmaTr, tpctofNSigmaTr, [](float tpc, float tof) -> float { return std::hypot(tpc, tof); }); //! Combined Nsigma separation with Tpc and Tof for triton +DECLARE_SOA_DYNAMIC_COLUMN(TpctofNSigmaHe, tpctofNSigmaHe, [](float tpc, float tof) -> float { return std::hypot(tpc, tof); }); //! Combined Nsigma separation with Tpc and Tof for helium3 + +} // namespace femtotracks + +// table for basic track information +DECLARE_SOA_TABLE_STAGED_VERSIONED(FTracks_001, "FTRACK", 1, //! femto tracks + o2::soa::Index<>, + femtobase::stored::CollisionId, + femtobase::stored::SignedPt, + femtobase::stored::Eta, + femtobase::stored::Phi, + femtobase::dynamic::Sign, + femtobase::dynamic::Pt, + femtobase::dynamic::P, + femtobase::dynamic::Px, + femtobase::dynamic::Py, + femtobase::dynamic::Pz, + femtobase::dynamic::Theta); +using FTracks = FTracks_001; + +// table for track selections and PID selections +DECLARE_SOA_TABLE_STAGED_VERSIONED(FTrackMasks_001, "FTRACKMASK", 1, //! track masks + femtotracks::TrackMask); +using FTrackMasks = FTrackMasks_001; + +// table for track DCA +DECLARE_SOA_TABLE_STAGED_VERSIONED(FTrackDcas_001, "FTRACKDCAS", 1, //! track dcas + femtotracks::DcaXY, + femtotracks::DcaZ, + femtotracks::Dca); +using FTrackDcas = FTrackDcas_001; + +// table for extra track information +DECLARE_SOA_TABLE_STAGED_VERSIONED(FTrackExtras_001, "FTRACKEXTRA", 1, //! track extra information + femtotracks::IsPVContributor, + femtotracks::ItsNCls, + femtotracks::ItsNClsInnerBarrel, + femtotracks::ItsChi2NCl, + femtotracks::ItsClusterSizes, + femtotracks::TpcSignal, + femtotracks::TpcInnerParam, + femtotracks::TpcNClsFound, + femtotracks::TpcNClsCrossedRows, + femtotracks::TpcNClsShared, + femtotracks::TofBeta, + femtotracks::TofMass, + femtotracks::TpcCrossedRowsOverFound, + femtotracks::TpcSharedOverFound); +using FTrackExtras = FTrackExtras_001; + +// table for extra PID information +DECLARE_SOA_TABLE_STAGED_VERSIONED(FElectronPids_001, "FELECTRONPID", 1, //! full electron pid + femtotracks::ItsNSigmaEl, + femtotracks::TpcNSigmaEl, + femtotracks::TofNSigmaEl, + femtotracks::TpcitsNSigmaEl, + femtotracks::TpctofNSigmaEl); +using FElectronPids = FElectronPids_001; +DECLARE_SOA_TABLE_STAGED_VERSIONED(FPionPids_001, "FPIONPID", 1, //! full pion pid + femtotracks::ItsNSigmaPi, + femtotracks::TpcNSigmaPi, + femtotracks::TofNSigmaPi, + femtotracks::TpcitsNSigmaPi, + femtotracks::TpctofNSigmaPi); +using FPionPids = FPionPids_001; +DECLARE_SOA_TABLE_STAGED_VERSIONED(FKaonPids_001, "FKAONPID", 1, //! full kaon pid + femtotracks::ItsNSigmaKa, + femtotracks::TpcNSigmaKa, + femtotracks::TofNSigmaKa, + femtotracks::TpcitsNSigmaKa, + femtotracks::TpctofNSigmaKa); +using FKaonPids = FKaonPids_001; +DECLARE_SOA_TABLE_STAGED_VERSIONED(FProtonPids_001, "FPROTONPID", 1, //! full proton pid + femtotracks::ItsNSigmaPr, + femtotracks::TpcNSigmaPr, + femtotracks::TofNSigmaPr, + femtotracks::TpcitsNSigmaPr, + femtotracks::TpctofNSigmaPr); +using FProtonPids = FProtonPids_001; +DECLARE_SOA_TABLE_STAGED_VERSIONED(FDeuteronPids_001, "FDEUTERONPID", 1, //! full deuteron pid + femtotracks::ItsNSigmaDe, + femtotracks::TpcNSigmaDe, + femtotracks::TofNSigmaDe, + femtotracks::TpcitsNSigmaDe, + femtotracks::TpctofNSigmaDe); +using FDeuteronPids = FDeuteronPids_001; +DECLARE_SOA_TABLE_STAGED_VERSIONED(FTritonPids_001, "FTRITONPID", 1, //! full triton pid + femtotracks::ItsNSigmaTr, + femtotracks::TpcNSigmaTr, + femtotracks::TofNSigmaTr, + femtotracks::TpcitsNSigmaTr, + femtotracks::TpctofNSigmaTr); +using FTritonPids = FTritonPids_001; +DECLARE_SOA_TABLE_STAGED_VERSIONED(FHeliumPids_001, "FHELIUMPID", 1, //! full helium3 pid + femtotracks::ItsNSigmaHe, + femtotracks::TpcNSigmaHe, + femtotracks::TofNSigmaHe, + femtotracks::TpcitsNSigmaHe, + femtotracks::TpctofNSigmaHe); +using FHeliumPids = FHeliumPids_001; + +using FTrackPids = soa::Join; + +namespace femtotwotrackresonances +{ +// columns for resonance bit masks +DECLARE_SOA_COLUMN(Mask, mask, femtodatatypes::TwoTrackResonanceMaskType); //! Bitmask for resonance selections + +// id columns for resonance daughter tracks +DECLARE_SOA_INDEX_COLUMN_FULL(PosDau, posDau, int32_t, FTracks, "_PosDau"); //! index column for positive daughter track +DECLARE_SOA_INDEX_COLUMN_FULL(NegDau, negDau, int32_t, FTracks, "_NegDau"); //! index column for negative daughter track +} // namespace femtotwotrackresonances +// table for phis +DECLARE_SOA_TABLE_STAGED_VERSIONED(FPhis_001, "FPHI", 1, //! femto phis + o2::soa::Index<>, + femtobase::stored::CollisionId, + femtobase::stored::Pt, + femtobase::stored::Eta, + femtobase::stored::Phi, + femtobase::stored::Mass, + femtotwotrackresonances::PosDauId, + femtotwotrackresonances::NegDauId, + femtobase::dynamic::P, + femtobase::dynamic::Px, + femtobase::dynamic::Py, + femtobase::dynamic::Pz, + femtobase::dynamic::Theta); +using FPhis = FPhis_001; +DECLARE_SOA_TABLE_STAGED_VERSIONED(FPhiMasks_001, "FPHIMASK", 1, //! mask for phis + femtotwotrackresonances::Mask); +using FPhiMasks = FPhiMasks_001; + +// table for kstars +DECLARE_SOA_TABLE_STAGED_VERSIONED(FKstar0s_001, "FKSTAR0", 1, //! femto k0star + o2::soa::Index<>, + femtobase::stored::CollisionId, + femtobase::stored::SignedPt, //! +1 for k0star and -1 for k0starbar + femtobase::stored::Eta, + femtobase::stored::Phi, + femtobase::stored::Mass, + femtotwotrackresonances::PosDauId, + femtotwotrackresonances::NegDauId, + femtobase::dynamic::Sign, + femtobase::dynamic::Pt, + femtobase::dynamic::P, + femtobase::dynamic::Px, + femtobase::dynamic::Py, + femtobase::dynamic::Pz, + femtobase::dynamic::Theta); +using FKstar0s = FKstar0s_001; +DECLARE_SOA_TABLE_STAGED_VERSIONED(FKstar0Masks_001, "FKSTAR0MASK", 1, //! k0star masks + femtotwotrackresonances::Mask); +using FKstar0Masks = FKstar0Masks_001; + +DECLARE_SOA_TABLE_STAGED_VERSIONED(FRho0s_001, "FRHO0", 1, //! femto rho0s + o2::soa::Index<>, + femtobase::stored::CollisionId, + femtobase::stored::Pt, + femtobase::stored::Eta, + femtobase::stored::Phi, + femtobase::stored::Mass, + femtotwotrackresonances::PosDauId, + femtotwotrackresonances::NegDauId, + femtobase::dynamic::P, + femtobase::dynamic::Px, + femtobase::dynamic::Py, + femtobase::dynamic::Pz, + femtobase::dynamic::Theta); +using FRho0s = FRho0s_001; +DECLARE_SOA_TABLE_STAGED_VERSIONED(FRho0Masks_001, "FRHO0MASK", 1, //! rho0s masks + femtotwotrackresonances::Mask); +using FRho0Masks = FRho0Masks_001; + +namespace femtov0s +{ +// columns for bit masks +DECLARE_SOA_COLUMN(Mask, mask, femtodatatypes::V0MaskType); //! Bitmask for v0 selections + +// columns for debug information +DECLARE_SOA_COLUMN(MassLambda, massLambda, float); //! Mass of Lambda +DECLARE_SOA_COLUMN(MassAntiLambda, massAntiLambda, float); //! Mass of AntiLambda +DECLARE_SOA_COLUMN(MassK0short, massK0short, float); //! Mass of K0short +DECLARE_SOA_COLUMN(CosPa, cosPa, float); //! Lambda daughter DCA at decay vertex +DECLARE_SOA_COLUMN(DauDca, dauDca, float); //! Lambda daughter DCA at decay vertex +DECLARE_SOA_COLUMN(TransRadius, transRadius, float); //! Lambda transvers radius +DECLARE_SOA_COLUMN(DecayVtxX, decayVtxX, float); //! x coordinate of Lambda decay vertex +DECLARE_SOA_COLUMN(DecayVtxY, decayVtxY, float); //! y coordinate of Lambda decay vertex +DECLARE_SOA_COLUMN(DecayVtxZ, decayVtxZ, float); //! z coordinate of Lambda decay vertex +DECLARE_SOA_DYNAMIC_COLUMN(DecayVtx, decayVtx, //! distance of decay vertex from nominal interaction point + [](float vtxX, float vtxY, float vtxZ) -> float { + return std::hypot(vtxX, vtxY, vtxZ); + }); + +// id columns for Lambda daughter tracks +DECLARE_SOA_INDEX_COLUMN_FULL(PosDau, posDau, int32_t, FTracks, "_PosDau"); //! index column for positive daughter track +DECLARE_SOA_INDEX_COLUMN_FULL(NegDau, negDau, int32_t, FTracks, "_NegDau"); //! index column for negative daughter track + +} // namespace femtov0s + +// table for basic lambda information +DECLARE_SOA_TABLE_STAGED_VERSIONED(FLambdas_001, "FLAMBDA", 1, //! femto lambdas + o2::soa::Index<>, + femtobase::stored::CollisionId, // use sign to differentiate between lambda (+1) and antilambda (-1) + femtobase::stored::SignedPt, + femtobase::stored::Eta, + femtobase::stored::Phi, + femtobase::stored::Mass, // mass of the lambda/antilambda depending on the sign of the pt + femtov0s::PosDauId, + femtov0s::NegDauId, + femtobase::dynamic::Sign, + femtobase::dynamic::Pt, + femtobase::dynamic::P, + femtobase::dynamic::Px, + femtobase::dynamic::Py, + femtobase::dynamic::Pz, + femtobase::dynamic::Theta); +using FLambdas = FLambdas_001; + +DECLARE_SOA_TABLE_STAGED_VERSIONED(FLambdaMasks_001, "FLAMBDAMASK", 1, //! lambda masks + femtov0s::Mask); +using FLambdaMasks = FLambdaMasks_001; + +DECLARE_SOA_TABLE_STAGED_VERSIONED(FLambdaExtras_001, "FLAMBDAEXTRA", 1, //! lambda extra information + femtobase::stored::MassAnti, // put mass of antiparticle, i.e. antilambda mass for lambdas and vice versa + femtov0s::MassK0short, + femtov0s::CosPa, + femtov0s::DauDca, + femtov0s::TransRadius, + femtov0s::DecayVtxX, + femtov0s::DecayVtxY, + femtov0s::DecayVtxZ, + femtov0s::DecayVtx); + +using FLambdaExtras = FLambdaExtras_001; + +// table for basic k0short information +DECLARE_SOA_TABLE_STAGED_VERSIONED(FK0shorts_001, "FK0SHORT", 1, //! femto k0shorts + o2::soa::Index<>, + femtobase::stored::CollisionId, + femtobase::stored::Pt, + femtobase::stored::Eta, + femtobase::stored::Phi, + femtobase::stored::Mass, + femtov0s::PosDauId, + femtov0s::NegDauId, + femtobase::dynamic::P, + femtobase::dynamic::Px, + femtobase::dynamic::Py, + femtobase::dynamic::Pz, + femtobase::dynamic::Theta); +using FK0shorts = FK0shorts_001; + +DECLARE_SOA_TABLE_STAGED_VERSIONED(FK0shortMasks_001, "FK0SHORTMASK", 1, //! k0short masks + femtov0s::Mask); +using FK0shortMasks = FK0shortMasks_001; + +DECLARE_SOA_TABLE_STAGED_VERSIONED(FK0shortExtras_001, "FK0SHORTEXTRA", 1, //! k0short extra information + femtov0s::MassLambda, + femtov0s::MassAntiLambda, + femtov0s::CosPa, + femtov0s::DauDca, + femtov0s::TransRadius, + femtov0s::DecayVtxX, + femtov0s::DecayVtxY, + femtov0s::DecayVtxZ, + femtov0s::DecayVtx); + +using FK0shortExtras = FK0shortExtras_001; + +namespace femtocascades +{ +// columns for cascade bit masks +DECLARE_SOA_COLUMN(Mask, mask, femtodatatypes::CascadeMaskType); //! Bitmask for cascade selections + +// columns for cascad debug information +DECLARE_SOA_COLUMN(MassXi, massXi, float); //! Mass of xi +DECLARE_SOA_COLUMN(MassOmega, massOmega, float); //! Mass of omega +DECLARE_SOA_COLUMN(CascadeCosPa, cascadeCosPa, float); //! cosine of the poiting angle at decay vertex +DECLARE_SOA_COLUMN(CascadeDauDca, cascadeDauDca, float); //! Lambda daughter DCA at decay vertex +DECLARE_SOA_COLUMN(CascadeTransRadius, cascadeTransRadius, float); //! Lambda transvers radius +DECLARE_SOA_COLUMN(LambdaCosPa, lambdaCosPa, float); //! cosine of the poiting angle at decay vertex +DECLARE_SOA_COLUMN(LambdaDauDca, lambdaDauDca, float); //! Lambda daughter DCA at decay vertex +DECLARE_SOA_COLUMN(LambdaTransRadius, lambdaTransRadius, float); //! Lambda transvers radius +DECLARE_SOA_COLUMN(LambdaDcaToPv, lambdaDcaToPv, float); //! Lambda transvers radius + +// id columns for bachelor +// following same style as strangeness tables were we do not store the id of the lambda, but its daughters +DECLARE_SOA_INDEX_COLUMN_FULL(Bachelor, bachelor, int32_t, FTracks, "_Bachelor"); //! bachelor id + +} // namespace femtocascades + +DECLARE_SOA_TABLE_STAGED_VERSIONED(FXis_001, "FXI", 1, //! femto xis + o2::soa::Index<>, + femtobase::stored::CollisionId, + femtobase::stored::SignedPt, + femtobase::stored::Eta, + femtobase::stored::Phi, + femtobase::stored::Mass, + femtocascades::BachelorId, + femtov0s::PosDauId, + femtov0s::NegDauId, + femtobase::dynamic::Sign, + femtobase::dynamic::Pt, + femtobase::dynamic::P, + femtobase::dynamic::Px, + femtobase::dynamic::Py, + femtobase::dynamic::Pz, + femtobase::dynamic::Theta); +using FXis = FXis_001; + +DECLARE_SOA_TABLE_STAGED_VERSIONED(FXiMasks_001, "FXIMASK", 1, //! xi masks + femtocascades::Mask); +using FXiMasks = FXiMasks_001; + +DECLARE_SOA_TABLE_STAGED_VERSIONED(FXiExtras_001, "FXIEXTRA", 1, //! xi extra information + femtocascades::MassOmega, + femtocascades::CascadeCosPa, + femtocascades::CascadeDauDca, + femtocascades::CascadeTransRadius, + femtocascades::LambdaCosPa, + femtocascades::LambdaDauDca, + femtocascades::LambdaTransRadius, + femtocascades::LambdaDcaToPv); +using FXiExtras = FXiExtras_001; + +DECLARE_SOA_TABLE_STAGED_VERSIONED(FOmegas_001, "FOMEGA", 1, //! femto omegas + o2::soa::Index<>, + femtobase::stored::CollisionId, + femtobase::stored::SignedPt, + femtobase::stored::Eta, + femtobase::stored::Phi, + femtobase::stored::Mass, + femtocascades::BachelorId, + femtov0s::PosDauId, + femtov0s::NegDauId, + femtobase::dynamic::Sign, + femtobase::dynamic::Pt, + femtobase::dynamic::P, + femtobase::dynamic::Px, + femtobase::dynamic::Py, + femtobase::dynamic::Pz, + femtobase::dynamic::Theta); +using FOmegas = FOmegas_001; + +DECLARE_SOA_TABLE_STAGED_VERSIONED(FOmegaMasks_001, "FOMEGAMASK", 1, //! omega masks + femtocascades::Mask); +using FOmegaMasks = FOmegaMasks_001; + +DECLARE_SOA_TABLE_STAGED_VERSIONED(FOmegaExtras_001, "FOMEGAEXTRA", 1, //! omega extra information + femtocascades::MassXi, + femtocascades::CascadeCosPa, + femtocascades::CascadeDauDca, + femtocascades::CascadeTransRadius, + femtocascades::LambdaCosPa, + femtocascades::LambdaDauDca, + femtocascades::LambdaTransRadius, + femtocascades::LambdaDcaToPv); +using FOmegaExtras = FOmegaExtras_001; + +} // namespace o2::aod +#endif // PWGCF_FEMTO_DATAMODEL_FEMTOTABLES_H_ diff --git a/PWGCF/Femto/TableProducer/CMakeLists.txt b/PWGCF/Femto/TableProducer/CMakeLists.txt index 4c6576278a5..743fdc67534 100644 --- a/PWGCF/Femto/TableProducer/CMakeLists.txt +++ b/PWGCF/Femto/TableProducer/CMakeLists.txt @@ -9,6 +9,11 @@ # granted to it by virtue of its status as an Intergovernmental Organization # or submit itself to any jurisdiction. +o2physics_add_dpl_workflow(femto-producer + SOURCES femtoProducer.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(pinucleifemto SOURCES PiNucleiFemto.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils diff --git a/PWGCF/Femto/TableProducer/femtoProducer.cxx b/PWGCF/Femto/TableProducer/femtoProducer.cxx new file mode 100644 index 00000000000..0f69056e11d --- /dev/null +++ b/PWGCF/Femto/TableProducer/femtoProducer.cxx @@ -0,0 +1,300 @@ +// 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 femtoProducer.cxx +/// \brief Tasks that produces the all femto tables +/// \author Anton Riedel, TU München, anton.riedel@tum.de + +#include "PWGCF/Femto/Core/cascadeBuilder.h" +#include "PWGCF/Femto/Core/collisionBuilder.h" +#include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/Core/trackBuilder.h" +#include "PWGCF/Femto/Core/twoTrackResonanceBuilder.h" +#include "PWGCF/Femto/Core/v0Builder.h" +#include "PWGLF/DataModel/LFStrangenessTables.h" + +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponseITS.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include "CCDB/BasicCCDBManager.h" +#include "DataFormatsParameters/GRPMagField.h" +#include "Framework/AnalysisDataModel.h" +#include "Framework/AnalysisHelpers.h" +#include "Framework/AnalysisTask.h" +#include "Framework/Configurable.h" +#include "Framework/Expressions.h" +#include "Framework/InitContext.h" +#include "Framework/runDataProcessing.h" + +#include "fairlogger/Logger.h" + +#include +#include +#include +#include + +using namespace o2::aod; +using namespace o2::soa; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::analysis::femto; + +namespace o2::analysis::femto +{ +namespace consumeddata +{ +using Run3PpCollisions = soa::Join; +// FIXME: sometimes people want to run analyis even though centrality calibration is not available yet +// using Run3PpWithoutCentCollisions = soa::Join; + +using Run3FullPidTracks = + soa::Join; + +using Run3PpVzeros = V0Datas; + +using Run3PpCascades = CascDatas; + +} // namespace consumeddata +} // namespace o2::analysis::femto + +struct FemtoProducer { + + // configurables + struct : ConfigurableGroup { + std::string prefix = std::string("General"); + Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "URL to ccdb"}; + Configurable grpPath{"grpPath", "GLO/Config/GRPMagField", "Path to GRP object (Run3 -> GLO/Config/GRPMagField/Run2 -> GLO/GRP/GRP"}; + } ConfOptions; + + // collision builder + collisionbuilder::CollisionBuilderProducts collisionBuilderProducts; + collisionbuilder::ConfCollisionTables confCollisionTables; + collisionbuilder::ConfCollisionFilter confCollisionFilter; + collisionbuilder::ConfCollisionFlags confCollisionFlags; + collisionbuilder::CollisionBuilder collisionBuilder; + + // track builder + trackbuilder::TrackBuilderProducts trackBuilderProducts; + trackbuilder::ConfTrackTables confTrackTables; + trackbuilder::TrackBuilder trackBuilder; + trackbuilder::ConfTrackBits confTrackBits; + trackbuilder::ConfTrackFilters confTrackFilters; + + // v0 builders + v0builder::V0BuilderProducts v0builderProducts; + v0builder::ConfV0Tables confV0Tables; + v0builder::ConfV0Filters confV0Filters; + v0builder::ConfK0shortBits confK0shortBits; + v0builder::V0Builder k0shortBuilder; + v0builder::ConfLambdaBits confLambdaBits; + v0builder::V0Builder lambdaBuilder; + v0builder::V0Builder antilambdaBuilder; + + // cascade builder + cascadebuilder::CascadeBuilderProducts cascadeBuilderProducts; + cascadebuilder::ConfCascadeTables confCascadeTables; + cascadebuilder::ConfCascadeFilters confCascadeFilters; + cascadebuilder::ConfXiBits confXiBits; + cascadebuilder::CascadeBuilder xiBuilder; + cascadebuilder::ConfOmegaBits confOmegaBits; + cascadebuilder::CascadeBuilder omegaBuilder; + + // resonance daughter filters and partitions + twotrackresonancebuilder::ConfTwoTrackResonanceDaughterFilters confResonanceDaughterFilters; + // caching and preslicing + SliceCache cache; + Preslice perColTracks = o2::aod::track::collisionId; + Partition partitionPositiveDaughters = + (track::signed1Pt > 0.f) && + (track::pt > confResonanceDaughterFilters.ptMin && track::pt < confResonanceDaughterFilters.ptMax) && + (track::eta > confResonanceDaughterFilters.etaMin && track::eta < confResonanceDaughterFilters.etaMax) && + (track::phi > confResonanceDaughterFilters.phiMin && track::phi < confResonanceDaughterFilters.phiMax); + Partition partitionNegativeDaughters = + (track::signed1Pt < 0.f) && + (track::pt > confResonanceDaughterFilters.ptMin && track::pt < confResonanceDaughterFilters.ptMax) && + (track::eta > confResonanceDaughterFilters.etaMin && track::eta < confResonanceDaughterFilters.etaMax) && + (track::phi > confResonanceDaughterFilters.phiMin && track::phi < confResonanceDaughterFilters.phiMax); + + // resonance builders + twotrackresonancebuilder::TwoTrackResonanceBuilderProducts twoTrackResonanceBuilderProducts; + twotrackresonancebuilder::ConfTwoTrackResonanceTables confTwoTrackResonanceTables; + twotrackresonancebuilder::ConfRhoFilters confRhoFilters; + twotrackresonancebuilder::ConfRho0Bits confRho0Bits; + twotrackresonancebuilder::TwoTrackResonanceBuilder rho0Builder; + twotrackresonancebuilder::ConfPhiFilters confPhiFilters; + twotrackresonancebuilder::ConfPhiBits confPhiBits; + twotrackresonancebuilder::TwoTrackResonanceBuilder phiBuilder; + twotrackresonancebuilder::ConfKstarFilters confKstarFilters; + twotrackresonancebuilder::ConfKstar0Bits confKstar0Bits; + twotrackresonancebuilder::TwoTrackResonanceBuilder kstar0Builder; + twotrackresonancebuilder::TwoTrackResonanceBuilder kstar0barBuilder; + + // histogramming + // add histograms in next iteration + // HistogramRegistry hRegistry{"FemtoProducer", {}, OutputObjHandlingPolicy::AnalysisObject}; + + // data members + int runNumber = -1; + float magField = 0.f; + Service ccdb; /// Accessing the CCDB + std::unordered_map indexMapTracks; // for mapping tracks to lambdas, cascades and resonances + + void initFromCcdb(o2::aod::BCsWithTimestamps::iterator const& bc) + { + if (runNumber == bc.runNumber()) + return; + auto timestamp = bc.timestamp(); + static o2::parameters::GRPMagField* grpo = nullptr; + grpo = ccdb->getForTimeStamp(ConfOptions.grpPath.value, timestamp); + if (grpo == nullptr) { + LOGF(fatal, "GRP object not found for timestamp %llu", timestamp); + return; + } + magField = 0.1 * grpo->getNominalL3Field(); // get magnetic field in tesla + runNumber = bc.runNumber(); + }; + + void init(InitContext& context) + { + // init ccdb + ccdb->setURL(ConfOptions.ccdbUrl.value); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + int64_t now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + ccdb->setCreatedNotAfter(now); + + // collision selection + collisionBuilder.init(confCollisionFilter, confCollisionFlags, confCollisionTables, context); + + // configure track builder + trackBuilder.init(confTrackBits, confTrackFilters, confTrackTables, context); + + // configure v0 builder + k0shortBuilder.init(confK0shortBits, confV0Filters, confV0Tables, context); + lambdaBuilder.init(confLambdaBits, confV0Filters, confV0Tables, context); + antilambdaBuilder.init(confLambdaBits, confV0Filters, confV0Tables, context); + + // cascade selections + xiBuilder.init(confXiBits, confCascadeFilters, confCascadeTables, context); + omegaBuilder.init(confOmegaBits, confCascadeFilters, confCascadeTables, context); + + // configure resonance selections + rho0Builder.init(confRho0Bits, confRhoFilters, confResonanceDaughterFilters, confTwoTrackResonanceTables, context); + phiBuilder.init(confPhiBits, confPhiFilters, confResonanceDaughterFilters, confTwoTrackResonanceTables, context); + kstar0Builder.init(confKstar0Bits, confKstarFilters, confResonanceDaughterFilters, confTwoTrackResonanceTables, context); + kstar0barBuilder.init(confKstar0Bits, confKstarFilters, confResonanceDaughterFilters, confTwoTrackResonanceTables, context); + + if ((xiBuilder.fillAnyTable() || omegaBuilder.fillAnyTable()) && !doprocessTracksV0sCascadesRun3pp) { + LOG(fatal) << "At least one cascade tabel is enabled, but wrong process function is enabled. Breaking..."; + } + if ((lambdaBuilder.fillAnyTable() || antilambdaBuilder.fillAnyTable() || k0shortBuilder.fillAnyTable()) && (!doprocessTracksV0sCascadesRun3pp && !doprocessTracksV0sRun3pp)) { + LOG(info) << "At least one v0 tabel is enbaled, but wrong process function is enabled. Breaking..."; + } + } + + // Core implementation, parameterized by builders to call + template + void processTracks(T1 const& col, T2 const& /* bcs*/, T3 const& tracks, T4 const& tracksWithItsPid) + { + initFromCcdb(col.template bc_as()); + collisionBuilder.buildCollision(col, tracks, magField); + if (!collisionBuilder.checkCuts(col)) { + return; + } + collisionBuilder.fillCollision(collisionBuilderProducts, col); + + // tracks + indexMapTracks.clear(); + trackBuilder.fillTracks(tracksWithItsPid, trackBuilderProducts, collisionBuilderProducts, indexMapTracks); + + // resonances + auto groupPositiveTracks = partitionPositiveDaughters->sliceByCached(o2::aod::track::collisionId, col.globalIndex(), cache); + auto groupNegativeTracks = partitionNegativeDaughters->sliceByCached(o2::aod::track::collisionId, col.globalIndex(), cache); + rho0Builder.fillResonances(collisionBuilderProducts, trackBuilderProducts, twoTrackResonanceBuilderProducts, groupPositiveTracks, groupNegativeTracks, trackBuilder, indexMapTracks); + phiBuilder.fillResonances(collisionBuilderProducts, trackBuilderProducts, twoTrackResonanceBuilderProducts, groupPositiveTracks, groupNegativeTracks, trackBuilder, indexMapTracks); + kstar0Builder.fillResonances(collisionBuilderProducts, trackBuilderProducts, twoTrackResonanceBuilderProducts, groupPositiveTracks, groupNegativeTracks, trackBuilder, indexMapTracks); + kstar0barBuilder.fillResonances(collisionBuilderProducts, trackBuilderProducts, twoTrackResonanceBuilderProducts, groupPositiveTracks, groupNegativeTracks, trackBuilder, indexMapTracks); + } + + // add v0s + template + void processTracksV0s(T1 const& col, T2 const& bcs, T3 const& tracks, T4 const& tracksWithItsPid, T5 const& v0s) + { + processTracks(col, bcs, tracks, tracksWithItsPid); + lambdaBuilder.fillV0s(collisionBuilderProducts, trackBuilderProducts, v0builderProducts, v0s, tracks, trackBuilder, indexMapTracks); + antilambdaBuilder.fillV0s(collisionBuilderProducts, trackBuilderProducts, v0builderProducts, v0s, tracks, trackBuilder, indexMapTracks); + k0shortBuilder.fillV0s(collisionBuilderProducts, trackBuilderProducts, v0builderProducts, v0s, tracks, trackBuilder, indexMapTracks); + } + + // add cascades + template + void processTracksV0sCascades(T1 const& col, T2 const& bcs, T3 const& tracks, T4 const& tracksWithItsPid, T5 const& v0s, T6 const& cascades) + { + processTracksV0s(col, bcs, tracks, tracksWithItsPid, v0s); + xiBuilder.fillCascades(collisionBuilderProducts, trackBuilderProducts, cascadeBuilderProducts, + cascades, tracks, col, trackBuilder, indexMapTracks); + omegaBuilder.fillCascades(collisionBuilderProducts, trackBuilderProducts, cascadeBuilderProducts, + cascades, tracks, col, trackBuilder, indexMapTracks); + } + + // proccess functions + void processTracksRun3pp(consumeddata::Run3PpCollisions::iterator const& col, + BCsWithTimestamps const& bcs, + consumeddata::Run3FullPidTracks const& tracks) + { + // its pid information is generated dynamically, so we need to add it here + auto tracksWithItsPid = o2::soa::Attach(tracks); + processTracks(col, bcs, tracks, tracksWithItsPid); + } + PROCESS_SWITCH(FemtoProducer, processTracksRun3pp, "Process tracks", true); + + // process tracks and v0s + void processTracksV0sRun3pp(consumeddata::Run3PpCollisions::iterator const& col, + BCsWithTimestamps const& bcs, + consumeddata::Run3FullPidTracks const& tracks, + consumeddata::Run3PpVzeros const& v0s) + { + // its pid information is generated dynamically, so we need to add it here + auto tracksWithItsPid = o2::soa::Attach(tracks); + processTracksV0s(col, bcs, tracks, tracksWithItsPid, v0s); + }; + PROCESS_SWITCH(FemtoProducer, processTracksV0sRun3pp, "Process tracks and v0s", false); + + // process tracks, v0s and casacades + void processTracksV0sCascadesRun3pp(consumeddata::Run3PpCollisions::iterator const& col, + BCsWithTimestamps const& bcs, + consumeddata::Run3FullPidTracks const& tracks, + consumeddata::Run3PpVzeros const& v0s, + consumeddata::Run3PpCascades const& cascades) + { + // its pid information is generated dynamically, so we need to add it here + auto tracksWithItsPid = o2::soa::Attach(tracks); + processTracksV0sCascades(col, bcs, tracks, tracksWithItsPid, v0s, cascades); + } + PROCESS_SWITCH(FemtoProducer, processTracksV0sCascadesRun3pp, "Provide Tracks, V0s and Cascades for Run3", false); +}; + +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..eb16171c62b --- /dev/null +++ b/PWGCF/Femto/Tasks/CMakeLists.txt @@ -0,0 +1,40 @@ +# 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. + +o2physics_add_dpl_workflow(femto-track-qa + SOURCES femtoTrackQa.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(femto-twotrackresonance-qa + SOURCES femtoTwotrackresonanceQa.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(femto-v0-qa + SOURCES femtoV0Qa.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(femto-cascade-qa + SOURCES femtoCascadeQa.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +# o2physics_add_dpl_workflow(femtounited-pair-track-track +# SOURCES femtounitedPairTrackTrack.cxx +# PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore +# COMPONENT_NAME Analysis) +# +# o2physics_add_dpl_workflow(femtounited-pair-track-v0 +# SOURCES femtounitedPairTrackV0.cxx +# PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore +# COMPONENT_NAME Analysis) diff --git a/PWGCF/Femto/Tasks/femtoCascadeQa.cxx b/PWGCF/Femto/Tasks/femtoCascadeQa.cxx new file mode 100644 index 00000000000..b447d31633b --- /dev/null +++ b/PWGCF/Femto/Tasks/femtoCascadeQa.cxx @@ -0,0 +1,161 @@ +// Copyright 2019-2025 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file femtoCascadeQa.cxx +/// \brief Tasks for Qa of cascades +/// \author Anton Riedel, TU München, anton.riedel@cern.ch + +#include "PWGCF/Femto/Core/cascadeBuilder.h" +#include "PWGCF/Femto/Core/cascadeHistManager.h" +#include "PWGCF/Femto/Core/collisionBuilder.h" +#include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/Core/partitions.h" +#include "PWGCF/Femto/Core/trackHistManager.h" +#include "PWGCF/Femto/DataModel/FemtoTables.h" + +#include "Framework/ASoA.h" +#include "Framework/AnalysisHelpers.h" +#include "Framework/AnalysisTask.h" +#include "Framework/Configurable.h" +#include "Framework/Expressions.h" +#include "Framework/HistogramRegistry.h" +#include "Framework/InitContext.h" +#include "Framework/OutputObjHeader.h" +#include "Framework/runDataProcessing.h" + +#include +#include +#include + +using namespace o2; +using namespace o2::aod; +using namespace o2::soa; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::analysis::femto; + +struct FemtoCascadeQa { + + // setup tables + using Collisions = FCols; + using Collision = Collisions::iterator; + + using FilteredCollisions = o2::soa::Filtered; + using FilteredCollision = FilteredCollisions::iterator; + + using Xis = o2::soa::Join; + using Omegas = o2::soa::Join; + using Tracks = o2::soa::Join; + + SliceCache cache; + + // setup collisions + colhistmanager::CollisionHistManager colHistManager; + colhistmanager::ConfCollisionBinning confCollisionBinning; + collisionbuilder::ConfCollisionFilter collisionSelection; + Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); + + // setup for xis + cascadebuilder::ConfXiSelection confXiSelection; + Partition xiPartition = MAKE_CASCADE_PARTITION(confXiSelection); + Preslice preColXis = aod::femtobase::stored::collisionId; + + cascadehistmanager::ConfXiBinning confXiBinning; + cascadehistmanager::ConfXiQaBinning confXiQaBinning; + cascadehistmanager::CascadeHistManager< + cascadehistmanager::PrefixXiQa, + trackhistmanager::PrefixCascadeBachelorQa, + trackhistmanager::PrefixV0PosDaughterQa, + trackhistmanager::PrefixV0NegDaughterQa, + modes::Mode::kAnalysis_Qa, + modes::Cascade::kXi> + xiHistManager; + + // setup for omegas + cascadebuilder::ConfOmegaSelection confOmegaSelection; + Partition omegaPartition = MAKE_CASCADE_PARTITION(confOmegaSelection); + Preslice preColOmegas = aod::femtobase::stored::collisionId; + + cascadehistmanager::ConfOmegaBinning confOmegaBinning; + cascadehistmanager::ConfOmegaQaBinning confOmegaQaBinning; + cascadehistmanager::CascadeHistManager< + cascadehistmanager::PrefixOmegaQa, + trackhistmanager::PrefixCascadeBachelorQa, + trackhistmanager::PrefixV0PosDaughterQa, + trackhistmanager::PrefixV0NegDaughterQa, + modes::Mode::kAnalysis_Qa, + modes::Cascade::kOmega> + omegaHistManager; + + // setup for daughters/bachelor + trackhistmanager::ConfCascadePosDauBinning confPosDaughterBinning; + trackhistmanager::ConfCascadePosDauQaBinning confPosDaughterQaBinning; + trackhistmanager::ConfCascadeNegDauBinning confNegDaughterBinning; + trackhistmanager::ConfCascadeNegDauQaBinning confNegDaughterQaBinning; + trackhistmanager::ConfCascadeBachelorBinning confBachelorBinning; + trackhistmanager::ConfCascadeBachelorQaBinning confBachelorQaBinning; + + HistogramRegistry hRegistry{"FemtoCascadeQA", {}, OutputObjHandlingPolicy::AnalysisObject}; + + void init(InitContext&) + { + // create a map for histogram specs + auto colHistSpec = colhistmanager::makeColHistSpecMap(confCollisionBinning); + colHistManager.init(&hRegistry, colHistSpec); + + auto bachelorHistSpec = trackhistmanager::makeTrackQaHistSpecMap(confBachelorBinning, confBachelorQaBinning); + auto posDaughterHistSpec = trackhistmanager::makeTrackQaHistSpecMap(confPosDaughterBinning, confPosDaughterQaBinning); + auto negDaughterHistSpec = trackhistmanager::makeTrackQaHistSpecMap(confNegDaughterBinning, confNegDaughterQaBinning); + + if ((doprocessXis + doprocessOmegas) > 1) { + LOG(fatal) << "Only one process can be activated"; + } + + if (doprocessXis) { + auto xiHistSpec = cascadehistmanager::makeCascadeQaHistSpecMap(confXiBinning, confXiQaBinning); + xiHistManager.init(&hRegistry, xiHistSpec, bachelorHistSpec, posDaughterHistSpec, negDaughterHistSpec); + } + + if (doprocessOmegas) { + auto omegaHistSpec = cascadehistmanager::makeCascadeQaHistSpecMap(confOmegaBinning, confOmegaQaBinning); + omegaHistManager.init(&hRegistry, omegaHistSpec, bachelorHistSpec, posDaughterHistSpec, negDaughterHistSpec); + } + }; + + void processXis(FilteredCollision const& col, Xis const& /*xis*/, Tracks const& tracks) + { + colHistManager.fill(col); + auto xiSlice = xiPartition->sliceByCached(femtobase::stored::collisionId, col.globalIndex(), cache); + for (auto const& xi : xiSlice) { + xiHistManager.fill(xi, tracks); + } + } + PROCESS_SWITCH(FemtoCascadeQa, processXis, "Process Xis", true); + + void processOmegas(FilteredCollision const& col, Omegas const& /*omegas*/, Tracks const& tracks) + { + colHistManager.fill(col); + auto omegaSlice = omegaPartition->sliceByCached(femtobase::stored::collisionId, col.globalIndex(), cache); + for (auto const& omega : omegaSlice) { + omegaHistManager.fill(omega, tracks); + } + } + PROCESS_SWITCH(FemtoCascadeQa, processOmegas, "Process Omegas", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + WorkflowSpec workflow{ + adaptAnalysisTask(cfgc), + }; + return workflow; +} diff --git a/PWGCF/Femto/Tasks/femtoTrackQa.cxx b/PWGCF/Femto/Tasks/femtoTrackQa.cxx new file mode 100644 index 00000000000..4963b54f9dc --- /dev/null +++ b/PWGCF/Femto/Tasks/femtoTrackQa.cxx @@ -0,0 +1,100 @@ +// 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 femtoTrackQa.cxx +/// \brief QA task for tracks +/// \author Anton Riedel, TU München, anton.riedel@cern.ch + +#include "PWGCF/Femto/Core/collisionBuilder.h" +#include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/Core/partitions.h" +#include "PWGCF/Femto/Core/trackBuilder.h" +#include "PWGCF/Femto/Core/trackHistManager.h" +#include "PWGCF/Femto/DataModel/FemtoTables.h" + +#include "Framework/ASoA.h" +#include "Framework/AnalysisHelpers.h" +#include "Framework/AnalysisTask.h" +#include "Framework/Expressions.h" +#include "Framework/HistogramRegistry.h" +#include "Framework/InitContext.h" +#include "Framework/OutputObjHeader.h" +#include "Framework/runDataProcessing.h" + +#include +#include + +using namespace o2; +using namespace o2::aod; +using namespace o2::soa; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::analysis::femto; + +struct FemtoTrackQa { + + // setup tables + using Collisions = FCols; + using Collision = Collisions::iterator; + + using FilteredCollisions = o2::soa::Filtered; + using FilteredCollision = FilteredCollisions::iterator; + + using Tracks = o2::soa::Join; + + SliceCache cache; + + // setup collisions + collisionbuilder::ConfCollisionFilter collisionSelection; + Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); + colhistmanager::ConfCollisionBinning confCollisionBinning; + colhistmanager::CollisionHistManager colHistManager; + + // setup tracks + trackbuilder::ConfTrackSelection1 trackSelections; + trackhistmanager::ConfTrackBinning1 confTrackBinning; + trackhistmanager::ConfTrackQaBinning1 confTrackQaBinning; + trackhistmanager::TrackHistManager trackHistManager; + + Partition trackPartition = MAKE_TRACK_PARTITION(trackSelections); + Preslice perColReco = aod::femtobase::stored::collisionId; + + HistogramRegistry hRegistry{"FemtoTrackQA", {}, OutputObjHandlingPolicy::AnalysisObject}; + + void init(InitContext&) + { + // create a map for histogram specs + auto colHistSpec = colhistmanager::makeColHistSpecMap(confCollisionBinning); + colHistManager.init(&hRegistry, colHistSpec); + auto trackHistSpec = trackhistmanager::makeTrackQaHistSpecMap(confTrackBinning, confTrackQaBinning); + trackHistManager.init(&hRegistry, trackHistSpec); + }; + + void process(FilteredCollision const& col, Tracks const& /*tracks*/) + { + colHistManager.fill(col); + auto trackSlice = trackPartition->sliceByCached(femtobase::stored::collisionId, col.globalIndex(), cache); + for (auto const& track : trackSlice) { + trackHistManager.fill(track); + // asdf + } + } +}; + +WorkflowSpec + defineDataProcessing(ConfigContext const& cfgc) +{ + WorkflowSpec workflow{ + adaptAnalysisTask(cfgc), + }; + return workflow; +} diff --git a/PWGCF/Femto/Tasks/femtoTwotrackresonanceQa.cxx b/PWGCF/Femto/Tasks/femtoTwotrackresonanceQa.cxx new file mode 100644 index 00000000000..3ea287908d5 --- /dev/null +++ b/PWGCF/Femto/Tasks/femtoTwotrackresonanceQa.cxx @@ -0,0 +1,181 @@ +// 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 femtoTwotrackresonanceQa.cxx +/// \brief Qa task for two track resonances +/// \author Anton Riedel, TU München, anton.riedel@cern.ch + +#include "PWGCF/Femto/Core/collisionBuilder.h" +#include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/dataTypes.h" +#include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/Core/partitions.h" +#include "PWGCF/Femto/Core/trackHistManager.h" +#include "PWGCF/Femto/Core/twoTrackResonanceBuilder.h" +#include "PWGCF/Femto/Core/twoTrackResonanceHistManager.h" +#include "PWGCF/Femto/DataModel/FemtoTables.h" + +#include "Framework/ASoA.h" +#include "Framework/AnalysisTask.h" +#include "Framework/Configurable.h" +#include "Framework/Expressions.h" +#include "Framework/HistogramRegistry.h" +#include "Framework/runDataProcessing.h" + +#include +#include +#include + +using namespace o2; +using namespace o2::aod; +using namespace o2::soa; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::analysis::femto; + +struct FemtoTwotrackresonanceQa { + + // setup tables + using Collisions = FCols; + using Collision = Collisions::iterator; + + using FilteredCollisions = o2::soa::Filtered; + using FilteredCollision = FilteredCollisions::iterator; + + using Phis = o2::soa::Join; + using Rho0s = o2::soa::Join; + using Kstar0s = o2::soa::Join; + using Tracks = o2::soa::Join; + + SliceCache cache; + + // setup for collisions + colhistmanager::CollisionHistManager colHistManager; + colhistmanager::ConfCollisionBinning confCollisionBinning; + collisionbuilder::ConfCollisionFilter collisionSelection; + Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); + + // setup for phis + twotrackresonancebuilder::ConfPhiSelection confPhiSelection; + Partition phiPartition = MAKE_RESONANCE_0_PARTITON(confPhiSelection); + Preslice perColPhis = aod::femtobase::stored::collisionId; + + twotrackresonancehistmanager::ConfPhiBinning confPhiBinning; + twotrackresonancehistmanager::TwoTrackResonanceHistManager< + twotrackresonancehistmanager::PrefixPhi, + trackhistmanager::PrefixResonancePosDaughterQa, + trackhistmanager::PrefixResonanceNegDaughterQa, + modes::Mode::kAnalysis_Qa, + modes::TwoTrackResonance::kPhi> + phiHistManager; + + // setup for rho0s + twotrackresonancebuilder::ConfRho0Selection confRho0Selection; + Partition rho0Partition = MAKE_RESONANCE_0_PARTITON(confRho0Selection); + Preslice perColRhos = aod::femtobase::stored::collisionId; + + twotrackresonancehistmanager::ConfRho0Binning confRho0Binning; + twotrackresonancehistmanager::TwoTrackResonanceHistManager< + twotrackresonancehistmanager::PrefixRho, + trackhistmanager::PrefixResonancePosDaughterQa, + trackhistmanager::PrefixResonanceNegDaughterQa, + modes::Mode::kAnalysis_Qa, + modes::TwoTrackResonance::kRho0> + rho0HistManager; + + // setup for kstar0s + twotrackresonancebuilder::ConfKstar0Selection confKstar0Selection; + Partition kstar0Partition = MAKE_RESONANCE_1_PARTITON(confKstar0Selection); + Preslice perColKstars = aod::femtobase::stored::collisionId; + + twotrackresonancehistmanager::ConfKstar0Binning confKstar0Binning; + twotrackresonancehistmanager::TwoTrackResonanceHistManager< + twotrackresonancehistmanager::PrefixKstar, + trackhistmanager::PrefixResonancePosDaughterQa, + trackhistmanager::PrefixResonanceNegDaughterQa, + modes::Mode::kAnalysis_Qa, + modes::TwoTrackResonance::kKstar0> + kstar0HistManager; + + // setup for daughters + trackhistmanager::ConfResonancePosDauBinning confPosDaughterBinning; + trackhistmanager::ConfResonancePosDauQaBinning confPosDaughterQaBinning; + trackhistmanager::ConfResonanceNegDauBinning confNegDaughterBinning; + trackhistmanager::ConfResonanceNegDauQaBinning confNegDaughterQaBinning; + + HistogramRegistry hRegistry{"ResonanceQA", {}, OutputObjHandlingPolicy::AnalysisObject}; + + void init(InitContext&) + { + // create a map for histogram specs + auto colHistSpec = colhistmanager::makeColHistSpecMap(confCollisionBinning); + colHistManager.init(&hRegistry, colHistSpec); + + auto posDaughterHistSpec = trackhistmanager::makeTrackQaHistSpecMap(confPosDaughterBinning, confPosDaughterQaBinning); + auto negDaughterHistSpec = trackhistmanager::makeTrackQaHistSpecMap(confNegDaughterBinning, confNegDaughterQaBinning); + + if ((doprocessPhis + doprocessRho0s + doprocessKstar0s) > 1) { + LOG(fatal) << "Only one process can be activated"; + } + + if (doprocessPhis) { + auto phiHistSpec = twotrackresonancehistmanager::makeTwoTrackResonanceQaHistSpecMap(confPhiBinning); + phiHistManager.init(&hRegistry, phiHistSpec, posDaughterHistSpec, negDaughterHistSpec); + } + if (doprocessRho0s) { + auto rho0HistSpec = twotrackresonancehistmanager::makeTwoTrackResonanceQaHistSpecMap(confRho0Binning); + rho0HistManager.init(&hRegistry, rho0HistSpec, posDaughterHistSpec, negDaughterHistSpec); + } + + if (doprocessKstar0s) { + auto kstar0HistSpec = twotrackresonancehistmanager::makeTwoTrackResonanceQaHistSpecMap(confKstar0Binning); + kstar0HistManager.init(&hRegistry, kstar0HistSpec, posDaughterHistSpec, negDaughterHistSpec); + } + }; + + void processPhis(FilteredCollision const& col, Phis const& /*phis*/, Tracks const& tracks) + { + colHistManager.fill(col); + auto phiSlice = phiPartition->sliceByCached(femtobase::stored::collisionId, col.globalIndex(), cache); + for (auto const& phi : phiSlice) { + phiHistManager.fill(phi, tracks); + } + }; + PROCESS_SWITCH(FemtoTwotrackresonanceQa, processPhis, "Process Phis", true); + + void processRho0s(FilteredCollision const& col, Rho0s const& /*rho0s*/, Tracks const& tracks) + { + colHistManager.fill(col); + auto rho0Slice = rho0Partition->sliceByCached(femtobase::stored::collisionId, col.globalIndex(), cache); + for (auto const& rho0 : rho0Slice) { + rho0HistManager.fill(rho0, tracks); + } + }; + PROCESS_SWITCH(FemtoTwotrackresonanceQa, processRho0s, "Process Rho0s", false); + + void processKstar0s(FilteredCollision const& col, Kstar0s const& /*kstar0s*/, Tracks const& tracks) + { + colHistManager.fill(col); + auto kstar0Slice = kstar0Partition->sliceByCached(femtobase::stored::collisionId, col.globalIndex(), cache); + for (auto const& kstar0 : kstar0Slice) { + kstar0HistManager.fill(kstar0, tracks); + } + }; + PROCESS_SWITCH(FemtoTwotrackresonanceQa, processKstar0s, "Process Kstar0s", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + WorkflowSpec workflow{ + adaptAnalysisTask(cfgc), + }; + return workflow; +} diff --git a/PWGCF/Femto/Tasks/femtoV0Qa.cxx b/PWGCF/Femto/Tasks/femtoV0Qa.cxx new file mode 100644 index 00000000000..c7ae9a8d786 --- /dev/null +++ b/PWGCF/Femto/Tasks/femtoV0Qa.cxx @@ -0,0 +1,160 @@ +// 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 femtoV0Qa.cxx +/// \brief QA task for v0s +/// \author Anton Riedel, TU München, anton.riedel@cern.ch + +#include "PWGCF/Femto/Core/collisionBuilder.h" +#include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/Core/partitions.h" +#include "PWGCF/Femto/Core/trackHistManager.h" +#include "PWGCF/Femto/Core/v0Builder.h" +#include "PWGCF/Femto/Core/v0HistManager.h" +#include "PWGCF/Femto/DataModel/FemtoTables.h" + +#include "Framework/ASoA.h" +#include "Framework/AnalysisHelpers.h" +#include "Framework/AnalysisTask.h" +#include "Framework/Configurable.h" +#include "Framework/Expressions.h" +#include "Framework/HistogramRegistry.h" +#include "Framework/InitContext.h" +#include "Framework/OutputObjHeader.h" +#include "Framework/runDataProcessing.h" + +#include +#include +#include + +using namespace o2; +using namespace o2::aod; +using namespace o2::soa; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::analysis::femto; + +struct FemtoV0Qa { + + // setup for collisions + collisionbuilder::ConfCollisionFilter collisionSelection; + Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); + + colhistmanager::CollisionHistManager colHistManager; + colhistmanager::ConfCollisionBinning confCollisionBinning; + + // using Collisions = o2::soa::Join; + using Collisions = FCols; + using Collision = Collisions::iterator; + + using FilteredCollisions = o2::soa::Filtered; + using FilteredCollision = FilteredCollisions::iterator; + + using Lambdas = o2::soa::Join; + using K0shorts = o2::soa::Join; + using Tracks = o2::soa::Join; + + SliceCache cache; + + // setup for lambdas + v0builder::ConfLambdaSelection1 confLambdaSelection; + + Partition lambdaPartition = MAKE_LAMBDA_PARTITION(confLambdaSelection); + Preslice perColLambdas = aod::femtobase::stored::collisionId; + + v0histmanager::ConfLambdaBinning1 confLambdaBinning; + v0histmanager::ConfLambdaQaBinning1 confLambdaQaBinning; + v0histmanager::V0HistManager< + v0histmanager::PrefixLambdaQa, + trackhistmanager::PrefixV0PosDaughterQa, + trackhistmanager::PrefixV0NegDaughterQa, + modes::Mode::kAnalysis_Qa, + modes::V0::kLambda> + lambdaHistManager; + + // setup for k0shorts + v0builder::ConfK0shortSelection1 confK0shortSelection; + + Partition k0shortPartition = MAKE_K0SHORT_PARTITION(confK0shortSelection); + Preslice perColK0shorts = aod::femtobase::stored::collisionId; + + v0histmanager::ConfK0shortBinning1 confK0shortBinning; + v0histmanager::ConfK0shortQaBinning1 confK0shortQaBinning; + v0histmanager::V0HistManager< + v0histmanager::PrefixK0shortQa, + trackhistmanager::PrefixV0PosDaughterQa, + trackhistmanager::PrefixV0NegDaughterQa, + modes::Mode::kAnalysis_Qa, + modes::V0::kK0short> + k0shortHistManager; + + // setup for daughters + trackhistmanager::ConfV0PosDauBinning confV0PosDaughterBinning; + trackhistmanager::ConfV0PosDauQaBinning confV0PosDaughterQaBinning; + + trackhistmanager::ConfV0NegDauBinning confV0NegDaughterBinning; + trackhistmanager::ConfV0NegDauQaBinning confV0NegDaughterQaBinning; + + HistogramRegistry hRegistry{"FemtoV0Qa", {}, OutputObjHandlingPolicy::AnalysisObject}; + + void init(InitContext&) + { + // create a map for histogram specs + auto colHistSpec = colhistmanager::makeColHistSpecMap(confCollisionBinning); + colHistManager.init(&hRegistry, colHistSpec); + + auto posDaughterHistSpec = trackhistmanager::makeTrackQaHistSpecMap(confV0PosDaughterBinning, confV0PosDaughterQaBinning); + auto negDaughterHistSpec = trackhistmanager::makeTrackQaHistSpecMap(confV0NegDaughterBinning, confV0NegDaughterQaBinning); + + if ((doprocessK0short + doprocessLambda) > 1) { + LOG(fatal) << "Only one process can be activated"; + } + + if (doprocessLambda) { + auto lambdaHistSpec = v0histmanager::makeV0QaHistSpecMap(confLambdaBinning, confLambdaQaBinning); + lambdaHistManager.init(&hRegistry, lambdaHistSpec, posDaughterHistSpec, negDaughterHistSpec); + } + + if (doprocessK0short) { + auto k0shortHistSpec = v0histmanager::makeV0QaHistSpecMap(confK0shortBinning, confK0shortQaBinning); + k0shortHistManager.init(&hRegistry, k0shortHistSpec, posDaughterHistSpec, negDaughterHistSpec); + } + }; + + void processK0short(FilteredCollision const& col, K0shorts const& /*k0shorts*/, Tracks const& tracks) + { + colHistManager.fill(col); + auto k0shortSlice = k0shortPartition->sliceByCached(femtobase::stored::collisionId, col.globalIndex(), cache); + for (auto const& k0short : k0shortSlice) { + k0shortHistManager.fill(k0short, tracks); + } + } + PROCESS_SWITCH(FemtoV0Qa, processK0short, "Process k0shorts", false); + + void processLambda(FilteredCollision const& col, Lambdas const& /*lambdas*/, Tracks const& tracks) + { + colHistManager.fill(col); + auto lambdaSlice = lambdaPartition->sliceByCached(femtobase::stored::collisionId, col.globalIndex(), cache); + for (auto const& lambda : lambdaSlice) { + lambdaHistManager.fill(lambda, tracks); + } + } + PROCESS_SWITCH(FemtoV0Qa, processLambda, "Process lambdas", true); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + WorkflowSpec workflow{ + adaptAnalysisTask(cfgc), + }; + return workflow; +} diff --git a/PWGCF/Femto/Utils/CMakeLists.txt b/PWGCF/Femto/Utils/CMakeLists.txt new file mode 100644 index 00000000000..4c182222bf2 --- /dev/null +++ b/PWGCF/Femto/Utils/CMakeLists.txt @@ -0,0 +1,10 @@ +# Copyright 2019-2024 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.