diff --git a/PWGCF/Femto/Core/dataTypes.h b/PWGCF/Femto/Core/dataTypes.h index 25c5d84147c..bbda8a76e93 100644 --- a/PWGCF/Femto/Core/dataTypes.h +++ b/PWGCF/Femto/Core/dataTypes.h @@ -35,6 +35,9 @@ using V0MaskType = uint32_t; using V0MaskType001 = uint16_t; // old data type, was too narrow using V0Type = uint16_t; +// datatypes for photons +using PhotonMaskType = uint16_t; + // datatypes for kinks using KinkMaskType = uint32_t; using KinkType = uint8_t; @@ -44,6 +47,9 @@ using TwoTrackResonanceMaskType = uint32_t; // two track resonance types using TwoTrackResonanceType = uint16_t; +// datatype for resonances built from two photons (pi0, eta, ...) +using TwoPhotonResonanceType = uint16_t; + // datatypes for cascades using CascadeMaskType = uint32_t; using CascadeMaskType001 = uint16_t; // old data type, was too narrow diff --git a/PWGCF/Femto/Core/modes.h b/PWGCF/Femto/Core/modes.h index ca55b122c80..609effb965b 100644 --- a/PWGCF/Femto/Core/modes.h +++ b/PWGCF/Femto/Core/modes.h @@ -234,6 +234,12 @@ enum class TwoTrackResonance : o2::analysis::femto::datatypes::TwoTrackResonance kKstar0Bar }; +// resonances reconstructed from two photons (PCM), e.g. pi0 -> gamma gamma, eta -> gamma gamma +enum class TwoPhotonResonance : o2::analysis::femto::datatypes::TwoPhotonResonanceType { + kPi0, + kEta +}; + enum class CharmHadron : o2::analysis::femto::datatypes::CharmHadronType { kD0, kD0Bar, diff --git a/PWGCF/Femto/Core/partitions.h b/PWGCF/Femto/Core/partitions.h index fac0704c71f..ad82cde98ea 100644 --- a/PWGCF/Femto/Core/partitions.h +++ b/PWGCF/Femto/Core/partitions.h @@ -94,6 +94,21 @@ ncheckbit(o2::aod::femtotwotrackresonances::maskNegDau, (selection).negDauMaskAboveThres), \ ncheckbit(o2::aod::femtotwotrackresonances::maskNegDau, (selection).negDauMaskBelowThres)) +// partition for resonances built from two photons (pi0, eta, ...): unlike MAKE_RESONANCE_0/1_PARTITON +// there is no sign and no momentum-threshold PID switch, just a plain mask check per (unordered) daughter +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define MAKE_TWOPHOTONRESONANCE_PARTITION(selection) \ + (o2::aod::femtobase::stored::pt > (selection).ptMin) && \ + (o2::aod::femtobase::stored::pt < (selection).ptMax) && \ + (o2::aod::femtobase::stored::eta > (selection).etaMin) && \ + (o2::aod::femtobase::stored::eta < (selection).etaMax) && \ + (o2::aod::femtobase::stored::phi > (selection).phiMin) && \ + (o2::aod::femtobase::stored::phi < (selection).phiMax) && \ + (o2::aod::femtobase::stored::mass > (selection).massMin) && \ + (o2::aod::femtobase::stored::mass < (selection).massMax) && \ + ncheckbit(o2::aod::femtotwophotonresonances::maskDau1, (selection).dau1Mask) && \ + ncheckbit(o2::aod::femtotwophotonresonances::maskDau2, (selection).dau2Mask) + // partition for lambdas // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define MAKE_LAMBDA_PARTITION(selection) \ @@ -123,6 +138,18 @@ (o2::aod::femtobase::stored::mass < (selection).massMax) && \ ncheckbit(o2::aod::femtov0s::mask, (selection).mask) +// partition for photons (PCM) +// no sign/mass handling: photons are their own antiparticle and have no mass window +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define MAKE_PHOTON_PARTITION(selection) \ + (o2::aod::femtobase::stored::pt > (selection).ptMin) && \ + (o2::aod::femtobase::stored::pt < (selection).ptMax) && \ + (o2::aod::femtobase::stored::eta > (selection).etaMin) && \ + (o2::aod::femtobase::stored::eta < (selection).etaMax) && \ + (o2::aod::femtobase::stored::phi > (selection).phiMin) && \ + (o2::aod::femtobase::stored::phi < (selection).phiMax) && \ + ncheckbit(o2::aod::femtophotons::mask, (selection).mask) + // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define MAKE_CASCADE_PARTITION(selection) \ ifnode((selection).sign.node() != 0, \ diff --git a/PWGCF/Femto/Core/photonBuilder.h b/PWGCF/Femto/Core/photonBuilder.h new file mode 100644 index 00000000000..38943d09c27 --- /dev/null +++ b/PWGCF/Femto/Core/photonBuilder.h @@ -0,0 +1,404 @@ +// 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 photonBuilder.h +/// \brief photon (PCM) builder +/// +/// Default selection values for the mandatory cuts (dauTpcNSigmaElAbsMax, keepDaughtersWithoutTpc) +/// are taken from the PCM branch of EventFiltering/PWGEM/EMPhotonFilter.cxx (isSelectedSecondary() +/// and the min_pt_pcm_photon cut in runFilter()): both V0 daughters must have |TPC nSigma_e| < 3.5 +/// whenever a TPC signal is available, and a daughter without TPC is not rejected. Note that +/// EMPhotonFilter.cxx also declares minpt_v0, maxeta_v0, max_dcatopv_xy_v0 and max_dcatopv_z_v0, +/// but never applies them -- they are dead configurables in that task. The additional geometric +/// cuts here (cosPA, DCA to PV, chi2/NDF, radius) are standard PCM conversion-quality cuts, not +/// used by that filter task, so they default to permissive (no-op) values. +/// \author Anton Riedel, TU München, anton.riedel@tum.de + +#ifndef PWGCF_FEMTO_CORE_PHOTONBUILDER_H_ +#define PWGCF_FEMTO_CORE_PHOTONBUILDER_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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace o2::analysis::femto::photonbuilder +{ + +// filters applied in the producer task (loose pre-selection, applied before quality/PID cuts) +struct ConfPhotonFilters : o2::framework::ConfigurableGroup { + std::string prefix = std::string("PhotonFilters"); + o2::framework::Configurable ptMin{"ptMin", 0.f, "Minimum pT (matches EMPhotonFilter min_pt_pcm_photon default)"}; + 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 mGammaAbsMax{"mGammaAbsMax", 1e10f, "Maximum |m_ee| of the conversion (KF) at the secondary vertex"}; +}; + +// selection bits for photons (PCM) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define PHOTON_DEFAULT_BITS \ + o2::framework::Configurable passThrough{"passThrough", false, "If true, all photons are passed through. Bits for all selections are stored."}; \ + o2::framework::Configurable> cosPaMin{"cosPaMin", {-1.f}, "Minimum cosine of pointing angle (not used by EMPhotonFilter, default is permissive)"}; \ + o2::framework::Configurable> dcaToPvXYAbsMax{"dcaToPvXYAbsMax", {1e10f}, "Maximum |DCAxy| of the photon (V0) to the PV (cm)"}; \ + o2::framework::Configurable> dcaToPvZAbsMax{"dcaToPvZAbsMax", {1e10f}, "Maximum |DCAz| of the photon (V0) to the PV (cm)"}; \ + o2::framework::Configurable> chi2NdfMax{"chi2NdfMax", {1e10f}, "Maximum chi2/NDF of the KF conversion vertex"}; \ + o2::framework::Configurable> v0RadiusMin{"v0RadiusMin", {0.f}, "Minimum transverse radius of the conversion point (cm)"}; \ + o2::framework::Configurable> v0RadiusMax{"v0RadiusMax", {1e10f}, "Maximum transverse radius of the conversion point (cm)"}; \ + o2::framework::Configurable> dauAbsEtaMax{"dauAbsEtaMax", {0.9f}, "Maximum |eta| for daughter tracks (matches EMPhotonFilter maxeta_v0, though unused there)"}; \ + o2::framework::Configurable> dauTpcNSigmaElAbsMax{"dauTpcNSigmaElAbsMax", {3.5f}, "Maximum |TPC nSigma_e| for V0 daughters (from EMPhotonFilter isSelectedSecondary)"}; \ + o2::framework::Configurable keepDaughtersWithoutTpc{"keepDaughtersWithoutTpc", true, "If true, a daughter without a TPC signal is not rejected by the TPC electron PID cut (matches EMPhotonFilter behaviour)"}; + +struct ConfPhotonBits : o2::framework::ConfigurableGroup { + std::string prefix = std::string("PhotonBits"); + PHOTON_DEFAULT_BITS +}; + +#undef PHOTON_DEFAULT_BITS + +// base selection for QA/analysis tasks, used to re-select already produced photons by mask +// (analogous to ConfLambdaSelection/ConfK0shortSelection in spirit, but photons have neither +// a PDG hypothesis nor a mass window) +template +struct ConfPhotonSelection : o2::framework::ConfigurableGroup { + std::string prefix = Prefix; + 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 phi"}; + o2::framework::Configurable phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"}; + o2::framework::Configurable mask{"mask", 0, "Bitmask for photon selection"}; +}; + +constexpr const char PrefixPhotonSelection1[] = "PhotonSelection1"; +using ConfPhotonSelection1 = ConfPhotonSelection; + +/// The different selections for photons (PCM) +enum PhotonSels { + kCosPaMin, ///< Min. CPA (cosine pointing angle) of the conversion to the PV + kDcaToPvXYAbsMax, ///< Max. |DCAxy| of the photon to the PV + kDcaToPvZAbsMax, ///< Max. |DCAz| of the photon to the PV + kChi2NdfMax, ///< Max. chi2/NDF of the KF conversion vertex + kV0RadiusMin, ///< Min. transverse radius of the conversion point + kV0RadiusMax, ///< Max. transverse radius of the conversion point + kDauAbsEtaMax, ///< Max. absolute pseudorapidity of the daughters + kPosDauTpcNSigmaEl, ///< TPC electron PID for positive daughter + kNegDauTpcNSigmaEl, ///< TPC electron PID for negative daughter + kPhotonSelsMax +}; + +constexpr char PhotonSelHistName[] = "hPhotonSelection"; +constexpr char PhotonSelsName[] = "Photon selection object"; +const std::unordered_map photonSelectionNames = { + {kCosPaMin, "Min. CPA (cosine pointing angle)"}, + {kDcaToPvXYAbsMax, "Max. |DCAxy| to PV"}, + {kDcaToPvZAbsMax, "Max. |DCAz| to PV"}, + {kChi2NdfMax, "Max. chi2/NDF of conversion vertex"}, + {kV0RadiusMin, "Min. transverse radius"}, + {kV0RadiusMax, "Max. transverse radius"}, + {kDauAbsEtaMax, "Max. absolute pseudorapidity of daughters"}, + {kPosDauTpcNSigmaEl, "TPC electron PID for positive daughter"}, + {kNegDauTpcNSigmaEl, "TPC electron PID for negative daughter"}, +}; + +// enum for all photon (pre)filters (loose pre-selection, applied before quality/PID cuts) +enum PhotonFilters { + kPtMin, + kPtMax, + kEtaMin, + kEtaMax, + kPhiMin, + kPhiMax, + kMGammaAbsMax, + kPhotonFiltersMax +}; + +constexpr char PhotonFilterHistName[] = "hPhotonFilters"; +const std::unordered_map photonFilterNames = { + {kPtMin, "ptMin"}, + {kPtMax, "ptMax"}, + {kEtaMin, "etaMin"}, + {kEtaMax, "etaMax"}, + {kPhiMin, "phiMin"}, + {kPhiMax, "phiMax"}, + {kMGammaAbsMax, "mGammaAbsMax"}, +}; + +/// \brief Cut class to contain and execute all cuts applied to PCM photons (V0PhotonsKF) +template +class PhotonSelection : public baseselection::BaseSelection +{ + public: + PhotonSelection() = default; + ~PhotonSelection() override = default; + + template + void configure(o2::framework::HistogramRegistry* registry, T1& config, T2& filter) + { + this->init(config.passThrough.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; + mMGammaAbsMax = filter.mGammaAbsMax.value; + mKeepDaughtersWithoutTpc = config.keepDaughtersWithoutTpc.value; + + this->addSelection(kCosPaMin, photonSelectionNames.at(kCosPaMin), config.cosPaMin.value, limits::kLowerLimit, true, true, false); + this->addSelection(kDcaToPvXYAbsMax, photonSelectionNames.at(kDcaToPvXYAbsMax), config.dcaToPvXYAbsMax.value, limits::kAbsUpperLimit, true, true, false); + this->addSelection(kDcaToPvZAbsMax, photonSelectionNames.at(kDcaToPvZAbsMax), config.dcaToPvZAbsMax.value, limits::kAbsUpperLimit, true, true, false); + this->addSelection(kChi2NdfMax, photonSelectionNames.at(kChi2NdfMax), config.chi2NdfMax.value, limits::kUpperLimit, true, true, false); + this->addSelection(kV0RadiusMin, photonSelectionNames.at(kV0RadiusMin), config.v0RadiusMin.value, limits::kLowerLimit, true, true, false); + this->addSelection(kV0RadiusMax, photonSelectionNames.at(kV0RadiusMax), config.v0RadiusMax.value, limits::kUpperLimit, true, true, false); + this->addSelection(kDauAbsEtaMax, photonSelectionNames.at(kDauAbsEtaMax), config.dauAbsEtaMax.value, limits::kAbsUpperLimit, true, true, false); + this->addSelection(kPosDauTpcNSigmaEl, photonSelectionNames.at(kPosDauTpcNSigmaEl), config.dauTpcNSigmaElAbsMax.value, limits::kAbsUpperLimit, true, true, false); + this->addSelection(kNegDauTpcNSigmaEl, photonSelectionNames.at(kNegDauTpcNSigmaEl), config.dauTpcNSigmaElAbsMax.value, limits::kAbsUpperLimit, true, true, false); + + this->setupSelectionHistogram(registry); + this->template setupFilterHistogram( + registry, + { + {photonFilterNames.at(kPtMin), mPtMin}, + {photonFilterNames.at(kPtMax), mPtMax}, + {photonFilterNames.at(kEtaMin), mEtaMin}, + {photonFilterNames.at(kEtaMax), mEtaMax}, + {photonFilterNames.at(kPhiMin), mPhiMin}, + {photonFilterNames.at(kPhiMax), mPhiMax}, + {photonFilterNames.at(kMGammaAbsMax), mMGammaAbsMax}, + }); + } + + template + void applySelections(T1 const& photon, T2 const& /*v0legs*/) + { + this->reset(); + this->evaluateObservable(kCosPaMin, photon.cospa()); + this->evaluateObservable(kDcaToPvXYAbsMax, photon.dcaXYtopv()); + this->evaluateObservable(kDcaToPvZAbsMax, photon.dcaZtopv()); + this->evaluateObservable(kChi2NdfMax, photon.chiSquareNDF()); + this->evaluateObservable(kV0RadiusMin, photon.v0radius()); + this->evaluateObservable(kV0RadiusMax, photon.v0radius()); + + auto posDaughter = photon.template posTrack_as(); + auto negDaughter = photon.template negTrack_as(); + + std::array etaAbsDaughters = {std::fabs(posDaughter.eta()), std::fabs(negDaughter.eta())}; + this->evaluateObservable(kDauAbsEtaMax, *std::max_element(etaAbsDaughters.begin(), etaAbsDaughters.end())); + + // TPC electron PID: evaluate only when a TPC signal is available, matching + // EMPhotonFilter::isSelectedSecondary(), which never rejects a daughter without TPC + auto evaluateDaughterTpcEl = [this](PhotonSels bit, bool hasTpc, float tpcNSigmaEl) { + if (hasTpc) { + this->evaluateObservable(bit, tpcNSigmaEl); + } else if (mKeepDaughtersWithoutTpc) { + this->evaluateObservable(bit, 0.f); + } + }; + evaluateDaughterTpcEl(kPosDauTpcNSigmaEl, posDaughter.hasTPC(), posDaughter.tpcNSigmaEl()); + evaluateDaughterTpcEl(kNegDauTpcNSigmaEl, negDaughter.hasTPC(), negDaughter.tpcNSigmaEl()); + + this->assembleBitmask(); + } + + template + bool checkFilters(const T& photon) const + { + bool pass = true; + bool p = false; + + p = photon.pt() > mPtMin; + this->template fillFilter(kPtMin, p); + pass &= p; + + p = photon.pt() < mPtMax; + this->template fillFilter(kPtMax, p); + pass &= p; + + p = photon.eta() > mEtaMin; + this->template fillFilter(kEtaMin, p); + pass &= p; + + p = photon.eta() < mEtaMax; + this->template fillFilter(kEtaMax, p); + pass &= p; + + p = photon.phi() > mPhiMin; + this->template fillFilter(kPhiMin, p); + pass &= p; + + p = photon.phi() < mPhiMax; + this->template fillFilter(kPhiMax, p); + pass &= p; + + p = std::fabs(photon.mGamma()) < mMGammaAbsMax; + this->template fillFilter(kMGammaAbsMax, p); + pass &= p; + + this->template fillFilterSummary(pass); + + return this->isPassThrough() || pass; + } + + protected: + // kinematic filters + float mPtMin = 0.f; + float mPtMax = 99.f; + float mEtaMin = -10.f; + float mEtaMax = 10.f; + float mPhiMin = 0.f; + float mPhiMax = o2::constants::math::TwoPI; + float mMGammaAbsMax = 1e10f; + + bool mKeepDaughtersWithoutTpc = true; +}; + +struct PhotonBuilderProducts : o2::framework::ProducesGroup { + o2::framework::Produces producedPhotons; + o2::framework::Produces producedLitePhotons; + o2::framework::Produces producedPhotonMasks; + o2::framework::Produces producedPhotonExtras; +}; + +struct ConfPhotonTables : o2::framework::ConfigurableGroup { + std::string prefix = std::string("PhotonTables"); + o2::framework::Configurable producePhotons{"producePhotons", -1, "Produce Photons (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceLitePhotons{"produceLitePhotons", -1, "Produce LitePhotons (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable producePhotonMasks{"producePhotonMasks", -1, "Produce PhotonMasks (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable producePhotonExtras{"producePhotonExtras", -1, "Produce PhotonExtras (-1: auto; 0 off; 1 on)"}; +}; + +/// \brief Builder for PCM photons (V0PhotonsKF + V0Legs, from PWGEM/PhotonMeson) +/// +/// Unlike the strangeness V0 builder, daughter tracks are not indexed into FTracks: the +/// conversion legs (V0Legs) carry a reduced set of columns and are not meant to be paired +/// individually in femtoscopy, so only photon-level kinematics plus daughter PID/quality +/// diagnostics (FPhotonExtras) are stored. +class PhotonBuilder +{ + public: + PhotonBuilder() = default; + ~PhotonBuilder() = default; + + template + void init(o2::framework::HistogramRegistry* registry, T1& config, T2& filter, T3& table, T4& initContext) + { + LOG(info) << "Initialize femto photon (PCM) builder..."; + mProducePhotons = utils::enableTable("FPhotons_001", table.producePhotons.value, initContext); + mProduceLitePhotons = utils::enableTable("FLitePhotons_001", table.produceLitePhotons.value, initContext); + mProducePhotonMasks = utils::enableTable("FPhotonMasks_001", table.producePhotonMasks.value, initContext); + mProducePhotonExtras = utils::enableTable("FPhotonExtras_001", table.producePhotonExtras.value, initContext); + + if (mProducePhotons && mProduceLitePhotons) { + LOG(fatal) << "FPhotons and FLitePhotons are mutually exclusive -- enable only one. " + << "FLitePhotons is meant to replace FPhotons at the producer stage (for better compression in derived data)."; + } + if (mProducePhotons || mProduceLitePhotons || mProducePhotonMasks || mProducePhotonExtras) { + mFillAnyTable = true; + } else { + LOG(info) << "No tables configured, Selection object will not be configured..."; + LOG(info) << "Initialization done..."; + return; + } + mPhotonSelection.configure(registry, config, filter); + mPhotonSelection.printSelections(PhotonSelsName); + LOG(info) << "Initialization done..."; + } + + template + void fillPhotons(T1 const& col, T2& collisionBuilder, T3& collisionProducts, T4& photonProducts, T5 const& photons, T6 const& v0legs) + { + if (!mFillAnyTable) { + return; + } + for (const auto& photon : photons) { + if (!mPhotonSelection.checkFilters(photon)) { + continue; + } + mPhotonSelection.applySelections(photon, v0legs); + if (!mPhotonSelection.passesAllRequiredSelections()) { + continue; + } + + collisionBuilder.template fillCollision(collisionProducts, col); + + if (mProducePhotons) { + photonProducts.producedPhotons(collisionBuilder.collisionIndex(), + photon.pt(), + photon.eta(), + photon.phi()); + } + if (mProduceLitePhotons) { + photonProducts.producedLitePhotons(collisionBuilder.collisionIndex(), + o2::aod::femtobase::lite::binUnsignedPt(photon.pt()), + o2::aod::femtobase::lite::binEta(photon.eta()), + o2::aod::femtobase::lite::binPhi(photon.phi())); + } + if (mProducePhotonMasks) { + photonProducts.producedPhotonMasks(mPhotonSelection.getBitmask()); + } + if (mProducePhotonExtras) { + auto posDaughter = photon.template posTrack_as(); + auto negDaughter = photon.template negTrack_as(); + photonProducts.producedPhotonExtras( + photon.cospa(), + photon.dcaXYtopv(), + photon.dcaZtopv(), + photon.chiSquareNDF(), + photon.v0radius(), + posDaughter.pt(), + negDaughter.pt(), + posDaughter.tpcInnerParam(), + negDaughter.tpcInnerParam(), + posDaughter.tpcSignal(), + negDaughter.tpcSignal(), + posDaughter.tpcNSigmaEl(), + negDaughter.tpcNSigmaEl(), + photon.vx(), + photon.vy(), + photon.vz()); + } + } + } + + [[nodiscard]] bool fillAnyTable() const { return mFillAnyTable; } + [[nodiscard]] bool isPassThrough() const { return mPhotonSelection.isPassThrough(); } + + private: + PhotonSelection mPhotonSelection; + bool mFillAnyTable = false; + bool mProducePhotons = false; + bool mProduceLitePhotons = false; + bool mProducePhotonMasks = false; + bool mProducePhotonExtras = false; +}; + +} // namespace o2::analysis::femto::photonbuilder +#endif // PWGCF_FEMTO_CORE_PHOTONBUILDER_H_ diff --git a/PWGCF/Femto/Core/photonHistManager.h b/PWGCF/Femto/Core/photonHistManager.h new file mode 100644 index 00000000000..8c010060eb8 --- /dev/null +++ b/PWGCF/Femto/Core/photonHistManager.h @@ -0,0 +1,295 @@ +// 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 photonHistManager.h +/// \brief histogram manager for photon (PCM) histograms +/// \author Anton Riedel, TU München, anton.riedel@tum.de + +#ifndef PWGCF_FEMTO_CORE_PHOTONHISTMANAGER_H_ +#define PWGCF_FEMTO_CORE_PHOTONHISTMANAGER_H_ + +#include "PWGCF/Femto/Core/histManager.h" +#include "PWGCF/Femto/Core/modes.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace o2::analysis::femto::photonhistmanager +{ +// enum for photon (PCM) histograms +enum PhotonHist { + // analysis + kPt, + kEta, + kPhi, + // qa variables + kCosPa, + kPa, + kDcaToPvXY, + kDcaToPvZ, + kChi2Ndf, + kV0Radius, + kDecayVtxX, + kDecayVtxY, + kDecayVtxZ, + kDecayVtx, + kPosDauTpcNSigmaEl, + kNegDauTpcNSigmaEl, + kPosDauPt, + kNegDauPt, + // 2d qa + kPtVsEta, + kPtVsPhi, + kPhiVsEta, + kPtVsCosPa, + kPosDauVsNegDauTpcNSigmaEl, + kPosDauTpcSignalVsP, + kNegDauTpcSignalVsP, + + kPhotonHistLast +}; + +struct ConfPhotonBinning : o2::framework::ConfigurableGroup { + std::string prefix = std::string("PhotonBinning"); + 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"}; +}; + +struct ConfPhotonQaBinning : o2::framework::ConfigurableGroup { + std::string prefix = std::string("PhotonQaBinning"); + o2::framework::Configurable plot2d{"plot2d", true, "Generate various 2D QA plots"}; + o2::framework::ConfigurableAxis cosPa{"cosPa", {{100, 0.8, 1}}, "Cosine of pointing angle"}; + o2::framework::ConfigurableAxis pa{"pa", {{180, 0, 1.f * o2::constants::math::PI}}, "Pointing angle"}; + o2::framework::ConfigurableAxis dcaToPv{"dcaToPv", {{200, -1, 1}}, "DCA of the photon to the PV (cm)"}; + o2::framework::ConfigurableAxis chi2Ndf{"chi2Ndf", {{100, 0, 50}}, "Chi2/NDF of the KF conversion vertex"}; + o2::framework::ConfigurableAxis v0Radius{"v0Radius", {{200, 0, 200}}, "Transverse radius of the conversion point (cm)"}; + o2::framework::ConfigurableAxis decayVertex{"decayVertex", {{200, 0, 200}}, "Distance of the conversion point from the nominal interaction point (cm)"}; + o2::framework::ConfigurableAxis tpcNSigmaEl{"tpcNSigmaEl", {{200, -10, 10}}, "TPC n sigma electron for daughters"}; + o2::framework::ConfigurableAxis dauPt{"dauPt", {{300, 0, 3}}, "Pt of the daughters"}; + o2::framework::ConfigurableAxis dauTpcInnerParam{"dauTpcInnerParam", {{200, 0, 4}}, "Momentum of the daughters at the inner wall of the TPC (GeV/#it{c})"}; + o2::framework::ConfigurableAxis dauTpcSignal{"dauTpcSignal", {{200, 0, 200}}, "TPC dE/dx of the daughters"}; +}; + +// must be in sync with enum PhotonHist +constexpr std::array, kPhotonHistLast> HistTable = { + {{kPt, o2::framework::HistType::kTH1F, "hPt", "Transverse Momentum; p_{T} (GeV/#it{c}); Entries"}, + {kEta, o2::framework::HistType::kTH1F, "hEta", "Pseudorapdity; #eta; Entries"}, + {kPhi, o2::framework::HistType::kTH1F, "hPhi", "Azimuthal angle; #varphi; Entries"}, + {kCosPa, o2::framework::HistType::kTH1F, "hCosPa", "Cosine of pointing angle; cos(#alpha); Entries"}, + {kPa, o2::framework::HistType::kTH1F, "hPa", "Pointing angle; #alpha; Entries"}, + {kDcaToPvXY, o2::framework::HistType::kTH1F, "hDcaToPvXY", "DCA_{xy} to PV; DCA_{xy} (cm); Entries"}, + {kDcaToPvZ, o2::framework::HistType::kTH1F, "hDcaToPvZ", "DCA_{z} to PV; DCA_{z} (cm); Entries"}, + {kChi2Ndf, o2::framework::HistType::kTH1F, "hChi2Ndf", "Chi2/NDF of KF conversion vertex; #chi^{2}/NDF; Entries"}, + {kV0Radius, o2::framework::HistType::kTH1F, "hV0Radius", "Transverse radius; r_{xy} (cm); Entries"}, + {kDecayVtxX, o2::framework::HistType::kTH1F, "hDecayVtxX", "X coordinate of conversion point; DV_{X} (cm); Entries"}, + {kDecayVtxY, o2::framework::HistType::kTH1F, "hDecayVtxY", "Y coordinate of conversion point; DV_{Y} (cm); Entries"}, + {kDecayVtxZ, o2::framework::HistType::kTH1F, "hDecayVtxZ", "Z coordinate of conversion point; DV_{Z} (cm); Entries"}, + {kDecayVtx, o2::framework::HistType::kTH1F, "hDecayVtx", "Distance of conversion point from primary vertex; DV (cm); Entries"}, + {kPosDauTpcNSigmaEl, o2::framework::HistType::kTH1F, "hPosDauTpcNSigmaEl", "TPC n#sigma_{e} of positive daughter; n#sigma_{TPC,e}; Entries"}, + {kNegDauTpcNSigmaEl, o2::framework::HistType::kTH1F, "hNegDauTpcNSigmaEl", "TPC n#sigma_{e} of negative daughter; n#sigma_{TPC,e}; Entries"}, + {kPosDauPt, o2::framework::HistType::kTH1F, "hPosDauPt", "p_{T} of positive daughter; p_{T} (GeV/#it{c}); Entries"}, + {kNegDauPt, o2::framework::HistType::kTH1F, "hNegDauPt", "p_{T} of negative daughter; p_{T} (GeV/#it{c}); Entries"}, + {kPtVsEta, o2::framework::HistType::kTH2F, "hPtVsEta", "p_{T} vs #eta; p_{T} (GeV/#it{c}) ; #eta"}, + {kPtVsPhi, o2::framework::HistType::kTH2F, "hPtVsPhi", "p_{T} vs #varphi; p_{T} (GeV/#it{c}) ; #varphi"}, + {kPhiVsEta, o2::framework::HistType::kTH2F, "hPhiVsEta", "#varphi vs #eta; #varphi ; #eta"}, + {kPtVsCosPa, o2::framework::HistType::kTH2F, "hPtVsCosPa", "p_{T} vs cosine of pointing angle; p_{T} (GeV/#it{c}); cos(#alpha)"}, + {kPosDauVsNegDauTpcNSigmaEl, o2::framework::HistType::kTH2F, "hPosDauVsNegDauTpcNSigmaEl", "TPC n#sigma_{e} of positive vs negative daughter; n#sigma_{TPC,e} (pos.); n#sigma_{TPC,e} (neg.)"}, + {kPosDauTpcSignalVsP, o2::framework::HistType::kTH2F, "hPosDauTpcSignalVsP", "TPC dE/dx vs p of positive daughter; p_{TPC} (GeV/#it{c}); dE/dx"}, + {kNegDauTpcSignalVsP, o2::framework::HistType::kTH2F, "hNegDauTpcSignalVsP", "TPC dE/dx vs p of negative daughter; p_{TPC} (GeV/#it{c}); dE/dx"}}, +}; + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define PHOTON_HIST_ANALYSIS_MAP(conf) \ + {kPt, {(conf).pt}}, \ + {kEta, {(conf).eta}}, \ + {kPhi, {(conf).phi}}, + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define PHOTON_HIST_QA_MAP(confAnalysis, confQa) \ + {kCosPa, {(confQa).cosPa}}, \ + {kPa, {(confQa).pa}}, \ + {kDcaToPvXY, {(confQa).dcaToPv}}, \ + {kDcaToPvZ, {(confQa).dcaToPv}}, \ + {kChi2Ndf, {(confQa).chi2Ndf}}, \ + {kV0Radius, {(confQa).v0Radius}}, \ + {kDecayVtxX, {(confQa).decayVertex}}, \ + {kDecayVtxY, {(confQa).decayVertex}}, \ + {kDecayVtxZ, {(confQa).decayVertex}}, \ + {kDecayVtx, {(confQa).decayVertex}}, \ + {kPosDauTpcNSigmaEl, {(confQa).tpcNSigmaEl}}, \ + {kNegDauTpcNSigmaEl, {(confQa).tpcNSigmaEl}}, \ + {kPosDauPt, {(confQa).dauPt}}, \ + {kNegDauPt, {(confQa).dauPt}}, \ + {kPtVsEta, {(confAnalysis).pt, (confAnalysis).eta}}, \ + {kPtVsPhi, {(confAnalysis).pt, (confAnalysis).phi}}, \ + {kPhiVsEta, {(confAnalysis).phi, (confAnalysis).eta}}, \ + {kPtVsCosPa, {(confAnalysis).pt, (confQa).cosPa}}, \ + {kPosDauVsNegDauTpcNSigmaEl, {(confQa).tpcNSigmaEl, (confQa).tpcNSigmaEl}}, \ + {kPosDauTpcSignalVsP, {(confQa).dauTpcInnerParam, (confQa).dauTpcSignal}}, \ + {kNegDauTpcSignalVsP, {(confQa).dauTpcInnerParam, (confQa).dauTpcSignal}}, + +template +auto makePhotonHistSpecMap(const T& confBinningAnalysis) +{ + return std::map>{ + PHOTON_HIST_ANALYSIS_MAP(confBinningAnalysis)}; +} + +template +std::map> makePhotonQaHistSpecMap(T1 const& confBinningAnalysis, T2 const& confBinningQa) +{ + return std::map>{ + PHOTON_HIST_ANALYSIS_MAP(confBinningAnalysis) + PHOTON_HIST_QA_MAP(confBinningAnalysis, confBinningQa)}; +} + +#undef PHOTON_HIST_ANALYSIS_MAP +#undef PHOTON_HIST_QA_MAP + +constexpr char PrefixPhotonQa[] = "PhotonQA/"; +constexpr char PrefixTwoPhotonResonanceDau1Qa[] = "TwoPhotonResonanceDau1Qa/"; +constexpr char PrefixTwoPhotonResonanceDau2Qa[] = "TwoPhotonResonanceDau2Qa/"; + +constexpr std::string_view AnalysisDir = "Analysis/"; +constexpr std::string_view QaDir = "QA/"; + +/// \class PhotonHistManager +/// \brief Histogram manager for PCM photon candidates (reco + QA only, no MC) +template +class PhotonHistManager +{ + public: + PhotonHistManager() = default; + ~PhotonHistManager() = default; + + // init for analysis only + template + void init(o2::framework::HistogramRegistry* registry, std::map> const& specs) + { + mHistogramRegistry = registry; + if constexpr (modes::isFlagSet(mode, modes::Mode::kReco)) { + this->initAnalysis(specs); + } + } + + // init for analysis + qa + template + void init(o2::framework::HistogramRegistry* registry, std::map> const& specs, T const& confQaBinning) + { + mHistogramRegistry = registry; + mPlot2d = confQaBinning.plot2d.value; + if constexpr (modes::isFlagSet(mode, modes::Mode::kReco)) { + this->initAnalysis(specs); + } + if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { + this->initQa(specs); + } + } + + template + void fill(T const& photon) + { + if constexpr (modes::isFlagSet(mode, modes::Mode::kReco)) { + this->fillAnalysis(photon); + } + if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { + this->fillQa(photon); + } + } + + private: + void initAnalysis(std::map> const& specs) + { + std::string analysisDir = std::string(Prefix) + std::string(AnalysisDir); + mHistogramRegistry->add(analysisDir + getHistNameV2(kPt, HistTable), getHistDesc(kPt, HistTable), getHistType(kPt, HistTable), {specs.at(kPt)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kEta, HistTable), getHistDesc(kEta, HistTable), getHistType(kEta, HistTable), {specs.at(kEta)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kPhi, HistTable), getHistDesc(kPhi, HistTable), getHistType(kPhi, HistTable), {specs.at(kPhi)}); + } + + void initQa(std::map> const& specs) + { + std::string qaDir = std::string(Prefix) + std::string(QaDir); + mHistogramRegistry->add(qaDir + getHistNameV2(kCosPa, HistTable), getHistDesc(kCosPa, HistTable), getHistType(kCosPa, HistTable), {specs.at(kCosPa)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kPa, HistTable), getHistDesc(kPa, HistTable), getHistType(kPa, HistTable), {specs.at(kPa)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kDcaToPvXY, HistTable), getHistDesc(kDcaToPvXY, HistTable), getHistType(kDcaToPvXY, HistTable), {specs.at(kDcaToPvXY)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kDcaToPvZ, HistTable), getHistDesc(kDcaToPvZ, HistTable), getHistType(kDcaToPvZ, HistTable), {specs.at(kDcaToPvZ)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kChi2Ndf, HistTable), getHistDesc(kChi2Ndf, HistTable), getHistType(kChi2Ndf, HistTable), {specs.at(kChi2Ndf)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kV0Radius, HistTable), getHistDesc(kV0Radius, HistTable), getHistType(kV0Radius, HistTable), {specs.at(kV0Radius)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kDecayVtxX, HistTable), getHistDesc(kDecayVtxX, HistTable), getHistType(kDecayVtxX, HistTable), {specs.at(kDecayVtxX)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kDecayVtxY, HistTable), getHistDesc(kDecayVtxY, HistTable), getHistType(kDecayVtxY, HistTable), {specs.at(kDecayVtxY)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kDecayVtxZ, HistTable), getHistDesc(kDecayVtxZ, HistTable), getHistType(kDecayVtxZ, HistTable), {specs.at(kDecayVtxZ)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kDecayVtx, HistTable), getHistDesc(kDecayVtx, HistTable), getHistType(kDecayVtx, HistTable), {specs.at(kDecayVtx)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kPosDauTpcNSigmaEl, HistTable), getHistDesc(kPosDauTpcNSigmaEl, HistTable), getHistType(kPosDauTpcNSigmaEl, HistTable), {specs.at(kPosDauTpcNSigmaEl)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kNegDauTpcNSigmaEl, HistTable), getHistDesc(kNegDauTpcNSigmaEl, HistTable), getHistType(kNegDauTpcNSigmaEl, HistTable), {specs.at(kNegDauTpcNSigmaEl)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kPosDauPt, HistTable), getHistDesc(kPosDauPt, HistTable), getHistType(kPosDauPt, HistTable), {specs.at(kPosDauPt)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kNegDauPt, HistTable), getHistDesc(kNegDauPt, HistTable), getHistType(kNegDauPt, HistTable), {specs.at(kNegDauPt)}); + + if (mPlot2d) { + mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsEta, HistTable), getHistDesc(kPtVsEta, HistTable), getHistType(kPtVsEta, HistTable), {specs.at(kPtVsEta)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsPhi, HistTable), getHistDesc(kPtVsPhi, HistTable), getHistType(kPtVsPhi, HistTable), {specs.at(kPtVsPhi)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kPhiVsEta, HistTable), getHistDesc(kPhiVsEta, HistTable), getHistType(kPhiVsEta, HistTable), {specs.at(kPhiVsEta)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsCosPa, HistTable), getHistDesc(kPtVsCosPa, HistTable), getHistType(kPtVsCosPa, HistTable), {specs.at(kPtVsCosPa)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kPosDauVsNegDauTpcNSigmaEl, HistTable), getHistDesc(kPosDauVsNegDauTpcNSigmaEl, HistTable), getHistType(kPosDauVsNegDauTpcNSigmaEl, HistTable), {specs.at(kPosDauVsNegDauTpcNSigmaEl)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kPosDauTpcSignalVsP, HistTable), getHistDesc(kPosDauTpcSignalVsP, HistTable), getHistType(kPosDauTpcSignalVsP, HistTable), {specs.at(kPosDauTpcSignalVsP)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kNegDauTpcSignalVsP, HistTable), getHistDesc(kNegDauTpcSignalVsP, HistTable), getHistType(kNegDauTpcSignalVsP, HistTable), {specs.at(kNegDauTpcSignalVsP)}); + } + } + + template + void fillAnalysis(T const& photon) + { + mHistogramRegistry->fill(HIST(Prefix) + HIST(AnalysisDir) + HIST(getHistName(kPt, HistTable)), photon.pt()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(AnalysisDir) + HIST(getHistName(kEta, HistTable)), photon.eta()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(AnalysisDir) + HIST(getHistName(kPhi, HistTable)), photon.phi()); + } + + template + void fillQa(T const& photon) + { + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kCosPa, HistTable)), photon.cosPa()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kPa, HistTable)), photon.pa()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kDcaToPvXY, HistTable)), photon.dcaToPvXY()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kDcaToPvZ, HistTable)), photon.dcaToPvZ()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kChi2Ndf, HistTable)), photon.chi2Ndf()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kV0Radius, HistTable)), photon.v0Radius()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kDecayVtxX, HistTable)), photon.decayVtxX()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kDecayVtxY, HistTable)), photon.decayVtxY()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kDecayVtxZ, HistTable)), photon.decayVtxZ()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kDecayVtx, HistTable)), photon.decayVtx()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kPosDauTpcNSigmaEl, HistTable)), photon.posDauTpcNSigmaEl()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kNegDauTpcNSigmaEl, HistTable)), photon.negDauTpcNSigmaEl()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kPosDauPt, HistTable)), photon.posDauPt()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kNegDauPt, HistTable)), photon.negDauPt()); + + if (mPlot2d) { + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kPtVsEta, HistTable)), photon.pt(), photon.eta()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kPtVsPhi, HistTable)), photon.pt(), photon.phi()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kPhiVsEta, HistTable)), photon.phi(), photon.eta()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kPtVsCosPa, HistTable)), photon.pt(), photon.cosPa()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kPosDauVsNegDauTpcNSigmaEl, HistTable)), photon.posDauTpcNSigmaEl(), photon.negDauTpcNSigmaEl()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kPosDauTpcSignalVsP, HistTable)), photon.posDauTpcInnerParam(), photon.posDauTpcSignal()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kNegDauTpcSignalVsP, HistTable)), photon.negDauTpcInnerParam(), photon.negDauTpcSignal()); + } + } + + o2::framework::HistogramRegistry* mHistogramRegistry = nullptr; + bool mPlot2d = false; +}; +} // namespace o2::analysis::femto::photonhistmanager +#endif // PWGCF_FEMTO_CORE_PHOTONHISTMANAGER_H_ diff --git a/PWGCF/Femto/Core/twoPhotonResonanceBuilder.h b/PWGCF/Femto/Core/twoPhotonResonanceBuilder.h new file mode 100644 index 00000000000..484fa18e098 --- /dev/null +++ b/PWGCF/Femto/Core/twoPhotonResonanceBuilder.h @@ -0,0 +1,241 @@ +// 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 twoPhotonResonanceBuilder.h +/// \brief two photon resonance builder (pi0, eta, ... -> gamma gamma) +/// \author Anton Riedel, TU München, anton.riedel@tum.de + +#ifndef PWGCF_FEMTO_CORE_TWOPHOTONRESONANCEBUILDER_H_ +#define PWGCF_FEMTO_CORE_TWOPHOTONRESONANCEBUILDER_H_ + +#include "PWGCF/Femto/Core/dataTypes.h" +#include "PWGCF/Femto/Core/femtoUtils.h" +#include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/DataModel/FemtoTables.h" + +#include "Common/Core/RecoDecay.h" + +#include +#include +#include +#include +#include + +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include + +#include + +namespace o2::analysis::femto::twophotonresonancebuilder +{ +template +struct ConfTwoPhotonResonanceFilters : o2::framework::ConfigurableGroup { + std::string prefix = Prefix; + o2::framework::Configurable ptMin{"ptMin", 0.f, "Minimum pT"}; + o2::framework::Configurable ptMax{"ptMax", 10.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 the resonance"}; + o2::framework::Configurable massMax{"massMax", 1.f, "Maximum invariant mass for the resonance"}; +}; +constexpr const char PrefixPi0Filters[] = "Pi0Filters1"; +constexpr const char PrefixEtaFilters[] = "EtaFilters1"; + +using ConfPi0Filters = ConfTwoPhotonResonanceFilters; +using ConfEtaFilters = ConfTwoPhotonResonanceFilters; + +// selection used downstream (QA/pairing) to re-select already produced resonances by mass window + daughter mask. +// unlike TWOTRACKRESONANCE_DEFAULT_SELECTION there is no pos/neg split and no momentum-threshold PID +// switch: photon daughters are unordered and have no momentum-dependent PID regime. +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define TWOPHOTONRESONANCE_DEFAULT_SELECTION(defaultMassMin, defaultMassMax) \ + o2::framework::Configurable ptMin{"ptMin", 0.f, "Minimum pT"}; \ + o2::framework::Configurable ptMax{"ptMax", 10.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 the resonance"}; \ + o2::framework::Configurable massMax{"massMax", (defaultMassMax), "Maximum invariant mass for the resonance"}; \ + o2::framework::Configurable dau1Mask{"dau1Mask", 0, "Bitmask required for first photon daughter"}; \ + o2::framework::Configurable dau2Mask{"dau2Mask", 0, "Bitmask required for second photon daughter"}; + +struct ConfPi0Selection : o2::framework::ConfigurableGroup { + std::string prefix = std::string("Pi0Selection1"); + TWOPHOTONRESONANCE_DEFAULT_SELECTION(0.1f, 0.17f) +}; + +struct ConfEtaSelection : o2::framework::ConfigurableGroup { + std::string prefix = std::string("EtaSelection1"); + TWOPHOTONRESONANCE_DEFAULT_SELECTION(0.4f, 0.7f) +}; + +#undef TWOPHOTONRESONANCE_DEFAULT_SELECTION + +struct TwoPhotonResonanceBuilderProducts : o2::framework::ProducesGroup { + o2::framework::Produces producedPi0s; + o2::framework::Produces producedPi0Masks; + o2::framework::Produces producedEtas; + o2::framework::Produces producedEtaMasks; +}; + +struct ConfTwoPhotonResonanceTables : o2::framework::ConfigurableGroup { + std::string prefix = std::string("TwoPhotonResonanceTables"); + o2::framework::Configurable producePi0s{"producePi0s", -1, "Produce Pi0s (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable producePi0Masks{"producePi0Masks", -1, "Produce Pi0Masks (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceEtas{"produceEtas", -1, "Produce Etas (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceEtaMasks{"produceEtaMasks", -1, "Produce EtaMasks (-1: auto; 0 off; 1 on)"}; +}; + +/// \brief Builder for resonances reconstructed from two PCM photons (pi0, eta, ... -> gamma gamma) +/// +/// Unlike TwoTrackResonanceBuilder, there is no pos/neg daughter distinction (photons are their own +/// antiparticle) and no momentum-threshold PID switch (photon selection has no momentum-dependent PID +/// regime the way track PID does), so daughter combinatorics run over a single photon partition using +/// CombinationsStrictlyUpperIndexPolicy to avoid self-pairing/double-counting. +template +class TwoPhotonResonanceBuilder +{ + public: + TwoPhotonResonanceBuilder() = default; + ~TwoPhotonResonanceBuilder() = default; + + template + void init(T1& confFilter, T2& confTable, T3& initContext) + { + mMassMin = confFilter.massMin.value; + mMassMax = confFilter.massMax.value; + mPtMin = confFilter.ptMin.value; + mPtMax = confFilter.ptMax.value; + mEtaMin = confFilter.etaMin.value; + mEtaMax = confFilter.etaMax.value; + mPhiMin = confFilter.phiMin.value; + mPhiMax = confFilter.phiMax.value; + + if constexpr (modes::isEqual(resoType, modes::TwoPhotonResonance::kPi0)) { + LOG(info) << "Initialize femto Pi0 builder..."; + mProducePi0s = utils::enableTable("FPi0s_001", confTable.producePi0s.value, initContext); + mProducePi0Masks = utils::enableTable("FPi0Masks_001", confTable.producePi0Masks.value, initContext); + } + if constexpr (modes::isEqual(resoType, modes::TwoPhotonResonance::kEta)) { + LOG(info) << "Initialize femto Eta builder..."; + mProduceEtas = utils::enableTable("FEtas_001", confTable.produceEtas.value, initContext); + mProduceEtaMasks = utils::enableTable("FEtaMasks_001", confTable.produceEtaMasks.value, initContext); + } + + if (mProducePi0s || mProducePi0Masks || mProduceEtas || mProduceEtaMasks) { + mFillAnyTable = true; + } else { + LOG(info) << "No tables configured, Selection object will not be configured..."; + LOG(info) << "Initialization done..."; + return; + } + LOG(info) << "Initialization done..."; + } + + template + void fillResonances(T1 const& col, T2& resonanceProducts, T3& photonPartition, T4& cache) + { + if (!mFillAnyTable) { + return; + } + auto photonSlice = photonPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + for (auto const& [dau1, dau2] : o2::soa::combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(photonSlice, photonSlice))) { + this->fillResonance(col, dau1, dau2, resonanceProducts); + } + } + + private: + template + void reconstructResonance(T1 const& dau1, T2 const& dau2) + { + ROOT::Math::PtEtaPhiMVector vecDau1{dau1.pt(), dau1.eta(), dau1.phi(), 0.f}; + ROOT::Math::PtEtaPhiMVector vecDau2{dau2.pt(), dau2.eta(), dau2.phi(), 0.f}; + ROOT::Math::PtEtaPhiMVector vecResonance = vecDau1 + vecDau2; + + mPt = vecResonance.Pt(); + mEta = vecResonance.Eta(); + mPhi = RecoDecay::constrainAngle(vecResonance.Phi()); + mMass = vecResonance.M(); + } + + [[nodiscard]] bool checkFilters() const + { + return ((mMass > mMassMin && mMass < mMassMax) && + (mPt > mPtMin && mPt < mPtMax) && + (mEta > mEtaMin && mEta < mEtaMax) && + (mPhi >= mPhiMin && mPhi < mPhiMax)); + } + + template + void fillResonance(T1 const& col, T2 const& dau1, T3 const& dau2, T4& resonanceProducts) + { + reconstructResonance(dau1, dau2); + if (!checkFilters()) { + return; + } + + if constexpr (modes::isEqual(resoType, modes::TwoPhotonResonance::kPi0)) { + if (mProducePi0s) { + resonanceProducts.producedPi0s(col.globalIndex(), + mPt, + mEta, + mPhi, + mMass, + dau1.globalIndex(), + dau2.globalIndex()); + } + if (mProducePi0Masks) { + resonanceProducts.producedPi0Masks(dau1.mask(), dau2.mask()); + } + } + if constexpr (modes::isEqual(resoType, modes::TwoPhotonResonance::kEta)) { + if (mProduceEtas) { + resonanceProducts.producedEtas(col.globalIndex(), + mPt, + mEta, + mPhi, + mMass, + dau1.globalIndex(), + dau2.globalIndex()); + } + if (mProduceEtaMasks) { + resonanceProducts.producedEtaMasks(dau1.mask(), dau2.mask()); + } + } + } + + // cached kinematics of the resonance + float mPt = 0; + float mEta = 0; + float mPhi = 0; + float mMass = 0; + + float mMassMin = 0.f; + float mMassMax = 0.f; + float mPtMin = 0.f; + float mPtMax = 0.f; + float mEtaMin = 0.f; + float mEtaMax = 0.f; + float mPhiMin = 0.f; + float mPhiMax = 0.f; + + bool mFillAnyTable = false; + bool mProducePi0s = false; + bool mProducePi0Masks = false; + bool mProduceEtas = false; + bool mProduceEtaMasks = false; +}; + +} // namespace o2::analysis::femto::twophotonresonancebuilder +#endif // PWGCF_FEMTO_CORE_TWOPHOTONRESONANCEBUILDER_H_ diff --git a/PWGCF/Femto/Core/twoPhotonResonanceHistManager.h b/PWGCF/Femto/Core/twoPhotonResonanceHistManager.h new file mode 100644 index 00000000000..834317c1b03 --- /dev/null +++ b/PWGCF/Femto/Core/twoPhotonResonanceHistManager.h @@ -0,0 +1,207 @@ +// 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 twoPhotonResonanceHistManager.h +/// \brief histogram manager for resonances built from two photons (pi0, eta, ...) +/// \author Anton Riedel, TU München, anton.riedel@tum.de + +#ifndef PWGCF_FEMTO_CORE_TWOPHOTONRESONANCEHISTMANAGER_H_ +#define PWGCF_FEMTO_CORE_TWOPHOTONRESONANCEHISTMANAGER_H_ + +#include "PWGCF/Femto/Core/histManager.h" +#include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/Core/photonHistManager.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace o2::analysis::femto::twophotonresonancehistmanager +{ +enum TwoPhotonResonanceHist { + // analysis + kPt, + kEta, + kPhi, + kMass, + kPtVsMass, + // 2d qa + kPtVsEta, + kPtVsPhi, + kPhiVsEta, + kTwoPhotonResonanceHistLast +}; + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define TWOPHOTONRESONANCE_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"}; + +struct ConfPi0Binning : o2::framework::ConfigurableGroup { + std::string prefix = std::string("Pi0Binning"); + TWOPHOTONRESONANCE_DEFAULT_BINNING(0.f, 0.3f) +}; + +struct ConfEtaBinning : o2::framework::ConfigurableGroup { + std::string prefix = std::string("EtaBinning"); + TWOPHOTONRESONANCE_DEFAULT_BINNING(0.2f, 0.9f) +}; +#undef TWOPHOTONRESONANCE_DEFAULT_BINNING + +constexpr std::array, kTwoPhotonResonanceHistLast> HistTable = { + {{kPt, o2::framework::HistType::kTH1F, "hPt", "Transverse Momentum; p_{T} (GeV/#it{c}); Entries"}, + {kEta, o2::framework::HistType::kTH1F, "hEta", "Pseudorapdity; #eta; Entries"}, + {kPhi, o2::framework::HistType::kTH1F, "hPhi", "Azimuthal angle; #varphi; Entries"}, + {kMass, o2::framework::HistType::kTH1F, "hMass", "Invariant mass; m_{#gamma#gamma} (GeV/#it{c}^{2}); Entries"}, + {kPtVsMass, o2::framework::HistType::kTH2F, "hPtVsMass", "p_{T} vs invariant mass; p_{T} (GeV/#it{c}); m_{#gamma#gamma} (GeV/#it{c}^{2})"}, + {kPtVsEta, o2::framework::HistType::kTH2F, "hPtVsEta", "p_{T} vs #eta; p_{T} (GeV/#it{c}) ; #eta"}, + {kPtVsPhi, o2::framework::HistType::kTH2F, "hPtVsPhi", "p_{T} vs #varphi;p_{T} (GeV/#it{c});#varphi"}, + {kPhiVsEta, o2::framework::HistType::kTH2F, "hPhiVsEta", "#varphi vs #eta; #varphi ; #eta"}}, +}; + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define TWOPHOTONRESONANCE_HIST_ANALYSIS_MAP(conf) \ + {kPt, {(conf).pt}}, \ + {kEta, {(conf).eta}}, \ + {kPhi, {(conf).phi}}, \ + {kMass, {(conf).mass}}, \ + {kPtVsMass, {(conf).pt, (conf).mass}}, + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define TWOPHOTONRESONANCE_HIST_QA_MAP(conf) \ + {kPtVsEta, {(conf).pt, (conf).eta}}, \ + {kPtVsPhi, {(conf).pt, (conf).phi}}, \ + {kPhiVsEta, {(conf).phi, (conf).eta}}, \ + {kPtVsMass, {(conf).pt, (conf).mass}}, + +template +std::map> makeTwoPhotonResonanceHistSpecMap(const T& confBinningAnalysis) +{ + return std::map>{ + TWOPHOTONRESONANCE_HIST_ANALYSIS_MAP(confBinningAnalysis)}; +} + +template +auto makeTwoPhotonResonanceQaHistSpecMap(const T& confBinningAnalysis) +{ + return std::map>{ + TWOPHOTONRESONANCE_HIST_ANALYSIS_MAP(confBinningAnalysis) + TWOPHOTONRESONANCE_HIST_QA_MAP(confBinningAnalysis)}; +} + +#undef TWOPHOTONRESONANCE_HIST_ANALYSIS_MAP +#undef TWOPHOTONRESONANCE_HIST_QA_MAP + +constexpr char PrefixPi0[] = "Pi0/"; +constexpr char PrefixEta[] = "Eta/"; + +constexpr std::string_view AnalysisDir = "Analysis/"; +constexpr std::string_view QaDir = "QA/"; + +/// \class TwoPhotonResonanceHistManager +/// \brief Histogram manager for resonances built from two PCM photons (reco + QA only, no MC). +/// Both daughters share the same PhotonHistManager-based sub-manager treatment: there is no +/// pos/neg distinction, nor a PDG-dependent daughter lookup the way TwoTrackResonanceHistManager +/// needs (photon daughters are always massless, regardless of the mother species). +template +class TwoPhotonResonanceHistManager +{ + public: + TwoPhotonResonanceHistManager() = default; + ~TwoPhotonResonanceHistManager() = default; + + template + void init(o2::framework::HistogramRegistry* registry, + std::map> const& ResoSpecs, + std::map> const& DauSpecs, + T1 const& ConfDauQaBinning) + { + mHistogramRegistry = registry; + mDau1Manager.template init(registry, DauSpecs, ConfDauQaBinning); + mDau2Manager.template init(registry, DauSpecs, ConfDauQaBinning); + if constexpr (modes::isFlagSet(mode, modes::Mode::kReco)) { + initAnalysis(ResoSpecs); + } + if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { + initQa(ResoSpecs); + } + } + + template + void fill(T1 const& resonance, T2 const& photons) + { + auto dau1 = photons.rawIteratorAt(resonance.dau1Id() - photons.offset()); + mDau1Manager.template fill(dau1); + auto dau2 = photons.rawIteratorAt(resonance.dau2Id() - photons.offset()); + mDau2Manager.template fill(dau2); + if constexpr (modes::isFlagSet(mode, modes::Mode::kReco)) { + fillAnalysis(resonance); + } + if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { + fillQa(resonance); + } + } + + private: + void initAnalysis(std::map> const& ResoSpecs) + { + std::string analysisDir = std::string(resoPrefix) + std::string(AnalysisDir); + mHistogramRegistry->add(analysisDir + getHistNameV2(kPt, HistTable), getHistDesc(kPt, HistTable), getHistType(kPt, HistTable), {ResoSpecs.at(kPt)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kEta, HistTable), getHistDesc(kEta, HistTable), getHistType(kEta, HistTable), {ResoSpecs.at(kEta)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kPhi, HistTable), getHistDesc(kPhi, HistTable), getHistType(kPhi, HistTable), {ResoSpecs.at(kPhi)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kMass, HistTable), getHistDesc(kMass, HistTable), getHistType(kMass, HistTable), {ResoSpecs.at(kMass)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kPtVsMass, HistTable), getHistDesc(kPtVsMass, HistTable), getHistType(kPtVsMass, HistTable), {ResoSpecs.at(kPtVsMass)}); + } + + void initQa(std::map> const& ResoSpecs) + { + std::string qaDir = std::string(resoPrefix) + std::string(QaDir); + mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsEta, HistTable), getHistDesc(kPtVsEta, HistTable), getHistType(kPtVsEta, HistTable), {ResoSpecs.at(kPtVsEta)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsPhi, HistTable), getHistDesc(kPtVsPhi, HistTable), getHistType(kPtVsPhi, HistTable), {ResoSpecs.at(kPtVsPhi)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kPhiVsEta, HistTable), getHistDesc(kPhiVsEta, HistTable), getHistType(kPhiVsEta, HistTable), {ResoSpecs.at(kPhiVsEta)}); + } + + template + void fillAnalysis(T const& resonance) + { + 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()); + mHistogramRegistry->fill(HIST(resoPrefix) + HIST(AnalysisDir) + HIST(getHistName(kPtVsMass, HistTable)), resonance.pt(), resonance.mass()); + } + + template + void fillQa(T const& resonance) + { + 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()); + } + + o2::framework::HistogramRegistry* mHistogramRegistry = nullptr; + photonhistmanager::PhotonHistManager mDau1Manager; + photonhistmanager::PhotonHistManager mDau2Manager; +}; +} // namespace o2::analysis::femto::twophotonresonancehistmanager +#endif // PWGCF_FEMTO_CORE_TWOPHOTONRESONANCEHISTMANAGER_H_ diff --git a/PWGCF/Femto/Core/v0Builder.h b/PWGCF/Femto/Core/v0Builder.h index 6792d395071..bc894a8600b 100644 --- a/PWGCF/Femto/Core/v0Builder.h +++ b/PWGCF/Femto/Core/v0Builder.h @@ -715,7 +715,9 @@ class V0Builder v0.v0radius(), v0.x(), v0.y(), - v0.z()); + v0.z(), + v0.alpha(), + v0.qtarm()); } } @@ -754,7 +756,9 @@ class V0Builder v0.v0radius(), v0.x(), v0.y(), - v0.z()); + v0.z(), + v0.alpha(), + v0.qtarm()); } } diff --git a/PWGCF/Femto/Core/v0HistManager.h b/PWGCF/Femto/Core/v0HistManager.h index 4f55c529358..331d426893a 100644 --- a/PWGCF/Femto/Core/v0HistManager.h +++ b/PWGCF/Femto/Core/v0HistManager.h @@ -75,6 +75,7 @@ enum V0Hist { kK0shortMassVsAntiLambdaMass, kStrangeTofVsTofPosDau, kStrangeTofVsTofNegDau, + kArmenterosQtVsAlpha, // mc kOrigin, kPdg, @@ -145,6 +146,8 @@ struct ConfV0QaBinning : o2::framework::ConfigurableGroup { 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"}; o2::framework::ConfigurableAxis strangeTof{"strangeTof", {{500, -5, 5}}, "Strangeness TOF vs TOF Nsigma for daughters"}; + o2::framework::ConfigurableAxis armenterosAlpha{"armenterosAlpha", {{200, -1, 1}}, "Armenteros-Podolanski alpha"}; + o2::framework::ConfigurableAxis armenterosQt{"armenterosQt", {{150, 0, 0.3}}, "Armenteros-Podolanski qt (GeV/#it{c})"}; }; constexpr const char PrefixLambdaQaBinning1[] = "LambdaQaBinning1"; @@ -188,6 +191,7 @@ constexpr std::array, kV0HistLast> HistTable = { {kLambdaMassVsAntiLambdaMass, o2::framework::HistType::kTH2F, "hLambdaMassVsAntiLambdaMass", "#Lambda mass vs #bar{#Lambda}; m_{p#pi^{-}} (GeV/#it{c}^{2}); m_{#bar{p}#pi^{+}} (GeV/#it{c}^{2})"}, {kStrangeTofVsTofPosDau, o2::framework::HistType::kTH2F, "hStrangeTofVsTofPosDau", "TOF_{Strange} vs TOF_{Tracking} of positive Daughter; n#sigma_{TOF, strange}; n#sigma_{TOF, tracking}"}, {kStrangeTofVsTofNegDau, o2::framework::HistType::kTH2F, "hStrangeTofVsTofNegDau", "TOF_{Strange} vs TOF_{Tracking} of negative Daughter; n#sigma_{TOF, strange}; n#sigma_{TOF, tracking}"}, + {kArmenterosQtVsAlpha, o2::framework::HistType::kTH2F, "hArmenterosPodolanski", "Armenteros-Podolanski; #alpha; q_{T} (GeV/#it{c})"}, {kOrigin, o2::framework::HistType::kTH1F, "hOrigin", "Status Codes (=Origin); Status Code; Entries"}, {kPdg, o2::framework::HistType::kTH1F, "hPdg", "PDG Codes of reconstructed v0; PDG Code; Entries"}, {kPdgMother, o2::framework::HistType::kTH1F, "hPdgMother", "PDG Codes of mother of reconstructed v0; PDG Code; Entries"}, @@ -251,7 +255,8 @@ constexpr std::array, kV0HistLast> HistTable = { {kK0shortMassVsLambdaMass, {(confQa).massK0short, (confQa).massLambda}}, \ {kK0shortMassVsAntiLambdaMass, {(confQa).massK0short, (confQa).massAntiLambda}}, \ {kStrangeTofVsTofPosDau, {(confQa).strangeTof, (confQa).strangeTof}}, \ - {kStrangeTofVsTofNegDau, {(confQa).strangeTof, (confQa).strangeTof}}, + {kStrangeTofVsTofNegDau, {(confQa).strangeTof, (confQa).strangeTof}}, \ + {kArmenterosQtVsAlpha, {(confQa).armenterosAlpha, (confQa).armenterosQt}}, // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define V0_HIST_MC_QA_MAP(confAnalysis, confQa) \ @@ -518,6 +523,7 @@ class V0HistManager mHistogramRegistry->add(qaDir + getHistNameV2(kK0shortMassVsAntiLambdaMass, HistTable), getHistDesc(kK0shortMassVsAntiLambdaMass, HistTable), getHistType(kK0shortMassVsAntiLambdaMass, HistTable), {V0Specs.at(kK0shortMassVsAntiLambdaMass)}); mHistogramRegistry->add(qaDir + getHistNameV2(kStrangeTofVsTofPosDau, HistTable), getHistDesc(kStrangeTofVsTofPosDau, HistTable), getHistType(kStrangeTofVsTofPosDau, HistTable), {V0Specs.at(kStrangeTofVsTofPosDau)}); mHistogramRegistry->add(qaDir + getHistNameV2(kStrangeTofVsTofNegDau, HistTable), getHistDesc(kStrangeTofVsTofNegDau, HistTable), getHistType(kStrangeTofVsTofNegDau, HistTable), {V0Specs.at(kStrangeTofVsTofNegDau)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kArmenterosQtVsAlpha, HistTable), getHistDesc(kArmenterosQtVsAlpha, HistTable), getHistType(kArmenterosQtVsAlpha, HistTable), {V0Specs.at(kArmenterosQtVsAlpha)}); } } @@ -640,6 +646,7 @@ class V0HistManager mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kK0shortMassVsAntiLambdaMass, HistTable)), massK0short, massAntiLambda); mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kStrangeTofVsTofPosDau, HistTable)), v0candidate.strangeTofPosDau(), tofPosDau); mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kStrangeTofVsTofNegDau, HistTable)), v0candidate.strangeTofNegDau(), tofNegDau); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kArmenterosQtVsAlpha, HistTable)), v0candidate.alpha(), v0candidate.qtArm()); } } diff --git a/PWGCF/Femto/DataModel/FemtoTables.h b/PWGCF/Femto/DataModel/FemtoTables.h index 958fb9cb99d..89841e1b412 100644 --- a/PWGCF/Femto/DataModel/FemtoTables.h +++ b/PWGCF/Femto/DataModel/FemtoTables.h @@ -637,6 +637,8 @@ DECLARE_SOA_DYNAMIC_COLUMN(DecayVtx, decayVtx, //! distance of d [](float vtxX, float vtxY, float vtxZ) -> float { return std::hypot(vtxX, vtxY, vtxZ); }); +DECLARE_SOA_COLUMN(Alpha, alpha, float); //! Armenteros-Podolanski alpha +DECLARE_SOA_COLUMN(QtArm, qtArm, float); //! Armenteros-Podolanski qt // id columns for Lambda daughter tracks DECLARE_SOA_INDEX_COLUMN_FULL(PosDau, posDau, int32_t, FTracks, "_PosDau"); //! index column for positive daughter track @@ -740,6 +742,8 @@ DECLARE_SOA_TABLE_STAGED_VERSIONED(FLambdaExtras_001, "FLAMBDAEXTRA", 1, //! lam femtov0s::DecayVtxX, femtov0s::DecayVtxY, femtov0s::DecayVtxZ, + femtov0s::Alpha, + femtov0s::QtArm, femtov0s::DecayVtx, femtov0s::Pa); @@ -800,11 +804,155 @@ DECLARE_SOA_TABLE_STAGED_VERSIONED(FK0shortExtras_001, "FK0SHORTEXTRA", 1, //! k femtov0s::DecayVtxX, femtov0s::DecayVtxY, femtov0s::DecayVtxZ, + femtov0s::Alpha, + femtov0s::QtArm, femtov0s::DecayVtx, femtov0s::Pa); using FK0shortExtras = FK0shortExtras_001; +namespace femtophotons +{ +// columns for bit masks +DECLARE_SOA_COLUMN(Mask, mask, o2::analysis::femto::datatypes::PhotonMaskType); //! Bitmask for photon (PCM) selections + +// columns for debug information +DECLARE_SOA_COLUMN(CosPa, cosPa, float); //! Cosine of pointing angle of the V0 (conversion) to the primary vertex +DECLARE_SOA_DYNAMIC_COLUMN(Pa, pa, //! pointing angle + [](float cosPa) -> float { + return std::acos(cosPa); + }); +DECLARE_SOA_COLUMN(DcaToPvXY, dcaToPvXY, float); //! DCAxy of the photon (V0) to the primary vertex +DECLARE_SOA_COLUMN(DcaToPvZ, dcaToPvZ, float); //! DCAz of the photon (V0) to the primary vertex +DECLARE_SOA_COLUMN(Chi2Ndf, chi2Ndf, float); //! Chi2 / NDF of the reconstructed conversion vertex (KF) +DECLARE_SOA_COLUMN(V0Radius, v0Radius, float); //! Transverse radius of the conversion point +DECLARE_SOA_COLUMN(PosDauTpcNSigmaEl, posDauTpcNSigmaEl, float); //! TPC electron nSigma of the positive daughter +DECLARE_SOA_COLUMN(NegDauTpcNSigmaEl, negDauTpcNSigmaEl, float); //! TPC electron nSigma of the negative daughter +DECLARE_SOA_COLUMN(PosDauPt, posDauPt, float); //! pT of the positive daughter (at the conversion point) +DECLARE_SOA_COLUMN(NegDauPt, negDauPt, float); //! pT of the negative daughter (at the conversion point) +DECLARE_SOA_COLUMN(PosDauTpcInnerParam, posDauTpcInnerParam, float); //! momentum of the positive daughter at the inner wall of the TPC +DECLARE_SOA_COLUMN(NegDauTpcInnerParam, negDauTpcInnerParam, float); //! momentum of the negative daughter at the inner wall of the TPC +DECLARE_SOA_COLUMN(PosDauTpcSignal, posDauTpcSignal, float); //! TPC dE/dx of the positive daughter +DECLARE_SOA_COLUMN(NegDauTpcSignal, negDauTpcSignal, float); //! TPC dE/dx of the negative daughter +DECLARE_SOA_COLUMN(DecayVtxX, decayVtxX, float); //! x coordinate of conversion point +DECLARE_SOA_COLUMN(DecayVtxY, decayVtxY, float); //! y coordinate of conversion point +DECLARE_SOA_COLUMN(DecayVtxZ, decayVtxZ, float); //! z coordinate of conversion point +DECLARE_SOA_DYNAMIC_COLUMN(DecayVtx, decayVtx, //! distance of conversion point from nominal interaction point + [](float vtxX, float vtxY, float vtxZ) -> float { + return std::hypot(vtxX, vtxY, vtxZ); + }); +} // namespace femtophotons + +// table for basic PCM photon information (mass is fixed to 0 by construction, not stored) +DECLARE_SOA_TABLE_STAGED_VERSIONED(FPhotons_001, "FPHOTON", 1, //! femto PCM photons + o2::soa::Index<>, + femtobase::stored::FColId, + femtobase::stored::Pt, + femtobase::stored::Eta, + femtobase::stored::Phi, + femtobase::dynamic::P, + femtobase::dynamic::Px, + femtobase::dynamic::Py, + femtobase::dynamic::Pz, + femtobase::dynamic::Theta); +using FPhotons = FPhotons_001; +using StoredFPhotons = StoredFPhotons_001; + +// table for basic PCM photon information with compressed/binned kinematics +DECLARE_SOA_TABLE_STAGED_VERSIONED(FLitePhotons_001, "FLITEPHOTON", 1, + o2::soa::Index<>, + femtobase::stored::FLiteColId, + femtobase::lite::UnsignedBinnedPt, + femtobase::lite::BinnedEta, + femtobase::lite::BinnedPhi, + femtobase::lite::unsignedpt::Pt, + femtobase::lite::Eta, + femtobase::lite::Phi); +using FLitePhotons = FLitePhotons_001; +using FLitePhoton = FLitePhotons::iterator; +using StoredFLitePhotons = StoredFLitePhotons_001; + +DECLARE_SOA_TABLE_STAGED_VERSIONED(FPhotonMasks_001, "FPHOTONMASK", 1, //! photon (PCM) masks + femtophotons::Mask); +using FPhotonMasks = FPhotonMasks_001; +using StoredFPhotonMasks = StoredFPhotonMasks_001; + +DECLARE_SOA_TABLE_STAGED_VERSIONED(FPhotonExtras_001, "FPHOTONEXTRA", 1, //! photon (PCM) extra information + femtophotons::CosPa, + femtophotons::DcaToPvXY, + femtophotons::DcaToPvZ, + femtophotons::Chi2Ndf, + femtophotons::V0Radius, + femtophotons::PosDauPt, + femtophotons::NegDauPt, + femtophotons::PosDauTpcInnerParam, + femtophotons::NegDauTpcInnerParam, + femtophotons::PosDauTpcSignal, + femtophotons::NegDauTpcSignal, + femtophotons::PosDauTpcNSigmaEl, + femtophotons::NegDauTpcNSigmaEl, + femtophotons::DecayVtxX, + femtophotons::DecayVtxY, + femtophotons::DecayVtxZ, + femtophotons::DecayVtx, + femtophotons::Pa); +using FPhotonExtras = FPhotonExtras_001; +using StoredFPhotonExtras = StoredFPhotonExtras_001; + +namespace femtotwophotonresonances +{ +// columns for daughter (photon) bit masks -- no momentum-threshold bit: photon daughters have no +// momentum-dependent PID switch the way charged-track daughters do in femtotwotrackresonances +DECLARE_SOA_COLUMN(MaskDau1, maskDau1, o2::analysis::femto::datatypes::PhotonMaskType); //! Bitmask for first photon daughter +DECLARE_SOA_COLUMN(MaskDau2, maskDau2, o2::analysis::femto::datatypes::PhotonMaskType); //! Bitmask for second photon daughter + +// id columns for the two photon daughters (unordered, no pos/neg distinction) +DECLARE_SOA_INDEX_COLUMN_FULL(Dau1, dau1, int32_t, FPhotons, "_Dau1"); //! index column for first photon daughter +DECLARE_SOA_INDEX_COLUMN_FULL(Dau2, dau2, int32_t, FPhotons, "_Dau2"); //! index column for second photon daughter +} // namespace femtotwophotonresonances + +// table for pi0s (-> gamma gamma) +DECLARE_SOA_TABLE_STAGED_VERSIONED(FPi0s_001, "FPI0", 1, //! femto pi0s + o2::soa::Index<>, + femtobase::stored::FColId, + femtobase::stored::Pt, + femtobase::stored::Eta, + femtobase::stored::Phi, + femtobase::stored::Mass, + femtotwophotonresonances::Dau1Id, + femtotwophotonresonances::Dau2Id, + femtobase::dynamic::P, + femtobase::dynamic::Px, + femtobase::dynamic::Py, + femtobase::dynamic::Pz, + femtobase::dynamic::Theta); +using FPi0s = FPi0s_001; +DECLARE_SOA_TABLE_STAGED_VERSIONED(FPi0Masks_001, "FPI0MASK", 1, //! mask for pi0s + femtotwophotonresonances::MaskDau1, + femtotwophotonresonances::MaskDau2); +using FPi0Masks = FPi0Masks_001; + +// table for etas (-> gamma gamma) +DECLARE_SOA_TABLE_STAGED_VERSIONED(FEtas_001, "FETA", 1, //! femto etas + o2::soa::Index<>, + femtobase::stored::FColId, + femtobase::stored::Pt, + femtobase::stored::Eta, + femtobase::stored::Phi, + femtobase::stored::Mass, + femtotwophotonresonances::Dau1Id, + femtotwophotonresonances::Dau2Id, + femtobase::dynamic::P, + femtobase::dynamic::Px, + femtobase::dynamic::Py, + femtobase::dynamic::Pz, + femtobase::dynamic::Theta); +using FEtas = FEtas_001; +DECLARE_SOA_TABLE_STAGED_VERSIONED(FEtaMasks_001, "FETAMASK", 1, //! mask for etas + femtotwophotonresonances::MaskDau1, + femtotwophotonresonances::MaskDau2); +using FEtaMasks = FEtaMasks_001; + namespace femtokinks { // columns for bit masks diff --git a/PWGCF/Femto/TableProducer/CMakeLists.txt b/PWGCF/Femto/TableProducer/CMakeLists.txt index 70caef3c49c..91add294624 100644 --- a/PWGCF/Femto/TableProducer/CMakeLists.txt +++ b/PWGCF/Femto/TableProducer/CMakeLists.txt @@ -11,7 +11,7 @@ o2physics_add_dpl_workflow(femto-producer SOURCES ./femtoProducer.cxx - PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils O2Physics::PWGEMPhotonMesonCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(femto-producer-derived-to-derived @@ -38,3 +38,8 @@ o2physics_add_dpl_workflow(femto-producer-resonances SOURCES ./femtoProducerResonances.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(femto-producer-two-photon-resonances + SOURCES ./femtoProducerTwoPhotonResonances.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) diff --git a/PWGCF/Femto/TableProducer/femtoProducer.cxx b/PWGCF/Femto/TableProducer/femtoProducer.cxx index 0053724e5a9..6fde51df1f2 100644 --- a/PWGCF/Femto/TableProducer/femtoProducer.cxx +++ b/PWGCF/Femto/TableProducer/femtoProducer.cxx @@ -19,8 +19,10 @@ #include "PWGCF/Femto/Core/kinkBuilder.h" #include "PWGCF/Femto/Core/mcBuilder.h" #include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/Core/photonBuilder.h" #include "PWGCF/Femto/Core/trackBuilder.h" #include "PWGCF/Femto/Core/v0Builder.h" +#include "PWGEM/PhotonMeson/DataModel/gammaTables.h" #include "PWGHF/DataModel/CandidateReconstructionTables.h" #include "PWGHF/DataModel/CandidateSelectionTables.h" #include "PWGLF/DataModel/LFKinkDecayTables.h" @@ -101,6 +103,7 @@ struct FemtoProducer { o2::framework::Preslice perMcCollision = o2::aod::mcparticle::mcCollisionId; o2::framework::Preslice perColRecoTracks = o2::aod::track::collisionId; o2::framework::Preslice perColRecoV0s = o2::aod::v0data::collisionId; + o2::framework::Preslice perColRecoPCMPhotons = o2::aod::v0photonkf::collisionId; o2::framework::Preslice perColRecoCascades = o2::aod::cascdata::collisionId; o2::framework::Preslice perColRecoKinks = o2::aod::kinkcand::collisionId; o2::framework::Preslice perColRecoD0s = o2::aod::hf_cand::collisionId; @@ -134,6 +137,13 @@ struct FemtoProducer { v0builder::V0Builder lambdaBuilder; v0builder::V0Builder antilambdaBuilder; + // photon (PCM) builder + photonbuilder::PhotonBuilderProducts photonBuilderProducts; + photonbuilder::ConfPhotonTables confPhotonTables; + photonbuilder::ConfPhotonFilters confPhotonFilters; + photonbuilder::ConfPhotonBits confPhotonBits; + photonbuilder::PhotonBuilder photonBuilder; + // charm hadron builder charmhadronbuilder::CharmHadronBuilderProducts charmHadronBuilderProducts; charmhadronbuilder::ConfCharmHadronFilters confCharmHadronFilters; @@ -198,6 +208,8 @@ struct FemtoProducer { lambdaBuilder.init(&hRegistry, confLambdaBits, confV0Filters, confV0Tables, context, trackBuilder); antilambdaBuilder.init(&hRegistry, confLambdaBits, confV0Filters, confV0Tables, context, trackBuilder); + photonBuilder.init(&hRegistry, confPhotonBits, confPhotonFilters, confPhotonTables, context); + d0Builder.init(&hRegistry, confD0Bits, confCharmHadronFilters, confCharmHadronTables, context); d0barBuilder.init(&hRegistry, confD0Bits, confCharmHadronFilters, confCharmHadronTables, context); @@ -229,6 +241,9 @@ struct FemtoProducer { !doprocessTracksV0sKinksRun3ppMc)) { LOG(fatal) << "At least one v0 table is enabled, but wrong process function is enabled. Breaking..."; } + if (photonBuilder.fillAnyTable() && (!doprocessTracksPhotonsRun3pp && !doprocessTracksPhotonsRun3PbPb)) { + LOG(fatal) << "At least one photon table is enabled, but wrong process function is enabled. Breaking..."; + } if ((sigmaBuilder.fillAnyTable() || sigmaPlusBuilder.fillAnyTable()) && (!doprocessTracksKinksRun3pp && !doprocessTracksV0sCascadesKinksRun3pp && !doprocessTracksKinksRun3ppMc && !doprocessTracksV0sKinksRun3ppMc)) { @@ -269,6 +284,7 @@ struct FemtoProducer { add("k0short", k0shortBuilder.fillAnyTable(), k0shortBuilder.isPassThrough()); add("lambda", lambdaBuilder.fillAnyTable(), lambdaBuilder.isPassThrough()); add("antilambda", antilambdaBuilder.fillAnyTable(), antilambdaBuilder.isPassThrough()); + add("photon", photonBuilder.fillAnyTable(), photonBuilder.isPassThrough()); add("d0", d0Builder.fillAnyTable(), d0Builder.isPassThrough()); add("d0bar", d0barBuilder.fillAnyTable(), d0barBuilder.isPassThrough()); add("lc", lcBuilder.fillAnyTable(), lcBuilder.isPassThrough()); @@ -322,6 +338,8 @@ struct FemtoProducer { static_cast(doprocessTracksRun3PbPb) + static_cast(doprocessTracksRun3PbPbWithEventShape) + static_cast(doprocessTracksV0sRun3pp) + + static_cast(doprocessTracksPhotonsRun3pp) + + static_cast(doprocessTracksPhotonsRun3PbPb) + static_cast(doprocessTracksV0sRun3PbPb) + static_cast(doprocessTracksV0sCascadesRun3pp) + static_cast(doprocessTracksV0sCascadesRun3PbPb) + @@ -424,6 +442,13 @@ struct FemtoProducer { k0shortBuilder.fillMcV0s(col, collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, v0builderProducts, v0s, tracks, trackBuilder, mcParticles, mcBuilder, mcProducts); } + // processing photons (PCM) + template + void processPhotons(T1 const& col, T2 const& photons, T3 const& v0legs) + { + photonBuilder.fillPhotons(col, collisionBuilder, collisionBuilderProducts, photonBuilderProducts, photons, v0legs); + } + // processing kinks template void processKinks(T1 const& col, T2 const& tracks, T3 const& kinks) @@ -540,6 +565,42 @@ struct FemtoProducer { } PROCESS_SWITCH(FemtoProducer, processTracksV0sRun3pp, "Provide tracks and v0s", false); + void processTracksPhotonsRun3pp(rawinputs::Run3PpCollisions::iterator const& col, + o2::aod::BCsWithTimestamps const& bcs, + rawinputs::Run3FullPidTracks const& tracks, + o2::aod::V0PhotonsKF const& photons, + o2::aod::V0Legs const& v0legs) + { + if (!processCollisions(col, bcs, tracks)) { + return; + } + auto tracksWithItsPid = o2::soa::Attach(tracks); + processTracks(col, tracksWithItsPid); + // V0PhotonsKF/V0Legs carry a plain CollisionId column (not a framework index column), so they + // are not auto-grouped by the DPL binder and must be sliced manually, same as EMPhotonFilter.cxx + auto photonsThisCol = photons.sliceBy(perColRecoPCMPhotons, col.globalIndex()); + processPhotons(col, photonsThisCol, v0legs); + } + PROCESS_SWITCH(FemtoProducer, processTracksPhotonsRun3pp, "Provide tracks and PCM photons", false); + + void processTracksPhotonsRun3PbPb(rawinputs::Run3PbPbCollisions::iterator const& col, + o2::aod::BCsWithTimestamps const& bcs, + rawinputs::Run3FullPidTracks const& tracks, + o2::aod::V0PhotonsKF const& photons, + o2::aod::V0Legs const& v0legs) + { + if (!processCollisions(col, bcs, tracks)) { + return; + } + auto tracksWithItsPid = o2::soa::Attach(tracks); + processTracks(col, tracksWithItsPid); + auto photonsThisCol = photons.sliceBy(perColRecoPCMPhotons, col.globalIndex()); + processPhotons(col, photonsThisCol, v0legs); + } + PROCESS_SWITCH(FemtoProducer, processTracksPhotonsRun3PbPb, "Provide tracks and PCM photons in PbPb collisions", false); + void processTracksV0sRun3PbPb(rawinputs::Run3PbPbCollisions::iterator const& col, o2::aod::BCsWithTimestamps const& bcs, rawinputs::Run3FullPidTracks const& tracks, diff --git a/PWGCF/Femto/TableProducer/femtoProducerTwoPhotonResonances.cxx b/PWGCF/Femto/TableProducer/femtoProducerTwoPhotonResonances.cxx new file mode 100644 index 00000000000..4752d5f3a54 --- /dev/null +++ b/PWGCF/Femto/TableProducer/femtoProducerTwoPhotonResonances.cxx @@ -0,0 +1,88 @@ +// 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 femtoProducerTwoPhotonResonances.cxx +/// \brief Tasks that produces femto tables for resonances built from two PCM photons (pi0, eta, ...) +/// \author Anton Riedel, TU München, anton.riedel@tum.de + +#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/photonBuilder.h" +#include "PWGCF/Femto/Core/twoPhotonResonanceBuilder.h" +#include "PWGCF/Femto/DataModel/FemtoTables.h" + +#include +#include +#include +#include +#include +#include + +using namespace o2::analysis::femto; + +struct FemtoProducerTwoPhotonResonances { + + using FemtoCollisions = o2::soa::Join; + using FilteredFemtoCollisions = o2::soa::Filtered; + using FilteredFemtoCollision = FilteredFemtoCollisions::iterator; + + using FemtoPhotons = o2::soa::Join; + + o2::framework::SliceCache cache; + + // setup collisions + collisionbuilder::ConfCollisionSelection collisionSelection; + o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); + colhistmanager::ConfCollisionBinning confCollisionBinning; + + // setup for resonance daughter photons + photonbuilder::ConfPhotonSelection1 confPhotonSelection; + o2::framework::Partition photonPartition = MAKE_PHOTON_PARTITION(confPhotonSelection); + o2::framework::Preslice perColPhotons = o2::aod::femtobase::stored::fColId; + + // resonance filters + twophotonresonancebuilder::ConfPi0Filters confPi0Filter; + twophotonresonancebuilder::ConfEtaFilters confEtaFilter; + + // resonance builders + twophotonresonancebuilder::ConfTwoPhotonResonanceTables confTwoPhotonResonanceTables; + twophotonresonancebuilder::TwoPhotonResonanceBuilderProducts twoPhotonResonanceBuilderProducts; + twophotonresonancebuilder::TwoPhotonResonanceBuilder pi0Builder; + twophotonresonancebuilder::TwoPhotonResonanceBuilder etaBuilder; + + void init(o2::framework::InitContext& context) + { + // init builders + pi0Builder.init(confPi0Filter, confTwoPhotonResonanceTables, context); + etaBuilder.init(confEtaFilter, confTwoPhotonResonanceTables, context); + } + + // process functions + void processPi0(FilteredFemtoCollision const& col, FemtoPhotons const& /*photons*/) + { + pi0Builder.fillResonances(col, twoPhotonResonanceBuilderProducts, photonPartition, cache); + } + PROCESS_SWITCH(FemtoProducerTwoPhotonResonances, processPi0, "Build Pi0 candidates", true); + + void processEta(FilteredFemtoCollision const& col, FemtoPhotons const& /*photons*/) + { + etaBuilder.fillResonances(col, twoPhotonResonanceBuilderProducts, photonPartition, cache); + } + PROCESS_SWITCH(FemtoProducerTwoPhotonResonances, processEta, "Build Eta candidates", true); +}; + +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) +{ + o2::framework::WorkflowSpec workflow{adaptAnalysisTask(context)}; + return workflow; +} diff --git a/PWGCF/Femto/Tasks/CMakeLists.txt b/PWGCF/Femto/Tasks/CMakeLists.txt index 0aa26cf38d7..6d4bb461d62 100644 --- a/PWGCF/Femto/Tasks/CMakeLists.txt +++ b/PWGCF/Femto/Tasks/CMakeLists.txt @@ -19,11 +19,21 @@ o2physics_add_dpl_workflow(femto-twotrackresonance-qa PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(femto-two-photon-resonance-qa + SOURCES femtoTwoPhotonResonanceQa.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-photon-qa + SOURCES femtoPhotonQa.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(femto-kink-qa SOURCES femtoKinkQa.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore diff --git a/PWGCF/Femto/Tasks/femtoPhotonQa.cxx b/PWGCF/Femto/Tasks/femtoPhotonQa.cxx new file mode 100644 index 00000000000..ad6adbbeebf --- /dev/null +++ b/PWGCF/Femto/Tasks/femtoPhotonQa.cxx @@ -0,0 +1,101 @@ +// 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 femtoPhotonQa.cxx +/// \brief QA task for photons (PCM) +/// \author Anton Riedel, TU München, anton.riedel@tum.de + +#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/photonBuilder.h" +#include "PWGCF/Femto/Core/photonHistManager.h" +#include "PWGCF/Femto/DataModel/FemtoTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace o2::analysis::femto; + +struct FemtoPhotonQa { + + // setup tables + using FemtoCollisions = o2::soa::Join; + using FilteredFemtoCollisions = o2::soa::Filtered; + using FilteredFemtoCollision = FilteredFemtoCollisions::iterator; + + using FemtoPhotons = o2::soa::Join; + + o2::framework::SliceCache cache; + + // setup for collisions + collisionbuilder::ConfCollisionSelection collisionSelection; + o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); + colhistmanager::CollisionHistManager colHistManager; + colhistmanager::ConfCollisionBinning confCollisionBinning; + colhistmanager::ConfCollisionQaBinning confCollisionQaBinning; + + // setup for photons + photonbuilder::ConfPhotonSelection1 confPhotonSelection; + + o2::framework::Partition photonPartition = MAKE_PHOTON_PARTITION(confPhotonSelection); + o2::framework::Preslice perColPhotons = o2::aod::femtobase::stored::fColId; + + photonhistmanager::ConfPhotonBinning confPhotonBinning; + photonhistmanager::ConfPhotonQaBinning confPhotonQaBinning; + photonhistmanager::PhotonHistManager photonHistManager; + + o2::framework::HistogramRegistry hRegistry{"FemtoPhotonQa", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; + + void init(o2::framework::InitContext&) + { + auto colHistSpec = colhistmanager::makeColQaHistSpecMap(confCollisionBinning, confCollisionQaBinning); + colHistManager.init(&hRegistry, colHistSpec, confCollisionBinning, confCollisionQaBinning); + + auto photonHistSpec = photonhistmanager::makePhotonQaHistSpecMap(confPhotonBinning, confPhotonQaBinning); + photonHistManager.init(&hRegistry, photonHistSpec, confPhotonQaBinning); + + hRegistry.print(); + } + + void processPhotons(FilteredFemtoCollision const& col, FemtoPhotons const& /*photons*/) + { + auto photonSlice = photonPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + if (photonSlice.size() == 0) { + return; + } + colHistManager.fill(col); + for (auto const& photon : photonSlice) { + photonHistManager.fill(photon); + } + } + PROCESS_SWITCH(FemtoPhotonQa, processPhotons, "Process photons", true); +}; + +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) +{ + o2::framework::WorkflowSpec workflow{ + adaptAnalysisTask(context), + }; + return workflow; +} diff --git a/PWGCF/Femto/Tasks/femtoTwoPhotonResonanceQa.cxx b/PWGCF/Femto/Tasks/femtoTwoPhotonResonanceQa.cxx new file mode 100644 index 00000000000..afcf432c8e4 --- /dev/null +++ b/PWGCF/Femto/Tasks/femtoTwoPhotonResonanceQa.cxx @@ -0,0 +1,143 @@ +// 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 femtoTwoPhotonResonanceQa.cxx +/// \brief QA task for resonances built from two photons (pi0, eta, ...) +/// \author Anton Riedel, TU München, anton.riedel@tum.de + +#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/photonHistManager.h" +#include "PWGCF/Femto/Core/twoPhotonResonanceBuilder.h" +#include "PWGCF/Femto/Core/twoPhotonResonanceHistManager.h" +#include "PWGCF/Femto/DataModel/FemtoTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2::analysis::femto; + +struct FemtoTwoPhotonResonanceQa { + + // setup tables + using FemtoCollisions = o2::soa::Join; + using FilteredFemtoCollisions = o2::soa::Filtered; + using FilteredFemtoCollision = FilteredFemtoCollisions::iterator; + + using FemtoPi0s = o2::soa::Join; + using FemtoEtas = o2::soa::Join; + using FemtoPhotons = o2::soa::Join; + + o2::framework::SliceCache cache; + + // setup for collisions + collisionbuilder::ConfCollisionSelection collisionSelection; + o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); + colhistmanager::CollisionHistManager colHistManager; + colhistmanager::ConfCollisionBinning confCollisionBinning; + colhistmanager::ConfCollisionQaBinning confCollisionQaBinning; + + // setup for pi0s + twophotonresonancebuilder::ConfPi0Selection confPi0Selection; + o2::framework::Partition pi0Partition = MAKE_TWOPHOTONRESONANCE_PARTITION(confPi0Selection); + o2::framework::Preslice perColPi0s = o2::aod::femtobase::stored::fColId; + + twophotonresonancehistmanager::ConfPi0Binning confPi0Binning; + twophotonresonancehistmanager::TwoPhotonResonanceHistManager< + twophotonresonancehistmanager::PrefixPi0, + photonhistmanager::PrefixTwoPhotonResonanceDau1Qa, + photonhistmanager::PrefixTwoPhotonResonanceDau2Qa, + modes::TwoPhotonResonance::kPi0> + pi0HistManager; + + // setup for etas + twophotonresonancebuilder::ConfEtaSelection confEtaSelection; + o2::framework::Partition etaPartition = MAKE_TWOPHOTONRESONANCE_PARTITION(confEtaSelection); + o2::framework::Preslice perColEtas = o2::aod::femtobase::stored::fColId; + + twophotonresonancehistmanager::ConfEtaBinning confEtaBinning; + twophotonresonancehistmanager::TwoPhotonResonanceHistManager< + twophotonresonancehistmanager::PrefixEta, + photonhistmanager::PrefixTwoPhotonResonanceDau1Qa, + photonhistmanager::PrefixTwoPhotonResonanceDau2Qa, + modes::TwoPhotonResonance::kEta> + etaHistManager; + + // setup for daughters (shared between pi0 and eta -- unordered, no pos/neg or species asymmetry) + photonhistmanager::ConfPhotonBinning confPhotonDauBinning; + photonhistmanager::ConfPhotonQaBinning confPhotonDauQaBinning; + + o2::framework::HistogramRegistry hRegistry{"FemtoTwoPhotonResonanceQa", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; + + void init(o2::framework::InitContext&) + { + auto colHistSpec = colhistmanager::makeColQaHistSpecMap(confCollisionBinning, confCollisionQaBinning); + colHistManager.init(&hRegistry, colHistSpec, confCollisionBinning, confCollisionQaBinning); + + auto dauHistSpec = photonhistmanager::makePhotonQaHistSpecMap(confPhotonDauBinning, confPhotonDauQaBinning); + + if ((static_cast(doprocessPi0s) + static_cast(doprocessEtas)) > 1) { + LOG(fatal) << "Only one process can be activated"; + } + + if (doprocessPi0s) { + auto pi0HistSpec = twophotonresonancehistmanager::makeTwoPhotonResonanceQaHistSpecMap(confPi0Binning); + pi0HistManager.init(&hRegistry, pi0HistSpec, dauHistSpec, confPhotonDauQaBinning); + } + if (doprocessEtas) { + auto etaHistSpec = twophotonresonancehistmanager::makeTwoPhotonResonanceQaHistSpecMap(confEtaBinning); + etaHistManager.init(&hRegistry, etaHistSpec, dauHistSpec, confPhotonDauQaBinning); + } + } + + void processPi0s(FilteredFemtoCollision const& col, FemtoPi0s const& /*pi0s*/, FemtoPhotons const& photons) + { + auto pi0Slice = pi0Partition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + if (pi0Slice.size() == 0) { + return; + } + colHistManager.fill(col); + for (auto const& pi0 : pi0Slice) { + pi0HistManager.fill(pi0, photons); + } + } + PROCESS_SWITCH(FemtoTwoPhotonResonanceQa, processPi0s, "Process Pi0s", true); + + void processEtas(FilteredFemtoCollision const& col, FemtoEtas const& /*etas*/, FemtoPhotons const& photons) + { + auto etaSlice = etaPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + if (etaSlice.size() == 0) { + return; + } + colHistManager.fill(col); + for (auto const& eta : etaSlice) { + etaHistManager.fill(eta, photons); + } + } + PROCESS_SWITCH(FemtoTwoPhotonResonanceQa, processEtas, "Process Etas", false); +}; + +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) +{ + o2::framework::WorkflowSpec workflow{ + adaptAnalysisTask(context), + }; + return workflow; +}