Skip to content

Commit b197d72

Browse files
authored
[PWGCF] Add Omega mesons to femto framework (#18357)
1 parent 4a90c8b commit b197d72

11 files changed

Lines changed: 802 additions & 28 deletions

‎PWGCF/Femto/Core/dataTypes.h‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ using TwoTrackResonanceType = uint16_t;
5050
// datatype for resonances built from two photons (pi0, eta, ...)
5151
using TwoPhotonResonanceType = uint16_t;
5252

53+
// datatype for resonances built from two tracks and a two-photon resonance (e.g. omega(782) -> pi+ pi- pi0)
54+
using TrackTrackTwoPhotonResonanceType = uint8_t;
55+
5356
// datatypes for cascades
5457
using CascadeMaskType = uint32_t;
5558
using CascadeMaskType001 = uint16_t; // old data type, was too narrow

‎PWGCF/Femto/Core/modes.h‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,12 @@ enum class TwoPhotonResonance : o2::analysis::femto::datatypes::TwoPhotonResonan
241241
kEta
242242
};
243243

244+
// resonances reconstructed from two tracks and a two-photon resonance, e.g. omega(782) -> pi+ pi- pi0.
245+
// Named kOmegaMeson (not kOmega) to not collide with Cascade::kOmega, the Omega- baryon.
246+
enum class TrackTrackTwoPhotonResonance : o2::analysis::femto::datatypes::TrackTrackTwoPhotonResonanceType {
247+
kOmegaMeson
248+
};
249+
244250
enum class CharmHadron : o2::analysis::femto::datatypes::CharmHadronType {
245251
kD0,
246252
kD0Bar,

‎PWGCF/Femto/Core/partitions.h‎

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -95,19 +95,42 @@
9595
ncheckbit(o2::aod::femtotwotrackresonances::maskNegDau, (selection).negDauMaskBelowThres))
9696

9797
// partition for resonances built from two photons (pi0, eta, ...): unlike MAKE_RESONANCE_0/1_PARTITON
98-
// there is no sign and no momentum-threshold PID switch, just a plain mask check per (unordered) daughter
99-
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
100-
#define MAKE_TWOPHOTONRESONANCE_PARTITION(selection) \
101-
(o2::aod::femtobase::stored::pt > (selection).ptMin) && \
102-
(o2::aod::femtobase::stored::pt < (selection).ptMax) && \
103-
(o2::aod::femtobase::stored::eta > (selection).etaMin) && \
104-
(o2::aod::femtobase::stored::eta < (selection).etaMax) && \
105-
(o2::aod::femtobase::stored::phi > (selection).phiMin) && \
106-
(o2::aod::femtobase::stored::phi < (selection).phiMax) && \
107-
(o2::aod::femtobase::stored::mass > (selection).massMin) && \
108-
(o2::aod::femtobase::stored::mass < (selection).massMax) && \
109-
ncheckbit(o2::aod::femtotwophotonresonances::maskDau1, (selection).dau1Mask) && \
110-
ncheckbit(o2::aod::femtotwophotonresonances::maskDau2, (selection).dau2Mask)
98+
// there is no sign and no momentum-threshold PID switch, and just a single mask check -- both
99+
// daughters are plain photons with the same selection requirement, so the candidate only stores the
100+
// AND of both legs' own masks (see femtotwophotonresonances::MaskDau)
101+
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
102+
#define MAKE_TWOPHOTONRESONANCE_PARTITION(selection) \
103+
(o2::aod::femtobase::stored::pt > (selection).ptMin) && \
104+
(o2::aod::femtobase::stored::pt < (selection).ptMax) && \
105+
(o2::aod::femtobase::stored::eta > (selection).etaMin) && \
106+
(o2::aod::femtobase::stored::eta < (selection).etaMax) && \
107+
(o2::aod::femtobase::stored::phi > (selection).phiMin) && \
108+
(o2::aod::femtobase::stored::phi < (selection).phiMax) && \
109+
(o2::aod::femtobase::stored::mass > (selection).massMin) && \
110+
(o2::aod::femtobase::stored::mass < (selection).massMax) && \
111+
ncheckbit(o2::aod::femtotwophotonresonances::maskDau, (selection).dauMask)
112+
113+
// partition for resonances built from two tracks and a two-photon resonance (e.g. omega(782) ->
114+
// pi+ pi- pi0): same pattern as MAKE_RESONANCE_1_PARTITON (daughter masks copied onto the candidate
115+
// at build time, systematics reselected here), just with 3 daughter masks instead of 2 -- the two
116+
// charged daughters keep their momentum-threshold PID switch, the neutral daughter does not
117+
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
118+
#define MAKE_TRACKTRACKTWOPHOTONRESONANCE_PARTITION(selection) \
119+
(o2::aod::femtobase::stored::pt > (selection).ptMin) && \
120+
(o2::aod::femtobase::stored::pt < (selection).ptMax) && \
121+
(o2::aod::femtobase::stored::eta > (selection).etaMin) && \
122+
(o2::aod::femtobase::stored::eta < (selection).etaMax) && \
123+
(o2::aod::femtobase::stored::phi > (selection).phiMin) && \
124+
(o2::aod::femtobase::stored::phi < (selection).phiMax) && \
125+
(o2::aod::femtobase::stored::mass > (selection).massMin) && \
126+
(o2::aod::femtobase::stored::mass < (selection).massMax) && \
127+
ifnode(o2::aod::femtotrackstwophotonresonances::posDauHasHighMomentum, \
128+
ncheckbit(o2::aod::femtotrackstwophotonresonances::maskPosDau, (selection).posDauMaskAboveThres), \
129+
ncheckbit(o2::aod::femtotrackstwophotonresonances::maskPosDau, (selection).posDauMaskBelowThres)) && \
130+
ifnode(o2::aod::femtotrackstwophotonresonances::negDauHasHighMomentum, \
131+
ncheckbit(o2::aod::femtotrackstwophotonresonances::maskNegDau, (selection).negDauMaskAboveThres), \
132+
ncheckbit(o2::aod::femtotrackstwophotonresonances::maskNegDau, (selection).negDauMaskBelowThres)) && \
133+
ncheckbit(o2::aod::femtotrackstwophotonresonances::maskNeutralDau, (selection).neutralDauMask)
111134

