Skip to content

Commit fc028de

Browse files
committed
[PWGCF] Femto: add Lc data model and selection
1 parent 225279c commit fc028de

4 files changed

Lines changed: 184 additions & 13 deletions

File tree

PWGCF/Femto/Core/charmHadronBuilder.h

Lines changed: 96 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "PWGCF/Femto/Core/selectionContainer.h"
2424
#include "PWGCF/Femto/DataModel/FemtoTables.h"
2525
#include "PWGHF/Core/HfHelper.h"
26+
#include "PWGHF/DataModel/CandidateReconstructionTables.h"
2627
#include "PWGHF/DataModel/TrackIndexSkimmingTables.h"
2728

2829
#include <CommonConstants/MathConstants.h>
@@ -73,6 +74,16 @@ struct ConfD0Bits : o2::framework::ConfigurableGroup {
7374
o2::framework::Configurable<std::vector<float>> cosThetaStarMax{"cosThetaStarMax", {1.f}, "Maximum |cos(theta*)| of the decay"};
7475
};
7576

77+
// derived selection bits for Lc
78+
struct ConfLcBits : o2::framework::ConfigurableGroup {
79+
std::string prefix = std::string("LcBits");
80+
o2::framework::Configurable<bool> passThrough{"passThrough", false, "If true, all Lcs are passed through. Bits for all selections are stored."};
81+
o2::framework::Configurable<std::vector<float>> cpaMin{"cpaMin", {0.9f}, "Minimum cosine of pointing angle"};
82+
o2::framework::Configurable<std::vector<float>> decayLengthMin{"decayLengthMin", {0.02f}, "Minimum decay length (cm)"};
83+
o2::framework::Configurable<std::vector<float>> chi2PcaMax{"chi2PcaMax", {1.f}, "Maximum sum of distances of the secondary vertex to its prongs (cm)"};
84+
o2::framework::Configurable<std::vector<float>> impactParameterProngSqSumMin{"impactParameterProngSqSumMin", {0.f}, "Minimum sum of squared prong impact parameters (cm^2)"};
85+
};
86+
7687
// base selection for analysis task for D0s
7788
template <auto& Prefix>
7889
struct ConfD0Selection : o2::framework::ConfigurableGroup {
@@ -93,11 +104,36 @@ struct ConfD0Selection : o2::framework::ConfigurableGroup {
93104
o2::framework::Configurable<datatypes::CharmHadronMaskType> mask{"mask", 0, "Bitmask for D0 selection"};
94105
};
95106

107+
// base selection for analysis task for Lcs
108+
template <auto& Prefix>
109+
struct ConfLcSelection : o2::framework::ConfigurableGroup {
110+
std::string prefix = Prefix;
111+
o2::framework::Configurable<int> pdgCodeAbs{"pdgCodeAbs", o2::constants::physics::Pdg::kLambdaCPlus, "PDG code (Lc)"};
112+
o2::framework::Configurable<int> sign{"sign", 0, "Particle sign (+1: Lc; -1: Lcbar; 0: both)"};
113+
o2::framework::Configurable<float> ptMin{"ptMin", 1.f, "Minimum pT"};
114+
o2::framework::Configurable<float> ptMax{"ptMax", 3.f, "Maximum pT"};
115+
// acceptance is applied as a rapidity cut in the builder; the eta/phi windows
116+
// are kept open and exist only to satisfy the partition macro
117+
o2::framework::Configurable<float> etaMin{"etaMin", -0.8f, "Minimum eta"};
118+
o2::framework::Configurable<float> etaMax{"etaMax", 0.8f, "Maximum eta"};
119+
o2::framework::Configurable<float> phiMin{"phiMin", 0.f, "Minimum phi"};
120+
o2::framework::Configurable<float> phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"};
121+
// signal region; side-bands remain available in the derived data
122+
o2::framework::Configurable<float> massMin{"massMin", 2.24f, "Minimum invariant mass for Lc"};
123+
o2::framework::Configurable<float> massMax{"massMax", 2.33f, "Maximum invariant mass for Lc"};
124+
o2::framework::Configurable<datatypes::CharmHadronMaskType> mask{"mask", 0, "Bitmask for Lc selection"};
125+
};
126+
96127
constexpr const char PrefixD0Selection1[] = "D0Selection1";
97128
constexpr const char PrefixD0Selection2[] = "D0Selection2";
98129
using ConfD0Selection1 = ConfD0Selection<PrefixD0Selection1>;
99130
using ConfD0Selection2 = ConfD0Selection<PrefixD0Selection2>;
100131

132+
constexpr const char PrefixLcSelection1[] = "LcSelection1";
133+
constexpr const char PrefixLcSelection2[] = "LcSelection2";
134+
using ConfLcSelection1 = ConfLcSelection<PrefixLcSelection1>;
135+
using ConfLcSelection2 = ConfLcSelection<PrefixLcSelection2>;
136+
101137
/// The different selections for charm hadrons.
102138
/// The enum is shared by all species in the family; which bits are actually
103139
/// registered is decided per species in CharmHadronSelection::configure.
@@ -110,18 +146,27 @@ enum CharmHadronSels {
110146
kImpactParameterProductMax, ///< Max. product of prong impact parameters (d0*d0)
111147
kCosThetaStarMax, ///< Max. |cos(theta*)| of the decay
112148

149+
// topological selections, 3-prong only (Lc)
150+
kChi2PcaMax, ///< Max. sum of distances of the secondary vertex to its prongs
151+
kImpactParameterProngSqSumMin, ///< Min. sum of squared prong impact parameters
152+
113153
kCharmHadronSelsMax
114154
};
115155

116156
constexpr char D0SelHistName[] = "hD0Selection";
117157
constexpr char D0barSelHistName[] = "hD0barSelection";
118158
constexpr char CharmHadronSelsName[] = "Charm hadron selection object";
159+
constexpr char LcSelHistName[] = "hLcSelection";
160+
constexpr char LcBarSelHistName[] = "hLcBarSelection";
119161
const std::unordered_map<CharmHadronSels, std::string> charmHadronSelectionNames = {
120162
{kCpaMin, "Min. CPA (cosine pointing angle)"},
121163
{kDecayLengthMin, "Min. decay length"},
122164

123165
{kImpactParameterProductMax, "Max. product of prong impact parameters (d0*d0)"},
124-
{kCosThetaStarMax, "Max. |cos(theta*)| of the decay"}};
166+
{kCosThetaStarMax, "Max. |cos(theta*)| of the decay"},
167+
168+
{kChi2PcaMax, "Max. sum of distances of the secondary vertex to its prongs (cm)"},
169+
{kImpactParameterProngSqSumMin, "Min. sum of squared prong impact parameters (cm^2)"}};
125170

