diff --git a/PWGCF/Femto/Core/dataTypes.h b/PWGCF/Femto/Core/dataTypes.h index bbda8a76e93..d96d1cf69bf 100644 --- a/PWGCF/Femto/Core/dataTypes.h +++ b/PWGCF/Femto/Core/dataTypes.h @@ -50,6 +50,9 @@ using TwoTrackResonanceType = uint16_t; // datatype for resonances built from two photons (pi0, eta, ...) using TwoPhotonResonanceType = uint16_t; +// datatype for resonances built from two tracks and a two-photon resonance (e.g. omega(782) -> pi+ pi- pi0) +using TrackTrackTwoPhotonResonanceType = uint8_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 e4306101980..e0a70e644ec 100644 --- a/PWGCF/Femto/Core/modes.h +++ b/PWGCF/Femto/Core/modes.h @@ -241,6 +241,12 @@ enum class TwoPhotonResonance : o2::analysis::femto::datatypes::TwoPhotonResonan kEta }; +// resonances reconstructed from two tracks and a two-photon resonance, e.g. omega(782) -> pi+ pi- pi0. +// Named kOmegaMeson (not kOmega) to not collide with Cascade::kOmega, the Omega- baryon. +enum class TrackTrackTwoPhotonResonance : o2::analysis::femto::datatypes::TrackTrackTwoPhotonResonanceType { + kOmegaMeson +}; + 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 ad82cde98ea..6900186a342 100644 --- a/PWGCF/Femto/Core/partitions.h +++ b/PWGCF/Femto/Core/partitions.h @@ -95,19 +95,42 @@ 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) +// there is no sign and no momentum-threshold PID switch, and just a single mask check -- both +// daughters are plain photons with the same selection requirement, so the candidate only stores the +// AND of both legs' own masks (see femtotwophotonresonances::MaskDau) +// 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::maskDau, (selection).dauMask) + +// partition for resonances built from two tracks and a two-photon resonance (e.g. omega(782) -> +// pi+ pi- pi0): same pattern as MAKE_RESONANCE_1_PARTITON (daughter masks copied onto the candidate +// at build time, systematics reselected here), just with 3 daughter masks instead of 2 -- the two +// charged daughters keep their momentum-threshold PID switch, the neutral daughter does not +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define MAKE_TRACKTRACKTWOPHOTONRESONANCE_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) && \ + ifnode(o2::aod::femtotrackstwophotonresonances::posDauHasHighMomentum, \ + ncheckbit(o2::aod::femtotrackstwophotonresonances::maskPosDau, (selection).posDauMaskAboveThres), \ + ncheckbit(o2::aod::femtotrackstwophotonresonances::maskPosDau, (selection).posDauMaskBelowThres)) && \ + ifnode(o2::aod::femtotrackstwophotonresonances::negDauHasHighMomentum, \ + ncheckbit(o2::aod::femtotrackstwophotonresonances::maskNegDau, (selection).negDauMaskAboveThres), \ + ncheckbit(o2::aod::femtotrackstwophotonresonances::maskNegDau, (selection).negDauMaskBelowThres)) && \ + ncheckbit(o2::aod::femtotrackstwophotonresonances::maskNeutralDau, (selection).neutralDauMask) // partition for lambdas // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) diff --git a/PWGCF/Femto/Core/trackTrackTwoPhotonResonanceBuilder.h b/PWGCF/Femto/Core/trackTrackTwoPhotonResonanceBuilder.h new file mode 100644 index 00000000000..2e280ea1365 --- /dev/null +++ b/PWGCF/Femto/Core/trackTrackTwoPhotonResonanceBuilder.h @@ -0,0 +1,236 @@ +// Copyright 2019-2026 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 trackTrackTwoPhotonResonanceBuilder.h +/// \brief builder for resonances reconstructed from two tracks and a two-photon resonance +/// (e.g. omega(782) -> pi+ pi- pi0) +/// \author Anton Riedel, TU München, anton.riedel@tum.de + +#ifndef PWGCF_FEMTO_CORE_TRACKTRACKTWOPHOTONRESONANCEBUILDER_H_ +#define PWGCF_FEMTO_CORE_TRACKTRACKTWOPHOTONRESONANCEBUILDER_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 + +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include + +#include + +namespace o2::analysis::femto::trackstwophotonresonancebuilder +{ +// filters applied in the producer task (loose pre-selection, applied before the mass window itself) +struct ConfOmegaMesonFilters : o2::framework::ConfigurableGroup { + std::string prefix = std::string("OmegaMesonFilters"); + 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.6f, "Minimum invariant mass"}; + o2::framework::Configurable massMax{"massMax", 0.95f, "Maximum invariant mass"}; +}; + +// selection used downstream (QA/pairing) to re-select already produced resonances by mass window +// and daughter mask -- same pattern as the two-track resonances (TWOTRACKRESONANCE_DEFAULT_SELECTION), +// just with a 3rd mask for the neutral daughter (no momentum-threshold split there, same as +// femtotwophotonresonances' single dauMask) +template +struct ConfOmegaMesonSelection : o2::framework::ConfigurableGroup { + std::string prefix = Prefix; + o2::framework::Configurable pdgCodeAbs{"pdgCodeAbs", o2::constants::physics::Pdg::kOmega, "Absolute PDG code of the resonance (pdg[omega(782)]=223)"}; + 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.6f, "Minimum invariant mass"}; + o2::framework::Configurable massMax{"massMax", 0.95f, "Maximum invariant mass"}; + o2::framework::Configurable posDauMaskBelowThres{"posDauMaskBelowThres", 0x10u, "Bitmask for positive daughter below threshold"}; + o2::framework::Configurable posDauMaskAboveThres{"posDauMaskAboveThres", 0x8u, "Bitmask for positive daughter above threshold"}; + o2::framework::Configurable negDauMaskBelowThres{"negDauMaskBelowThres", 0x2u, "Bitmask for negative daughter below threshold"}; + o2::framework::Configurable negDauMaskAboveThres{"negDauMaskAboveThres", 0x1u, "Bitmask for negative daughter above threshold"}; + o2::framework::Configurable neutralDauMask{"neutralDauMask", 0, "Bitmask required for the neutral (two-photon-resonance) daughter"}; +}; + +constexpr const char PrefixOmegaMesonSelection1[] = "OmegaMesonSelection1"; +using ConfOmegaMesonSelection1 = ConfOmegaMesonSelection; + +struct TrackTrackTwoPhotonResonanceBuilderProducts : o2::framework::ProducesGroup { + o2::framework::Produces producedOmegaMesons; + o2::framework::Produces producedOmegaMesonMasks; +}; + +struct ConfTrackTrackTwoPhotonResonanceTables : o2::framework::ConfigurableGroup { + std::string prefix = std::string("TrackTrackTwoPhotonResonanceTables"); + o2::framework::Configurable produceOmegaMesons{"produceOmegaMesons", -1, "Produce OmegaMesons (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceOmegaMesonMasks{"produceOmegaMesonMasks", -1, "Produce OmegaMesonMasks (-1: auto; 0 off; 1 on)"}; +}; + +/// \brief Builder for resonances reconstructed from two tracks and a two-photon resonance +/// (e.g. omega(782) -> pi+ pi- pi0). +/// +/// posDau/negDau are drawn from already-selected FTracks partitions (sign and PID handled by the +/// normal track selection, same as any other track daughter use case), and neutralDau from an +/// already-built FPi0s/FEtas partition. This builder does the 3-body combinatorics, the +/// resonance's own mass window, and -- like TwoTrackResonanceBuilder -- copies the daughters' +/// bitmasks onto the candidate so systematic variations can be reselected downstream from the +/// candidate table alone, without rejoining the daughter tables. +template +class TrackTrackTwoPhotonResonanceBuilder +{ + public: + TrackTrackTwoPhotonResonanceBuilder() = default; + ~TrackTrackTwoPhotonResonanceBuilder() = default; + + template + void init(T1& confFilter, T2& confPosDauSelection, T3& confNegDauSelection, T4& confTable, T5& 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; + + mPosDauThreshold = confPosDauSelection.pidThres.value; + mNegDauThreshold = confNegDauSelection.pidThres.value; + + if constexpr (modes::isEqual(resoType, modes::TrackTrackTwoPhotonResonance::kOmegaMeson)) { + LOG(info) << "Initialize femto OmegaMeson builder..."; + mPosDauMass = o2::constants::physics::MassPiPlus; + mNegDauMass = o2::constants::physics::MassPiMinus; + mProduceOmegaMesons = utils::enableTable("FOmegaMesons_001", confTable.produceOmegaMesons.value, initContext); + mProduceOmegaMesonMasks = utils::enableTable("FOmegaMesonMasks_001", confTable.produceOmegaMesonMasks.value, initContext); + } + + if (mProduceOmegaMesons || mProduceOmegaMesonMasks) { + 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& posDauPartition, T4& negDauPartition, T5& neutralDauPartition, T6& cache) + { + if (!mFillAnyTable) { + return; + } + auto posDauSlice = posDauPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + auto negDauSlice = negDauPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + auto neutralDauSlice = neutralDauPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + for (auto const& [posDau, negDau, neutralDau] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(posDauSlice, negDauSlice, neutralDauSlice))) { + this->fillResonance(col, posDau, negDau, neutralDau, resonanceProducts); + } + } + + private: + template + void reconstructResonance(T1 const& posDau, T2 const& negDau, T3 const& neutralDau) + { + ROOT::Math::PtEtaPhiMVector vecPosDau{posDau.pt(), posDau.eta(), posDau.phi(), mPosDauMass}; + ROOT::Math::PtEtaPhiMVector vecNegDau{negDau.pt(), negDau.eta(), negDau.phi(), mNegDauMass}; + ROOT::Math::PtEtaPhiMVector vecNeutralDau{neutralDau.pt(), neutralDau.eta(), neutralDau.phi(), neutralDau.mass()}; + ROOT::Math::PtEtaPhiMVector vecResonance = vecPosDau + vecNegDau + vecNeutralDau; + + 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& posDau, T3 const& negDau, T4 const& neutralDau, T5& resonanceProducts) + { + reconstructResonance(posDau, negDau, neutralDau); + if (!checkFilters()) { + return; + } + + bool posDauHasHighMomentum = posDau.p() > mPosDauThreshold; + bool negDauHasHighMomentum = negDau.p() > mNegDauThreshold; + + if constexpr (modes::isEqual(resoType, modes::TrackTrackTwoPhotonResonance::kOmegaMeson)) { + if (mProduceOmegaMesons) { + resonanceProducts.producedOmegaMesons(col.globalIndex(), + mPt, + mEta, + mPhi, + mMass, + posDau.globalIndex(), + negDau.globalIndex(), + neutralDau.globalIndex()); + } + if (mProduceOmegaMesonMasks) { + resonanceProducts.producedOmegaMesonMasks(posDau.mask(), + posDauHasHighMomentum, + negDau.mask(), + negDauHasHighMomentum, + neutralDau.maskDau()); + } + } + } + + // cached kinematics of the resonance + float mPt = 0.f; + float mEta = 0.f; + float mPhi = 0.f; + float mMass = 0.f; + + float mMassMin = 0.f; + float mMassMax = 1.f; + float mPtMin = 0.f; + float mPtMax = 10.f; + float mEtaMin = -0.9f; + float mEtaMax = 0.9f; + float mPhiMin = 0.f; + float mPhiMax = o2::constants::math::TwoPI; + + float mPosDauMass = 0.f; + float mNegDauMass = 0.f; + + float mPosDauThreshold = 0.f; + float mNegDauThreshold = 0.f; + + bool mFillAnyTable = false; + bool mProduceOmegaMesons = false; + bool mProduceOmegaMesonMasks = false; +}; + +} // namespace o2::analysis::femto::trackstwophotonresonancebuilder +#endif // PWGCF_FEMTO_CORE_TRACKTRACKTWOPHOTONRESONANCEBUILDER_H_ diff --git a/PWGCF/Femto/Core/trackTrackTwoPhotonResonanceHistManager.h b/PWGCF/Femto/Core/trackTrackTwoPhotonResonanceHistManager.h new file mode 100644 index 00000000000..936fd87dd36 --- /dev/null +++ b/PWGCF/Femto/Core/trackTrackTwoPhotonResonanceHistManager.h @@ -0,0 +1,231 @@ +// Copyright 2019-2026 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 trackTrackTwoPhotonResonanceHistManager.h +/// \brief histogram manager for resonances built from two tracks and a two-photon resonance +/// (e.g. omega(782) -> pi+ pi- pi0) +/// \author Anton Riedel, TU München, anton.riedel@tum.de + +#ifndef PWGCF_FEMTO_CORE_TRACKTRACKTWOPHOTONRESONANCEHISTMANAGER_H_ +#define PWGCF_FEMTO_CORE_TRACKTRACKTWOPHOTONRESONANCEHISTMANAGER_H_ + +#include "PWGCF/Femto/Core/histManager.h" +#include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/Core/photonHistManager.h" +#include "PWGCF/Femto/Core/trackHistManager.h" +#include "PWGCF/Femto/Core/twoPhotonResonanceHistManager.h" + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +namespace o2::analysis::femto::trackstwophotonresonancehistmanager +{ +enum TrackTrackTwoPhotonResonanceHist { + // analysis + kPt, + kEta, + kPhi, + kMass, + kPtVsMass, + // 2d qa + kPtVsEta, + kPtVsPhi, + kPhiVsEta, + kTrackTrackTwoPhotonResonanceHistLast +}; + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define TRACKTRACKTWOPHOTONRESONANCE_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 ConfOmegaMesonBinning : o2::framework::ConfigurableGroup { + std::string prefix = std::string("OmegaMesonBinning"); + TRACKTRACKTWOPHOTONRESONANCE_DEFAULT_BINNING(0.6f, 0.95f) +}; +#undef TRACKTRACKTWOPHOTONRESONANCE_DEFAULT_BINNING + +constexpr std::array, kTrackTrackTwoPhotonResonanceHistLast> 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 (GeV/#it{c}^{2}); Entries"}, + {kPtVsMass, o2::framework::HistType::kTH2F, "hPtVsMass", "p_{T} vs invariant mass; p_{T} (GeV/#it{c}); m (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 TRACKTRACKTWOPHOTONRESONANCE_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 TRACKTRACKTWOPHOTONRESONANCE_HIST_QA_MAP(conf) \ + {kPtVsEta, {(conf).pt, (conf).eta}}, \ + {kPtVsPhi, {(conf).pt, (conf).phi}}, \ + {kPhiVsEta, {(conf).phi, (conf).eta}}, + +template +auto makeTrackTrackTwoPhotonResonanceHistSpecMap(const T& confBinningAnalysis) +{ + return std::map>{ + TRACKTRACKTWOPHOTONRESONANCE_HIST_ANALYSIS_MAP(confBinningAnalysis)}; +} + +template +auto makeTrackTrackTwoPhotonResonanceQaHistSpecMap(const T& confBinningAnalysis) +{ + return std::map>{ + TRACKTRACKTWOPHOTONRESONANCE_HIST_ANALYSIS_MAP(confBinningAnalysis) + TRACKTRACKTWOPHOTONRESONANCE_HIST_QA_MAP(confBinningAnalysis)}; +} + +#undef TRACKTRACKTWOPHOTONRESONANCE_HIST_ANALYSIS_MAP +#undef TRACKTRACKTWOPHOTONRESONANCE_HIST_QA_MAP + +constexpr char PrefixOmegaMeson[] = "OmegaMeson/"; +constexpr char PrefixOmegaMesonNeutralDau[] = "OmegaMesonNeutralDau/"; +constexpr char PrefixOmegaMesonNeutralDauPhotonDau1[] = "OmegaMesonNeutralDauPhotonDau1Qa/"; +constexpr char PrefixOmegaMesonNeutralDauPhotonDau2[] = "OmegaMesonNeutralDauPhotonDau2Qa/"; + +constexpr std::string_view AnalysisDir = "Analysis/"; +constexpr std::string_view QaDir = "QA/"; + +/// \class TrackTrackTwoPhotonResonanceHistManager +/// \brief Histogram manager for resonances built from two tracks and a two-photon resonance (e.g. +/// omega(782) -> pi+ pi- pi0). The charged daughters reuse the regular TrackHistManager; +/// the embedded two-photon-resonance daughter reuses TwoPhotonResonanceHistManager as-is +/// (same reco+QA histograms, including its own photon-leg QA, that the dedicated Pi0/Eta +/// QA task gets) by also threading the FPhotons table through to it. +template +class TrackTrackTwoPhotonResonanceHistManager +{ + public: + TrackTrackTwoPhotonResonanceHistManager() = default; + ~TrackTrackTwoPhotonResonanceHistManager() = default; + + template + void init(o2::framework::HistogramRegistry* registry, + std::map> const& ResoSpecs, + std::map> const& PosDauSpecs, + std::map> const& NegDauSpecs, + std::map> const& NeutralDauSpecs, + std::map> const& NeutralDauPhotonSpecs, + T1 const& ConfPosDauQaBinning, + T2 const& ConfNegDauQaBinning, + T3 const& ConfNeutralDauPhotonQaBinning) + { + mHistogramRegistry = registry; + + // omega(782) -> pi+ pi- pi0: both charged daughters are pions. Daughter managers are kept at + // kReco regardless of the outer mode: their fillQa() needs detector-QA columns (tpcNClsFound, + // itsNCls, ...) that plain FTracks doesn't carry (only the dedicated femtoTrackQa.cxx joins + // those in) -- detailed per-track detector QA isn't this manager's job. + mPosDauManager.template init(registry, PosDauSpecs, 1, 1, kPiPlus, ConfPosDauQaBinning); + mNegDauManager.template init(registry, NegDauSpecs, 1, -1, kPiPlus, ConfNegDauQaBinning); + + // the embedded pi0/eta candidate is a fully-fledged femto table with its own mask/extras + // columns, so unlike the plain-track daughters above, it can just follow the outer mode. + mNeutralDauManager.template init(registry, NeutralDauSpecs, NeutralDauPhotonSpecs, ConfNeutralDauPhotonQaBinning); + + 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& tracks, T3 const& neutralDaus, T4 const& photons) + { + auto posDau = tracks.rawIteratorAt(resonance.posDauId() - tracks.offset()); + mPosDauManager.template fill(posDau, tracks); + auto negDau = tracks.rawIteratorAt(resonance.negDauId() - tracks.offset()); + mNegDauManager.template fill(negDau, tracks); + auto neutralDau = neutralDaus.rawIteratorAt(resonance.neutralDauId() - neutralDaus.offset()); + mNeutralDauManager.template fill(neutralDau, photons); + + 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; + trackhistmanager::TrackHistManager mPosDauManager; + trackhistmanager::TrackHistManager mNegDauManager; + twophotonresonancehistmanager::TwoPhotonResonanceHistManager mNeutralDauManager; +}; +} // namespace o2::analysis::femto::trackstwophotonresonancehistmanager +#endif // PWGCF_FEMTO_CORE_TRACKTRACKTWOPHOTONRESONANCEHISTMANAGER_H_ diff --git a/PWGCF/Femto/Core/twoPhotonResonanceBuilder.h b/PWGCF/Femto/Core/twoPhotonResonanceBuilder.h index 4e4d9ed31b0..5883ca23411 100644 --- a/PWGCF/Femto/Core/twoPhotonResonanceBuilder.h +++ b/PWGCF/Femto/Core/twoPhotonResonanceBuilder.h @@ -56,9 +56,12 @@ 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. +// 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. there is also just a single dauMask (not one per leg): both legs are plain photons with +// the same selection requirement, so the candidate only ever stores/needs the AND of both legs' +// masks (see femtotwophotonresonances::MaskDau in FemtoTables.h) -- no per-leg distinction to select on. // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define TWOPHOTONRESONANCE_DEFAULT_SELECTION(defaultMassMin, defaultMassMax, defaultPdgCodeAbs) \ o2::framework::Configurable ptMin{"ptMin", 0.f, "Minimum pT"}; \ @@ -70,8 +73,7 @@ using ConfEtaFilters = ConfTwoPhotonResonanceFilters; 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 pdgCodeAbs{"pdgCodeAbs", (defaultPdgCodeAbs), "Absolute PDG code of the resonance, used to set its mass for kstar/kt/mt (not the reconstructed two-photon mass)"}; \ - o2::framework::Configurable dau1Mask{"dau1Mask", 0, "Bitmask required for first photon daughter"}; \ - o2::framework::Configurable dau2Mask{"dau2Mask", 0, "Bitmask required for second photon daughter"}; + o2::framework::Configurable dauMask{"dauMask", 0, "Bitmask required for both photon daughters"}; struct ConfPi0Selection : o2::framework::ConfigurableGroup { std::string prefix = std::string("Pi0Selection1"); @@ -199,7 +201,7 @@ class TwoPhotonResonanceBuilder dau2.globalIndex()); } if (mProducePi0Masks) { - resonanceProducts.producedPi0Masks(dau1.mask(), dau2.mask()); + resonanceProducts.producedPi0Masks(dau1.mask() & dau2.mask()); } } if constexpr (modes::isEqual(resoType, modes::TwoPhotonResonance::kEta)) { @@ -213,7 +215,7 @@ class TwoPhotonResonanceBuilder dau2.globalIndex()); } if (mProduceEtaMasks) { - resonanceProducts.producedEtaMasks(dau1.mask(), dau2.mask()); + resonanceProducts.producedEtaMasks(dau1.mask() & dau2.mask()); } } } diff --git a/PWGCF/Femto/DataModel/FemtoTables.h b/PWGCF/Femto/DataModel/FemtoTables.h index 89841e1b412..029e5d3632e 100644 --- a/PWGCF/Femto/DataModel/FemtoTables.h +++ b/PWGCF/Femto/DataModel/FemtoTables.h @@ -901,10 +901,12 @@ 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 +// column for the daughter (photon) bit mask -- no momentum-threshold bit: photon daughters have no +// momentum-dependent PID switch the way charged-track daughters do in femtotwotrackresonances. Both +// legs are plain photons with the same selection requirement, so only the AND of both legs' own +// masks is stored (not one column per leg): requiring a set of bits in the AND is equivalent to +// requiring that same set of bits in both legs individually. +DECLARE_SOA_COLUMN(MaskDau, maskDau, o2::analysis::femto::datatypes::PhotonMaskType); //! Bitmask for the photon daughters (AND of both legs' own masks) // 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 @@ -928,8 +930,7 @@ DECLARE_SOA_TABLE_STAGED_VERSIONED(FPi0s_001, "FPI0", 1, //! femto pi0s femtobase::dynamic::Theta); using FPi0s = FPi0s_001; DECLARE_SOA_TABLE_STAGED_VERSIONED(FPi0Masks_001, "FPI0MASK", 1, //! mask for pi0s - femtotwophotonresonances::MaskDau1, - femtotwophotonresonances::MaskDau2); + femtotwophotonresonances::MaskDau); using FPi0Masks = FPi0Masks_001; // table for etas (-> gamma gamma) @@ -949,10 +950,53 @@ DECLARE_SOA_TABLE_STAGED_VERSIONED(FEtas_001, "FETA", 1, //! femto etas femtobase::dynamic::Theta); using FEtas = FEtas_001; DECLARE_SOA_TABLE_STAGED_VERSIONED(FEtaMasks_001, "FETAMASK", 1, //! mask for etas - femtotwophotonresonances::MaskDau1, - femtotwophotonresonances::MaskDau2); + femtotwophotonresonances::MaskDau); using FEtaMasks = FEtaMasks_001; +namespace femtotrackstwophotonresonances +{ +// columns for daughter bit masks, copied onto the candidate at build time -- same pattern as +// femtotwotrackresonances: systematic reselection happens on the candidate itself, not by rejoining +// the daughter tables. The two charged daughters keep their own momentum-threshold PID switch; the +// neutral (pi0/eta) daughter has no such switch and no dedicated mask of its own -- it is a direct +// copy of its FPi0Masks/FEtaMasks maskDau (itself already the AND of its two photon legs). +DECLARE_SOA_COLUMN(MaskPosDau, maskPosDau, o2::analysis::femto::datatypes::TrackMaskType); //! Bitmask for positive charged-track daughter +DECLARE_SOA_COLUMN(PosDauHasHighMomentum, posDauHasHighMomentum, bool); //! switch for pid threshold +DECLARE_SOA_COLUMN(MaskNegDau, maskNegDau, o2::analysis::femto::datatypes::TrackMaskType); //! Bitmask for negative charged-track daughter +DECLARE_SOA_COLUMN(NegDauHasHighMomentum, negDauHasHighMomentum, bool); //! switch for pid threshold +DECLARE_SOA_COLUMN(MaskNeutralDau, maskNeutralDau, o2::analysis::femto::datatypes::PhotonMaskType); //! Bitmask for the two-photon-resonance daughter (copy of its own maskDau) + +// id columns for the two charged-track daughters and the two-photon-resonance daughter +DECLARE_SOA_INDEX_COLUMN_FULL(PosDau, posDau, int32_t, FTracks, "_PosDau"); //! index column for the positive charged-track daughter +DECLARE_SOA_INDEX_COLUMN_FULL(NegDau, negDau, int32_t, FTracks, "_NegDau"); //! index column for the negative charged-track daughter +DECLARE_SOA_INDEX_COLUMN_FULL(NeutralDau, neutralDau, int32_t, FPi0s, "_NeutralDau"); //! index column for the two-photon-resonance daughter +} // namespace femtotrackstwophotonresonances + +// table for resonances built from two tracks and a two-photon resonance (e.g. omega(782) -> pi+ pi- pi0) +DECLARE_SOA_TABLE_STAGED_VERSIONED(FOmegaMesons_001, "FOMEMES", 1, //! femto omega mesons + o2::soa::Index<>, + femtobase::stored::FColId, + femtobase::stored::Pt, + femtobase::stored::Eta, + femtobase::stored::Phi, + femtobase::stored::Mass, + femtotrackstwophotonresonances::PosDauId, + femtotrackstwophotonresonances::NegDauId, + femtotrackstwophotonresonances::NeutralDauId, + femtobase::dynamic::P, + femtobase::dynamic::Px, + femtobase::dynamic::Py, + femtobase::dynamic::Pz, + femtobase::dynamic::Theta); +using FOmegaMesons = FOmegaMesons_001; +DECLARE_SOA_TABLE_STAGED_VERSIONED(FOmegaMesonMasks_001, "FOMEMESMASK", 1, //! mask for omega mesons + femtotrackstwophotonresonances::MaskPosDau, + femtotrackstwophotonresonances::PosDauHasHighMomentum, + femtotrackstwophotonresonances::MaskNegDau, + femtotrackstwophotonresonances::NegDauHasHighMomentum, + femtotrackstwophotonresonances::MaskNeutralDau); +using FOmegaMesonMasks = FOmegaMesonMasks_001; + namespace femtokinks { // columns for bit masks diff --git a/PWGCF/Femto/TableProducer/CMakeLists.txt b/PWGCF/Femto/TableProducer/CMakeLists.txt index 91add294624..9658d1b354f 100644 --- a/PWGCF/Femto/TableProducer/CMakeLists.txt +++ b/PWGCF/Femto/TableProducer/CMakeLists.txt @@ -43,3 +43,8 @@ o2physics_add_dpl_workflow(femto-producer-two-photon-resonances SOURCES ./femtoProducerTwoPhotonResonances.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(femto-producer-track-track-two-photon-resonances + SOURCES ./femtoProducerTrackTrackTwoPhotonResonances.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) diff --git a/PWGCF/Femto/TableProducer/femtoProducerTrackTrackTwoPhotonResonances.cxx b/PWGCF/Femto/TableProducer/femtoProducerTrackTrackTwoPhotonResonances.cxx new file mode 100644 index 00000000000..3aed1730218 --- /dev/null +++ b/PWGCF/Femto/TableProducer/femtoProducerTrackTrackTwoPhotonResonances.cxx @@ -0,0 +1,92 @@ +// Copyright 2019-2026 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 femtoProducerTrackTrackTwoPhotonResonances.cxx +/// \brief Task that produces femto tables for resonances built from two tracks and a two-photon +/// resonance (e.g. omega(782) -> pi+ pi- pi0) +/// \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/trackBuilder.h" +#include "PWGCF/Femto/Core/trackTrackTwoPhotonResonanceBuilder.h" +#include "PWGCF/Femto/Core/twoPhotonResonanceBuilder.h" +#include "PWGCF/Femto/DataModel/FemtoTables.h" + +#include +#include +#include +#include +#include +#include + +using namespace o2::analysis::femto; + +// reads the already-built FTracks (for the charged pion daughters) and FPi0s (for the neutral +// daughter) and combines them into omega(782) -> pi+ pi- pi0 candidates. A 3rd-stage producer, +// chained after femtoProducer.cxx (FTracks) and femtoProducerTwoPhotonResonances.cxx (FPi0s), same +// pattern as femtoProducerTwoPhotonResonances.cxx itself reading femtoProducer.cxx's FPhotons. +struct FemtoProducerTrackTrackTwoPhotonResonances { + + using FemtoCollisions = o2::soa::Join; + using FilteredFemtoCollisions = o2::soa::Filtered; + using FilteredFemtoCollision = FilteredFemtoCollisions::iterator; + + using FemtoTracks = o2::soa::Join; + using FemtoPi0s = 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 the two charged (pion) daughters -- plain track selections, same mechanism used for + // any other track daughter; sign is configured through these, not hardcoded in the builder + trackbuilder::ConfTrackSelection1 posDauSelection; + o2::framework::Partition posDauPartition = MAKE_TRACK_PARTITION(posDauSelection); + o2::framework::Preslice perColPosDau = o2::aod::femtobase::stored::fColId; + + trackbuilder::ConfTrackSelection2 negDauSelection; + o2::framework::Partition negDauPartition = MAKE_TRACK_PARTITION(negDauSelection); + o2::framework::Preslice perColNegDau = o2::aod::femtobase::stored::fColId; + + // setup for the neutral (pi0) daughter + twophotonresonancebuilder::ConfPi0Selection neutralDauSelection; + o2::framework::Partition neutralDauPartition = MAKE_TWOPHOTONRESONANCE_PARTITION(neutralDauSelection); + o2::framework::Preslice perColNeutralDau = o2::aod::femtobase::stored::fColId; + + // resonance filter and builder + trackstwophotonresonancebuilder::ConfOmegaMesonFilters confOmegaMesonFilter; + trackstwophotonresonancebuilder::ConfTrackTrackTwoPhotonResonanceTables confOmegaMesonTables; + trackstwophotonresonancebuilder::TrackTrackTwoPhotonResonanceBuilderProducts omegaMesonBuilderProducts; + trackstwophotonresonancebuilder::TrackTrackTwoPhotonResonanceBuilder omegaMesonBuilder; + + void init(o2::framework::InitContext& context) + { + omegaMesonBuilder.init(confOmegaMesonFilter, posDauSelection, negDauSelection, confOmegaMesonTables, context); + } + + void processOmegaMesons(FilteredFemtoCollision const& col, FemtoTracks const& /*tracks*/, FemtoPi0s const& /*pi0s*/) + { + omegaMesonBuilder.fillResonances(col, omegaMesonBuilderProducts, posDauPartition, negDauPartition, neutralDauPartition, cache); + } + PROCESS_SWITCH(FemtoProducerTrackTrackTwoPhotonResonances, processOmegaMesons, "Build OmegaMeson 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 1c31864d7f7..1f9647bd7b2 100644 --- a/PWGCF/Femto/Tasks/CMakeLists.txt +++ b/PWGCF/Femto/Tasks/CMakeLists.txt @@ -24,6 +24,11 @@ o2physics_add_dpl_workflow(femto-two-photon-resonance-qa PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(femto-track-track-two-photon-resonance-qa + SOURCES femtoTrackTrackTwoPhotonResonanceQa.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 diff --git a/PWGCF/Femto/Tasks/femtoTrackTrackTwoPhotonResonanceQa.cxx b/PWGCF/Femto/Tasks/femtoTrackTrackTwoPhotonResonanceQa.cxx new file mode 100644 index 00000000000..35a09042d94 --- /dev/null +++ b/PWGCF/Femto/Tasks/femtoTrackTrackTwoPhotonResonanceQa.cxx @@ -0,0 +1,127 @@ +// Copyright 2019-2026 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 femtoTrackTrackTwoPhotonResonanceQa.cxx +/// \brief QA task for resonances built from two tracks and a two-photon resonance (omega(782) -> pi+ pi- pi0) +/// \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/trackHistManager.h" +#include "PWGCF/Femto/Core/trackTrackTwoPhotonResonanceBuilder.h" +#include "PWGCF/Femto/Core/trackTrackTwoPhotonResonanceHistManager.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 FemtoTrackTrackTwoPhotonResonanceQa { + + // setup tables + using FemtoCollisions = o2::soa::Join; + using FilteredFemtoCollisions = o2::soa::Filtered; + using FilteredFemtoCollision = FilteredFemtoCollisions::iterator; + + using FemtoOmegaMesons = o2::soa::Join; + using FemtoTracks = o2::soa::Join; + using FemtoPi0s = 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 omega mesons + trackstwophotonresonancebuilder::ConfOmegaMesonSelection1 confOmegaMesonSelection; + o2::framework::Partition omegaMesonPartition = MAKE_TRACKTRACKTWOPHOTONRESONANCE_PARTITION(confOmegaMesonSelection); + o2::framework::Preslice perColOmegaMesons = o2::aod::femtobase::stored::fColId; + + trackstwophotonresonancehistmanager::ConfOmegaMesonBinning confOmegaMesonBinning; + + // setup for the two charged (pion) daughters + trackhistmanager::ConfResonancePosDauBinning confPosDauBinning; + trackhistmanager::ConfResonancePosDauQaBinning confPosDauQaBinning; + trackhistmanager::ConfResonanceNegDauBinning confNegDauBinning; + trackhistmanager::ConfResonanceNegDauQaBinning confNegDauQaBinning; + + // setup for the neutral (pi0) daughter -- reuses the regular Pi0 binning/QA config, since the + // embedded candidate gets the same TwoPhotonResonanceHistManager treatment (including its own + // photon-leg QA) as the dedicated Pi0/Eta QA task + twophotonresonancehistmanager::ConfPi0Binning confNeutralDauBinning; + photonhistmanager::ConfPhotonBinning confNeutralDauPhotonBinning; + photonhistmanager::ConfPhotonQaBinning confNeutralDauPhotonQaBinning; + + trackstwophotonresonancehistmanager::TrackTrackTwoPhotonResonanceHistManager< + trackstwophotonresonancehistmanager::PrefixOmegaMeson, + trackhistmanager::PrefixResonancePosDaughter, + trackhistmanager::PrefixResonanceNegDaughter, + trackstwophotonresonancehistmanager::PrefixOmegaMesonNeutralDau, + trackstwophotonresonancehistmanager::PrefixOmegaMesonNeutralDauPhotonDau1, + trackstwophotonresonancehistmanager::PrefixOmegaMesonNeutralDauPhotonDau2, + modes::TrackTrackTwoPhotonResonance::kOmegaMeson, + modes::TwoPhotonResonance::kPi0> + omegaMesonHistManager; + + o2::framework::HistogramRegistry hRegistry{"FemtoTrackTrackTwoPhotonResonanceQa", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; + + void init(o2::framework::InitContext&) + { + auto colHistSpec = colhistmanager::makeColQaHistSpecMap(confCollisionBinning, confCollisionQaBinning); + colHistManager.init(&hRegistry, colHistSpec, confCollisionBinning, confCollisionQaBinning); + + auto omegaMesonHistSpec = trackstwophotonresonancehistmanager::makeTrackTrackTwoPhotonResonanceQaHistSpecMap(confOmegaMesonBinning); + auto posDauSpec = trackhistmanager::makeTrackQaHistSpecMap(confPosDauBinning, confPosDauQaBinning); + auto negDauSpec = trackhistmanager::makeTrackQaHistSpecMap(confNegDauBinning, confNegDauQaBinning); + auto neutralDauSpec = twophotonresonancehistmanager::makeTwoPhotonResonanceQaHistSpecMap(confNeutralDauBinning); + auto neutralDauPhotonSpec = photonhistmanager::makePhotonQaHistSpecMap(confNeutralDauPhotonBinning, confNeutralDauPhotonQaBinning); + + omegaMesonHistManager.init(&hRegistry, omegaMesonHistSpec, posDauSpec, negDauSpec, neutralDauSpec, neutralDauPhotonSpec, confPosDauQaBinning, confNegDauQaBinning, confNeutralDauPhotonQaBinning); + } + + void processOmegaMesons(FilteredFemtoCollision const& col, FemtoOmegaMesons const& /*omegaMesons*/, FemtoTracks const& tracks, FemtoPi0s const& pi0s, FemtoPhotons const& photons) + { + auto omegaMesonSlice = omegaMesonPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + if (omegaMesonSlice.size() == 0) { + return; + } + colHistManager.fill(col); + for (auto const& omegaMeson : omegaMesonSlice) { + omegaMesonHistManager.fill(omegaMeson, tracks, pi0s, photons); + } + } + PROCESS_SWITCH(FemtoTrackTrackTwoPhotonResonanceQa, processOmegaMesons, "Process OmegaMesons", true); +}; + +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) +{ + o2::framework::WorkflowSpec workflow{ + adaptAnalysisTask(context), + }; + return workflow; +}