112135
// partition for lambdas
113136
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
// Copyright 2019-2026 CERN and copyright holders of ALICE O2.
2+
// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3+
// All rights not expressly granted are reserved.
4+
//
5+
// This software is distributed under the terms of the GNU General Public
6+
// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7+
//
8+
// In applying this license CERN does not waive the privileges and immunities
9+
// granted to it by virtue of its status as an Intergovernmental Organization
10+
// or submit itself to any jurisdiction.
11+
12+
/// \file trackTrackTwoPhotonResonanceBuilder.h
13+
/// \brief builder for resonances reconstructed from two tracks and a two-photon resonance
14+
/// (e.g. omega(782) -> pi+ pi- pi0)
15+
/// \author Anton Riedel, TU München, anton.riedel@tum.de
16+
17+
#ifndef PWGCF_FEMTO_CORE_TRACKTRACKTWOPHOTONRESONANCEBUILDER_H_
18+
#define PWGCF_FEMTO_CORE_TRACKTRACKTWOPHOTONRESONANCEBUILDER_H_
19+
20+
#include "PWGCF/Femto/Core/dataTypes.h"
21+
#include "PWGCF/Femto/Core/femtoUtils.h"
22+
#include "PWGCF/Femto/Core/modes.h"
23+
#include "PWGCF/Femto/DataModel/FemtoTables.h"
24+
25+
#include "Common/Core/RecoDecay.h"
26+
27+
#include <CommonConstants/MathConstants.h>
28+
#include <CommonConstants/PhysicsConstants.h>
29+
#include <Framework/ASoAHelpers.h>
30+
#include <Framework/AnalysisHelpers.h>
31+
#include <Framework/Configurable.h>
32+
#include <Framework/Logger.h>
33+
34+
#include <Math/Vector4D.h> // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h)
35+
#include <Math/Vector4Dfwd.h>
36+
37+
#include <string>
38+
39+
namespace o2::analysis::femto::trackstwophotonresonancebuilder
40+
{
41+
// filters applied in the producer task (loose pre-selection, applied before the mass window itself)
42+
struct ConfOmegaMesonFilters : o2::framework::ConfigurableGroup {
43+
std::string prefix = std::string("OmegaMesonFilters");
44+
o2::framework::Configurable<float> ptMin{"ptMin", 0.f, "Minimum pT"};
45+
o2::framework::Configurable<float> ptMax{"ptMax", 10.f, "Maximum pT"};
46+
o2::framework::Configurable<float> etaMin{"etaMin", -0.9f, "Minimum eta"};
47+
o2::framework::Configurable<float> etaMax{"etaMax", 0.9f, "Maximum eta"};
48+
o2::framework::Configurable<float> phiMin{"phiMin", 0.f, "Minimum phi"};
49+
o2::framework::Configurable<float> phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"};
50+
o2::framework::Configurable<float> massMin{"massMin", 0.6f, "Minimum invariant mass"};
51+
o2::framework::Configurable<float> massMax{"massMax", 0.95f, "Maximum invariant mass"};
52+
};
53+
54+
// selection used downstream (QA/pairing) to re-select already produced resonances by mass window
55+
// and daughter mask -- same pattern as the two-track resonances (TWOTRACKRESONANCE_DEFAULT_SELECTION),
56+
// just with a 3rd mask for the neutral daughter (no momentum-threshold split there, same as
57+
// femtotwophotonresonances' single dauMask)
58+
template <auto& Prefix>
59+
struct ConfOmegaMesonSelection : o2::framework::ConfigurableGroup {
60+
std::string prefix = Prefix;
61+
o2::framework::Configurable<int> pdgCodeAbs{"pdgCodeAbs", o2::constants::physics::Pdg::kOmega, "Absolute PDG code of the resonance (pdg[omega(782)]=223)"};
62+
o2::framework::Configurable<float> ptMin{"ptMin", 0.f, "Minimum pT"};
63+
o2::framework::Configurable<float> ptMax{"ptMax", 10.f, "Maximum pT"};
64+
o2::framework::Configurable<float> etaMin{"etaMin", -0.9f, "Minimum eta"};
65+
o2::framework::Configurable<float> etaMax{"etaMax", 0.9f, "Maximum eta"};
66+
o2::framework::Configurable<float> phiMin{"phiMin", 0.f, "Minimum phi"};
67+
o2::framework::Configurable<float> phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"};
68+
o2::framework::Configurable<float> massMin{"massMin", 0.6f, "Minimum invariant mass"};
69+
o2::framework::Configurable<float> massMax{"massMax", 0.95f, "Maximum invariant mass"};
70+
o2::framework::Configurable<o2::analysis::femto::datatypes::TrackMaskType> posDauMaskBelowThres{"posDauMaskBelowThres", 0x10u, "Bitmask for positive daughter below threshold"};
71+
o2::framework::Configurable<o2::analysis::femto::datatypes::TrackMaskType> posDauMaskAboveThres{"posDauMaskAboveThres", 0x8u, "Bitmask for positive daughter above threshold"};
72+
o2::framework::Configurable<o2::analysis::femto::datatypes::TrackMaskType> negDauMaskBelowThres{"negDauMaskBelowThres", 0x2u, "Bitmask for negative daughter below threshold"};
73+
o2::framework::Configurable<o2::analysis::femto::datatypes::TrackMaskType> negDauMaskAboveThres{"negDauMaskAboveThres", 0x1u, "Bitmask for negative daughter above threshold"};
74+
o2::framework::Configurable<o2::analysis::femto::datatypes::PhotonMaskType> neutralDauMask{"neutralDauMask", 0, "Bitmask required for the neutral (two-photon-resonance) daughter"};
75+
};
76+
77+
constexpr const char PrefixOmegaMesonSelection1[] = "OmegaMesonSelection1";
78+
using ConfOmegaMesonSelection1 = ConfOmegaMesonSelection<PrefixOmegaMesonSelection1>;
79+
80+
struct TrackTrackTwoPhotonResonanceBuilderProducts : o2::framework::ProducesGroup {
81+
o2::framework::Produces<o2::aod::FOmegaMesons> producedOmegaMesons;
82+
o2::framework::Produces<o2::aod::FOmegaMesonMasks> producedOmegaMesonMasks;
83+
};
84+
85+
struct ConfTrackTrackTwoPhotonResonanceTables : o2::framework::ConfigurableGroup {
86+
std::string prefix = std::string("TrackTrackTwoPhotonResonanceTables");
87+
o2::framework::Configurable<int> produceOmegaMesons{"produceOmegaMesons", -1, "Produce OmegaMesons (-1: auto; 0 off; 1 on)"};
88+
o2::framework::Configurable<int> produceOmegaMesonMasks{"produceOmegaMesonMasks", -1, "Produce OmegaMesonMasks (-1: auto; 0 off; 1 on)"};
89+
};
90+
91+
/// \brief Builder for resonances reconstructed from two tracks and a two-photon resonance
92+
/// (e.g. omega(782) -> pi+ pi- pi0).
93+
///
94+
/// posDau/negDau are drawn from already-selected FTracks partitions (sign and PID handled by the
95+
/// normal track selection, same as any other track daughter use case), and neutralDau from an
96+
/// already-built FPi0s/FEtas partition. This builder does the 3-body combinatorics, the
97+
/// resonance's own mass window, and -- like TwoTrackResonanceBuilder -- copies the daughters'
98+
/// bitmasks onto the candidate so systematic variations can be reselected downstream from the
99+
/// candidate table alone, without rejoining the daughter tables.
100+
template <modes::TrackTrackTwoPhotonResonance resoType>
101+
class TrackTrackTwoPhotonResonanceBuilder
102+
{
103+
public:
104+
TrackTrackTwoPhotonResonanceBuilder() = default;
105+
~TrackTrackTwoPhotonResonanceBuilder() = default;
106+
107+
template <typename T1, typename T2, typename T3, typename T4, typename T5>
108+
void init(T1& confFilter, T2& confPosDauSelection, T3& confNegDauSelection, T4& confTable, T5& initContext)
109+
{
110+
mMassMin = confFilter.massMin.value;
111+
mMassMax = confFilter.massMax.value;
112+
mPtMin = confFilter.ptMin.value;
113+
mPtMax = confFilter.ptMax.value;
114+
mEtaMin = confFilter.etaMin.value;
115+
mEtaMax = confFilter.etaMax.value;
116+
mPhiMin = confFilter.phiMin.value;
117+
mPhiMax = confFilter.phiMax.value;
118+
119+
mPosDauThreshold = confPosDauSelection.pidThres.value;
120+
mNegDauThreshold = confNegDauSelection.pidThres.value;
121+
122+
if constexpr (modes::isEqual(resoType, modes::TrackTrackTwoPhotonResonance::kOmegaMeson)) {
123+
LOG(info) << "Initialize femto OmegaMeson builder...";
124+
mPosDauMass = o2::constants::physics::MassPiPlus;
125+
mNegDauMass = o2::constants::physics::MassPiMinus;
126+
mProduceOmegaMesons = utils::enableTable("FOmegaMesons_001", confTable.produceOmegaMesons.value, initContext);
127+
mProduceOmegaMesonMasks = utils::enableTable("FOmegaMesonMasks_001", confTable.produceOmegaMesonMasks.value, initContext);
128+
}
129+
130+
if (mProduceOmegaMesons || mProduceOmegaMesonMasks) {
131+
mFillAnyTable = true;
132+
} else {
133+
LOG(info) << "No tables configured, Selection object will not be configured...";
134+
LOG(info) << "Initialization done...";
135+
return;
136+
}
137+
LOG(info) << "Initialization done...";
138+
}
139+
140+
template <typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
141+
void fillResonances(T1 const& col, T2& resonanceProducts, T3& posDauPartition, T4& negDauPartition, T5& neutralDauPartition, T6& cache)
142+
{
143+
if (!mFillAnyTable) {
144+
return;
145+
}
146+
auto posDauSlice = posDauPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache);
147+
auto negDauSlice = negDauPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache);
148+
auto neutralDauSlice = neutralDauPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache);
149+
for (auto const& [posDau, negDau, neutralDau] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(posDauSlice, negDauSlice, neutralDauSlice))) {
150+
this->fillResonance(col, posDau, negDau, neutralDau, resonanceProducts);
151+
}
152+
}
153+
154+
private:
155+
template <typename T1, typename T2, typename T3>
156+
void reconstructResonance(T1 const& posDau, T2 const& negDau, T3 const& neutralDau)
157+
{
158+
ROOT::Math::PtEtaPhiMVector vecPosDau{posDau.pt(), posDau.eta(), posDau.phi(), mPosDauMass};
159+
ROOT::Math::PtEtaPhiMVector vecNegDau{negDau.pt(), negDau.eta(), negDau.phi(), mNegDauMass};
160+
ROOT::Math::PtEtaPhiMVector vecNeutralDau{neutralDau.pt(), neutralDau.eta(), neutralDau.phi(), neutralDau.mass()};
161+
ROOT::Math::PtEtaPhiMVector vecResonance = vecPosDau + vecNegDau + vecNeutralDau;
162+
163+
mPt = vecResonance.Pt();
164+
mEta = vecResonance.Eta();
165+
mPhi = RecoDecay::constrainAngle(vecResonance.Phi());
166+
mMass = vecResonance.M();
167+
}
168+
169+
[[nodiscard]] bool checkFilters() const
170+
{
171+
return ((mMass > mMassMin && mMass < mMassMax) &&
172+
(mPt > mPtMin && mPt < mPtMax) &&
173+
(mEta > mEtaMin && mEta < mEtaMax) &&
174+
(mPhi >= mPhiMin && mPhi < mPhiMax));
175+
}
176+
177+
template <typename T1, typename T2, typename T3, typename T4, typename T5>
178+
void fillResonance(T1 const& col, T2 const& posDau, T3 const& negDau, T4 const& neutralDau, T5& resonanceProducts)
179+
{
180+
reconstructResonance(posDau, negDau, neutralDau);
181+
if (!checkFilters()) {
182+
return;
183+
}
184+
185+
bool posDauHasHighMomentum = posDau.p() > mPosDauThreshold;
186+
bool negDauHasHighMomentum = negDau.p() > mNegDauThreshold;
187+
188+
if constexpr (modes::isEqual(resoType, modes::TrackTrackTwoPhotonResonance::kOmegaMeson)) {
189+
if (mProduceOmegaMesons) {
190+
resonanceProducts.producedOmegaMesons(col.globalIndex(),
191+
mPt,
192+
mEta,
193+
mPhi,
194+
mMass,
195+
posDau.globalIndex(),
196+
negDau.globalIndex(),
197+
neutralDau.globalIndex());
198+
}
199+
if (mProduceOmegaMesonMasks) {
200+
resonanceProducts.producedOmegaMesonMasks(posDau.mask(),
201+
posDauHasHighMomentum,
202+
negDau.mask(),
203+
negDauHasHighMomentum,
204+
neutralDau.maskDau());
205+
}
206+
}
207+
}
208+
209+
// cached kinematics of the resonance
210+
float mPt = 0.f;
211+
float mEta = 0.f;
212+
float mPhi = 0.f;
213+
float mMass = 0.f;
214+
215+
float mMassMin = 0.f;
216+
float mMassMax = 1.f;
217+
float mPtMin = 0.f;
218+
float mPtMax = 10.f;
219+
float mEtaMin = -0.9f;
220+
float mEtaMax = 0.9f;
221+
float mPhiMin = 0.f;
222+
float mPhiMax = o2::constants::math::TwoPI;
223+
224+
float mPosDauMass = 0.f;
225+
float mNegDauMass = 0.f;
226+
227+
float mPosDauThreshold = 0.f;
228+
float mNegDauThreshold = 0.f;
229+
230+
bool mFillAnyTable = false;
231+
bool mProduceOmegaMesons = false;
232+
bool mProduceOmegaMesonMasks = false;
233+
};
234+
235+
} // namespace o2::analysis::femto::trackstwophotonresonancebuilder
236+
#endif // PWGCF_FEMTO_CORE_TRACKTRACKTWOPHOTONRESONANCEBUILDER_H_

0 commit comments

Comments
 (0)