126171
/// enum for all charm hadron filters (loose kinematic pre-selection, applied before the bit selections).
127172
enum CharmHadronFilters {
@@ -143,6 +188,8 @@ enum CharmHadronFilters {
143188

144189
constexpr char D0FilterHistName[] = "hD0Filters";
145190
constexpr char D0barFilterHistName[] = "hD0barFilters";
191+
constexpr char LcFilterHistName[] = "hLcFilters";
192+
constexpr char LcBarFilterHistName[] = "hLcBarFilters";
146193
const std::unordered_map<CharmHadronFilters, std::string> charmHadronFilterNames = {
147194
{kDecayChannel, "Decay channel"},
148195
{kHypothesis, "Mass hypothesis"},
@@ -158,6 +205,20 @@ const std::unordered_map<CharmHadronFilters, std::string> charmHadronFilterNames
158205
{kMassMax, "Maximum invariant mass"},
159206
{kRejectAmbiguous, "Reject ambiguous mass hypothesis"}};
160207

208+
template <modes::CharmHadron hadronType>
209+
constexpr bool isThreeProng()
210+
{
211+
return modes::isEqual(hadronType, modes::CharmHadron::kLc) ||
212+
modes::isEqual(hadronType, modes::CharmHadron::kLcBar);
213+
}
214+
215+
template <modes::CharmHadron hadronType>
216+
constexpr bool isParticle()
217+
{
218+
return modes::isEqual(hadronType, modes::CharmHadron::kD0) ||
219+
modes::isEqual(hadronType, modes::CharmHadron::kLc);
220+
}
221+
161222
template <modes::CharmHadron hadronType, auto& SelectionHistName, auto& FilterHistName>
162223
class CharmHadronSelection : public baseselection::BaseSelection<float, o2::analysis::femto::datatypes::CharmHadronMaskType, kCharmHadronSelsMax>
163224
{
@@ -185,8 +246,14 @@ class CharmHadronSelection : public baseselection::BaseSelection<float, o2::anal
185246

186247
this->addSelection(kCpaMin, charmHadronSelectionNames.at(kCpaMin), config.cpaMin.value, limits::kLowerLimit, true, true, false);
187248
this->addSelection(kDecayLengthMin, charmHadronSelectionNames.at(kDecayLengthMin), config.decayLengthMin.value, limits::kLowerLimit, true, true, false);
188-
this->addSelection(kImpactParameterProductMax, charmHadronSelectionNames.at(kImpactParameterProductMax), config.impactParameterProductMax.value, limits::kUpperLimit, true, true, false);
189-
this->addSelection(kCosThetaStarMax, charmHadronSelectionNames.at(kCosThetaStarMax), config.cosThetaStarMax.value, limits::kAbsUpperLimit, true, true, false);
249+
250+
if constexpr (isThreeProng<hadronType>()) {
251+
this->addSelection(kChi2PcaMax, charmHadronSelectionNames.at(kChi2PcaMax), config.chi2PcaMax.value, limits::kUpperLimit, true, true, false);
252+
this->addSelection(kImpactParameterProngSqSumMin, charmHadronSelectionNames.at(kImpactParameterProngSqSumMin), config.impactParameterProngSqSumMin.value, limits::kLowerLimit, true, true, false);
253+
} else {
254+
this->addSelection(kImpactParameterProductMax, charmHadronSelectionNames.at(kImpactParameterProductMax), config.impactParameterProductMax.value, limits::kUpperLimit, true, true, false);
255+
this->addSelection(kCosThetaStarMax, charmHadronSelectionNames.at(kCosThetaStarMax), config.cosThetaStarMax.value, limits::kAbsUpperLimit, true, true, false);
256+
}
190257

191258
this->template setupSelectionHistogram<SelectionHistName>(registry);
192259
this->template setupFilterHistogram<FilterHistName>(
@@ -214,8 +281,14 @@ class CharmHadronSelection : public baseselection::BaseSelection<float, o2::anal
214281
this->reset();
215282
this->evaluateObservable(kCpaMin, candidate.cpa());
216283
this->evaluateObservable(kDecayLengthMin, candidate.decayLength());
217-
this->evaluateObservable(kImpactParameterProductMax, candidate.impactParameter0() * candidate.impactParameter1());
218-
this->evaluateObservable(kCosThetaStarMax, mHfHelper.cosThetaStarD0(candidate));
284+
285+
if constexpr (isThreeProng<hadronType>()) {
286+
this->evaluateObservable(kChi2PcaMax, candidate.chi2PCA());
287+
this->evaluateObservable(kImpactParameterProngSqSumMin, candidate.impactParameterProngSqSum());
288+
} else {
289+
this->evaluateObservable(kImpactParameterProductMax, candidate.impactParameter0() * candidate.impactParameter1());
290+
this->evaluateObservable(kCosThetaStarMax, mHfHelper.cosThetaStarD0(candidate));
291+
}
219292
this->template assembleBitmask<SelectionHistName>();
220293
}
221294

@@ -226,24 +299,31 @@ class CharmHadronSelection : public baseselection::BaseSelection<float, o2::anal
226299
bool p = false;
227300

228301
// decay channel
229-
p = (candidate.hfflag() & (1 << o2::aod::hf_cand_2prong::DecayType::D0ToPiK)) != 0;
302+
if constexpr (isThreeProng<hadronType>()) {
303+
p = (candidate.hfflag() & (1 << o2::aod::hf_cand_3prong::DecayType::LcToPKPi)) != 0;
304+
} else {
305+
p = (candidate.hfflag() & (1 << o2::aod::hf_cand_2prong::DecayType::D0ToPiK)) != 0;
306+
}
230307
this->template fillFilter<FilterHistName>(kDecayChannel, p);
231308
pass &= p;
232309

233310
if constexpr (modes::isEqual(hadronType, modes::CharmHadron::kD0)) {
234311
p = candidate.isSelD0();
235-
} else {
312+
} else if constexpr (modes::isEqual(hadronType, modes::CharmHadron::kD0Bar)) {
236313
p = candidate.isSelD0bar();
314+
} else { // kLc / kLcBar
315+
p = candidate.isSelLcToPKPi() || candidate.isSelLcToPiKP();
237316
}
238-
239317
this->template fillFilter<FilterHistName>(kHypothesis, p);
240318
pass &= p;
241319

242320
bool competing = false;
243321
if constexpr (modes::isEqual(hadronType, modes::CharmHadron::kD0)) {
244322
competing = candidate.isSelD0bar();
245-
} else {
323+
} else if constexpr (modes::isEqual(hadronType, modes::CharmHadron::kD0Bar)) {
246324
competing = candidate.isSelD0();
325+
} else { // Lc / Lcbar: ambiguous means both mass hypotheses passed
326+
competing = candidate.isSelLcToPKPi() && candidate.isSelLcToPiKP();
247327
}
248328
p = !mRejectAmbiguousHypothesis || !competing;
249329
this->template fillFilter<FilterHistName>(kRejectAmbiguous, p);
@@ -275,7 +355,12 @@ class CharmHadronSelection : public baseselection::BaseSelection<float, o2::anal
275355

276356
// rapidity
277357
if (mUseYCut) {
278-
const float y = mHfHelper.yD0(candidate);
358+
float y = 0.f;
359+
if constexpr (isThreeProng<hadronType>()) {
360+
y = mHfHelper.yLc(candidate);
361+
} else {
362+
y = mHfHelper.yD0(candidate);
363+
}
279364
p = y > mYMin;
280365
this->template fillFilter<FilterHistName>(kYMin, p);
281366
pass &= p;
@@ -286,7 +371,7 @@ class CharmHadronSelection : public baseselection::BaseSelection<float, o2::anal
286371
}
287372

288373
this->template fillFilterSummary<FilterHistName>(pass);
289-
return this->isPassThrough() || pass;
374+
return this->isPassThrough() || pass;ż
290375
}
291376

292377
[[nodiscard]] float getMassMin() const

PWGCF/Femto/Core/modes.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,8 +174,8 @@ enum class TwoTrackResonance : o2::analysis::femto::datatypes::TwoTrackResonance
174174
enum class CharmHadron : o2::analysis::femto::datatypes::CharmHadronType {
175175
kD0,
176176
kD0Bar,
177-
kDplus,
178-
kLc
177+
kLc,
178+
kLcBar
179179
};
180180

181181
enum class QvecDetector : o2::analysis::femto::datatypes::QvecDetectorType {

PWGCF/Femto/Core/partitions.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,23 @@
179179
(o2::aod::femtobase::stored::mass < (selection).massMax) && \
180180
ncheckbit(o2::aod::femtocharmhadrons::mask, (selection).mask)
181181

182+
// partition for 3-prong charm hadrons
183+
// sign of signedPt encodes particle (+) / antiparticle (-)
184+
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
185+
#define MAKE_CHARM3PRONG_PARTITION(selection) \
186+
ifnode((selection).sign.node() != 0, \
187+
ifnode((selection).sign.node() > 0, o2::aod::femtobase::stored::signedPt > 0.f, o2::aod::femtobase::stored::signedPt < 0.f), true) && \
188+
(nabs(o2::aod::femtobase::stored::signedPt) > (selection).ptMin) && \
189+
(nabs(o2::aod::femtobase::stored::signedPt) < (selection).ptMax) && \
190+
(o2::aod::femtobase::stored::eta > (selection).etaMin) && \
191+
(o2::aod::femtobase::stored::eta < (selection).etaMax) && \
192+
(o2::aod::femtobase::stored::phi > (selection).phiMin) && \
193+
(o2::aod::femtobase::stored::phi < (selection).phiMax) && \
194+
(o2::aod::femtobase::stored::mass > (selection).massMin) && \
195+
(o2::aod::femtobase::stored::mass < (selection).massMax) && \
196+
ncheckbit(o2::aod::femtocharmhadrons::mask, (selection).mask)
197+
198+
182199
// macros for mc collisions (mc only)
183200
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
184201
#define MAKE_MC_COLLISION_FILTER(selection) \

0 commit comments

Comments
 (0)