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
7788template <auto & Prefix>
7889struct 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+
96127constexpr const char PrefixD0Selection1[] = " D0Selection1" ;
97128constexpr const char PrefixD0Selection2[] = " D0Selection2" ;
98129using ConfD0Selection1 = ConfD0Selection<PrefixD0Selection1>;
99130using 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
116156constexpr char D0SelHistName[] = " hD0Selection" ;
117157constexpr char D0barSelHistName[] = " hD0barSelection" ;
118158constexpr char CharmHadronSelsName[] = " Charm hadron selection object" ;
159+ constexpr char LcSelHistName[] = " hLcSelection" ;
160+ constexpr char LcBarSelHistName[] = " hLcBarSelection" ;
119161const 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).
127172enum CharmHadronFilters {
@@ -143,6 +188,8 @@ enum CharmHadronFilters {
143188
144189constexpr char D0FilterHistName[] = " hD0Filters" ;
145190constexpr char D0barFilterHistName[] = " hD0barFilters" ;
191+ constexpr char LcFilterHistName[] = " hLcFilters" ;
192+ constexpr char LcBarFilterHistName[] = " hLcBarFilters" ;
146193const 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+
161222template <modes::CharmHadron hadronType, auto & SelectionHistName, auto & FilterHistName>
162223class 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
0 commit comments