Skip to content

Commit b286a24

Browse files
[PWGCF] Fix TOF selection logic of daughter tracks in femto framework (#18202)
Co-authored-by: ALICE Action Bot <alibuild@cern.ch>
1 parent 0ebec5c commit b286a24

9 files changed

Lines changed: 119 additions & 58 deletions

File tree

‎PWGCF/Femto/Core/baseSelection.h‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,11 @@ class BaseSelection
283283
/// \param comments Vector of comment strings, one per selection threshold.
284284
void addComments(int observableIndex, std::vector<std::string> const& comments) { mSelectionContainers.at(observableIndex).addComments(comments); }
285285

286+
/// \brief Attach the same comment to every selection threshold of a specific observable.
287+
/// \param observableIndex Index of the observable.
288+
/// \param comment Comment string applied to all thresholds.
289+
void addComments(int observableIndex, std::string const& comment) { mSelectionContainers.at(observableIndex).addComments(comment); }
290+
286291
[[nodiscard]] bool isPassThrough() const { return mPassThrough; }
287292

288293
/// \brief Check whether all required and optional cuts are passed.

‎PWGCF/Femto/Core/cascadeBuilder.h‎

Lines changed: 34 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include <array>
3434
#include <cmath>
3535
#include <cstdint>
36+
#include <limits>
3637
#include <string>
3738
#include <unordered_map>
3839
#include <vector>
@@ -63,25 +64,25 @@ struct ConfCascadeFilters : o2::framework::ConfigurableGroup {
6364
};
6465

6566
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
66-
#define CASCADE_DEFAULT_BITS \
67-
o2::framework::Configurable<bool> passThrough{"passThrough", false, "If true, all Cascades are passed through. Bits for all selections are stored."}; \
68-
o2::framework::Configurable<std::vector<std::string>> cascadeCpaMin{"cascadeCpaMin", {"0.95"}, "Minimum CPA as TFormula, x=pt"}; \
69-
o2::framework::Configurable<std::vector<std::string>> cascadePaMax{"cascadePaMax", {}, "Maximum pointing angle (rad) as TFormula, x=pt. Empty to disable"}; \
70-
o2::framework::Configurable<std::vector<float>> cascadeTransRadMin{"cascadeTransRadMin", {0.9f}, "Minimum transverse radius (cm)"}; \
71-
o2::framework::Configurable<std::vector<float>> cascadeDcaDauMax{"cascadeDcaDauMax", {0.25f}, "Maximum DCA between the daughters at decay vertex (cm)"}; \
72-
o2::framework::Configurable<std::vector<float>> lambdaCpaMin{"lambdaCpaMin", {0.78f}, "Minimum cosine of pointing angle"}; \
73-
o2::framework::Configurable<std::vector<float>> lambdaTransRadMin{"lambdaTransRadMin", {0.9f}, "Minimum transverse radius (cm)"}; \
74-
o2::framework::Configurable<std::vector<float>> lambdaDcaDauMax{"lambdaDcaDauMax", {0.5f}, "Maximum DCA between the daughters at decay vertex (cm)"}; \
75-
o2::framework::Configurable<std::vector<float>> lambdaDcaToPvMin{"lambdaDcaToPvMin", {0.3f}, "Minimum DCA between the lambda and primary vertex"}; \
76-
o2::framework::Configurable<std::vector<float>> dauAbsEtaMax{"dauAbsEtaMax", {0.8f}, "Maximum |eta| of all daughters"}; \
77-
o2::framework::Configurable<std::vector<float>> dauAbsDcaxyMin{"dauAbsDcaxyMin", {0.05f}, "Minimum |DCAxy| of the daughters and bachelor from primary vertex (cm)"}; \
78-
o2::framework::Configurable<std::vector<float>> dauTpcClustersMin{"dauTpcClustersMin", {80.f}, "Minimum number of TPC clusters for daughter tracks"}; \
79-
o2::framework::Configurable<std::vector<float>> posDauTpc{"posDauTpc", {5.f}, "Maximum |nsigma_Pion/Proton| TPC for positive daughter tracks"}; \
80-
o2::framework::Configurable<std::vector<float>> negDauTpc{"negDauTpc", {5.f}, "Maximum |nsigma_Pion/Proton| TPC for negative daughter tracks"}; \
81-
o2::framework::Configurable<std::vector<float>> posDauTof{"posDauTof", {}, "Maximum |nsigma_Pion/Proton| TOF for positive daughter tracks"}; \
82-
o2::framework::Configurable<std::vector<float>> negDauTof{"negDauTof", {}, "Maximum |nsigma_Pion/Proton| TOF for negative daughter tracks"}; \
83-
o2::framework::Configurable<bool> requireTof{"requireTof", false, "If true, TOF PID is a mandatory selection"}; \
84-
o2::framework::Configurable<bool> keepTracksWithoutTof{"keepTracksWithoutTof", true, "If true, candidates whose daughters have no TOF signal are kept"};
67+
#define CASCADE_DEFAULT_BITS \
68+
o2::framework::Configurable<bool> passThrough{"passThrough", false, "If true, all Cascades are passed through. Bits for all selections are stored."}; \
69+
o2::framework::Configurable<std::vector<std::string>> cascadeCpaMin{"cascadeCpaMin", {"0.95"}, "Minimum CPA as TFormula, x=pt"}; \
70+
o2::framework::Configurable<std::vector<std::string>> cascadePaMax{"cascadePaMax", {}, "Maximum pointing angle (rad) as TFormula, x=pt. Empty to disable"}; \
71+
o2::framework::Configurable<std::vector<float>> cascadeTransRadMin{"cascadeTransRadMin", {0.9f}, "Minimum transverse radius (cm)"}; \
72+
o2::framework::Configurable<std::vector<float>> cascadeDcaDauMax{"cascadeDcaDauMax", {0.25f}, "Maximum DCA between the daughters at decay vertex (cm)"}; \
73+
o2::framework::Configurable<std::vector<float>> lambdaCpaMin{"lambdaCpaMin", {0.78f}, "Minimum cosine of pointing angle"}; \
74+
o2::framework::Configurable<std::vector<float>> lambdaTransRadMin{"lambdaTransRadMin", {0.9f}, "Minimum transverse radius (cm)"}; \
75+
o2::framework::Configurable<std::vector<float>> lambdaDcaDauMax{"lambdaDcaDauMax", {0.5f}, "Maximum DCA between the daughters at decay vertex (cm)"}; \
76+
o2::framework::Configurable<std::vector<float>> lambdaDcaToPvMin{"lambdaDcaToPvMin", {0.3f}, "Minimum DCA between the lambda and primary vertex"}; \
77+
o2::framework::Configurable<std::vector<float>> dauAbsEtaMax{"dauAbsEtaMax", {0.8f}, "Maximum |eta| of all daughters"}; \
78+
o2::framework::Configurable<std::vector<float>> dauAbsDcaxyMin{"dauAbsDcaxyMin", {0.05f}, "Minimum |DCAxy| of the daughters and bachelor from primary vertex (cm)"}; \
79+
o2::framework::Configurable<std::vector<float>> dauTpcClustersMin{"dauTpcClustersMin", {80.f}, "Minimum number of TPC clusters for daughter tracks"}; \
80+
o2::framework::Configurable<std::vector<float>> posDauTpc{"posDauTpc", {5.f}, "Maximum |nsigma_Pion/Proton| TPC for positive daughter tracks"}; \
81+
o2::framework::Configurable<std::vector<float>> negDauTpc{"negDauTpc", {5.f}, "Maximum |nsigma_Pion/Proton| TPC for negative daughter tracks"}; \
82+
o2::framework::Configurable<std::vector<float>> posDauTof{"posDauTof", {}, "Maximum |nsigma_Pion/Proton| TOF for positive daughter tracks"}; \
83+
o2::framework::Configurable<std::vector<float>> negDauTof{"negDauTof", {}, "Maximum |nsigma_Pion/Proton| TOF for negative daughter tracks"}; \
84+
o2::framework::Configurable<bool> requireTof{"requireTof", false, "If true, the TOF PID cut is mandatory. For daughters with a TOF signal, the candidate is rejected on failure. For daughters without a TOF signal, the candidate is rejected unless keepTracksWithoutTof is true"}; \
85+
o2::framework::Configurable<bool> keepTracksWithoutTof{"keepTracksWithoutTof", true, "If true, daughters without a TOF signal pass the TOF PID cut unconditionally, overriding requireTof. If false, daughters without a TOF signal fail the TOF cut (and are rejected if requireTof is true)"};
8586

8687
struct ConfXiBits : o2::framework::ConfigurableGroup {
8788
std::string prefix = std::string("XiBits");
@@ -248,6 +249,7 @@ class CascadeSelection : public baseselection::BaseSelection<float, o2::analysis
248249
mLambdaMassMax = filter.massLambdaMax.value;
249250
mRequireTof = config.requireTof.value;
250251
mKeepTracksWithoutTof = config.keepTracksWithoutTof.value;
252+
const std::string tofComment = "requireTof = " + std::to_string(mRequireTof) + "; keepTracksWithoutTof = " + std::to_string(mKeepTracksWithoutTof);
251253

252254
if constexpr (modes::isEqual(cascadeType, modes::Cascade::kXi)) {
253255
mXiMassLowerLimit = filter.massXiMin.value;
@@ -256,7 +258,8 @@ class CascadeSelection : public baseselection::BaseSelection<float, o2::analysis
256258
mOmegaMassLowerLimit = filter.rejectMassOmegaMin.value;
257259
mOmegaMassUpperLimit = filter.rejectMassOmegaMax.value;
258260
this->addSelection(kBachelorTpcPion, cascadeSelectionNames.at(kBachelorTpcPion), config.bachelorTpcPion.value, limits::kAbsUpperLimit, true, true, false);
259-
this->addSelection(kBachelorTofPion, cascadeSelectionNames.at(kBachelorTofPion), config.bachelorTofPion.value, limits::kAbsUpperLimit, true, mRequireTof, false);
261+
this->addSelection(kBachelorTofPion, cascadeSelectionNames.at(kBachelorTofPion), config.bachelorTofPion.value, limits::kAbsUpperLimit, mRequireTof, mRequireTof, false);
262+
this->addComments(kBachelorTofPion, tofComment);
260263
}
261264
if constexpr (modes::isEqual(cascadeType, modes::Cascade::kOmega)) {
262265
mOmegaMassLowerLimit = filter.massOmegaMin.value;
@@ -265,13 +268,16 @@ class CascadeSelection : public baseselection::BaseSelection<float, o2::analysis
265268
mXiMassLowerLimit = filter.rejectMassXiMin.value;
266269
mXiMassUpperLimit = filter.rejectMassXiMax.value;
267270
this->addSelection(kBachelorTpcKaon, cascadeSelectionNames.at(kBachelorTpcKaon), config.bachelorTpcKaon.value, limits::kAbsUpperLimit, true, true, false);
268-
this->addSelection(kBachelorTofKaon, cascadeSelectionNames.at(kBachelorTofKaon), config.bachelorTofKaon.value, limits::kAbsUpperLimit, true, mRequireTof, false);
271+
this->addSelection(kBachelorTofKaon, cascadeSelectionNames.at(kBachelorTofKaon), config.bachelorTofKaon.value, limits::kAbsUpperLimit, mRequireTof, mRequireTof, false);
272+
this->addComments(kBachelorTofKaon, tofComment);
269273
}
270274

271275
this->addSelection(kPosDauTpc, cascadeSelectionNames.at(kPosDauTpc), config.posDauTpc.value, limits::kAbsUpperLimit, true, true, false);
272276
this->addSelection(kNegDauTpc, cascadeSelectionNames.at(kNegDauTpc), config.negDauTpc.value, limits::kAbsUpperLimit, true, true, false);
273-
this->addSelection(kPosDauTof, cascadeSelectionNames.at(kPosDauTof), config.posDauTof.value, limits::kAbsUpperLimit, true, mRequireTof, false);
274-
this->addSelection(kNegDauTof, cascadeSelectionNames.at(kNegDauTof), config.negDauTof.value, limits::kAbsUpperLimit, true, mRequireTof, false);
277+
this->addSelection(kPosDauTof, cascadeSelectionNames.at(kPosDauTof), config.posDauTof.value, limits::kAbsUpperLimit, mRequireTof, mRequireTof, false);
278+
this->addSelection(kNegDauTof, cascadeSelectionNames.at(kNegDauTof), config.negDauTof.value, limits::kAbsUpperLimit, mRequireTof, mRequireTof, false);
279+
this->addComments(kPosDauTof, tofComment);
280+
this->addComments(kNegDauTof, tofComment);
275281

276282
this->addSelection(kCascadeCpaMin, cascadeSelectionNames.at(kCascadeCpaMin), filter.ptMin.value, filter.ptMax.value, config.cascadeCpaMin.value, limits::kLowerFunctionLimit, true, true, false);
277283
this->addSelection(kCascadePaMax, cascadeSelectionNames.at(kCascadePaMax), filter.ptMin.value, filter.ptMax.value, config.cascadePaMax.value, limits::kUpperFunctionLimit, true, true, false);
@@ -341,14 +347,18 @@ class CascadeSelection : public baseselection::BaseSelection<float, o2::analysis
341347

342348
// pid selections
343349
// TPC nSigma comes from the daughter track, TOF nSigma and the has-TOF flags from the cascade candidate
344-
// if a daughter has no TOF signal, feed 0 so the bit passes any limit (opt-in via keepTracksWithoutTof)
350+
// if a daughter has no TOF signal: feed 0 so the bit passes any limit if keepTracksWithoutTof is set,
351+
// overriding requireTof; otherwise feed a value that fails every limit, so that if requireTof is also
352+
// true, the mandatory TOF cut correctly rejects the candidate instead of silently letting it through
345353
auto evaluatePid = [this](CascadeSels tpcBit, float tpcNSigma,
346354
CascadeSels tofBit, float tofNSigma, bool hasTof) {
347355
this->evaluateObservable(tpcBit, tpcNSigma);
348356
if (hasTof) {
349357
this->evaluateObservable(tofBit, tofNSigma);
350358
} else if (mKeepTracksWithoutTof) {
351359
this->evaluateObservable(tofBit, 0.f);
360+
} else {
361+
this->evaluateObservable(tofBit, std::numeric_limits<float>::max());
352362
}
353363
};
354364

‎PWGCF/Femto/Core/charmHadronBuilder.h‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ enum CharmHadronFilters {
177177
kEtaMax,
178178
kPhiMin,
179179
kPhiMax,
180+
kUseYCut,
180181
kYMin,
181182
kYMax,
182183
kMassMin,
@@ -198,6 +199,7 @@ const std::unordered_map<CharmHadronFilters, std::string> charmHadronFilterNames
198199
{kEtaMax, "Maximum eta"},
199200
{kPhiMin, "Minimum phi"},
200201
{kPhiMax, "Maximum phi"},
202+
{kUseYCut, "Use rapidity cut instead of eta cut"},
201203
{kYMin, "Minimum rapidity"},
202204
{kYMax, "Maximum rapidity"},
203205
{kMassMin, "Minimum invariant mass"},
@@ -266,6 +268,7 @@ class CharmHadronSelection : public baseselection::BaseSelection<float, o2::anal
266268
{charmHadronFilterNames.at(kEtaMax), mEtaMax},
267269
{charmHadronFilterNames.at(kPhiMin), mPhiMin},
268270
{charmHadronFilterNames.at(kPhiMax), mPhiMax},
271+
{charmHadronFilterNames.at(kUseYCut), static_cast<float>(mUseYCut)},
269272
{charmHadronFilterNames.at(kYMin), mYMin},
270273
{charmHadronFilterNames.at(kYMax), mYMax},
271274
{charmHadronFilterNames.at(kMassMin), mMassMin},

‎PWGCF/Femto/Core/collisionBuilder.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,7 @@ class CollisionSelection : public baseselection::BaseSelection<float, o2::analys
262262

263263
this->addSelection(kSel8, collisionSelectionNames.at(kSel8), config.sel8.value);
264264
this->addSelection(kRctFlags, collisionSelectionNames.at(kRctFlags), config.rctFlags.value);
265+
this->addComments(kRctFlags, "label = " + confRct.label.value + "; useZdc = " + std::to_string(confRct.useZdc.value) + "; treatLimitedAcceptanceAsBad = " + std::to_string(confRct.treatLimitedAcceptanceAsBad.value));
265266
this->addSelection(kNoSameBunchPileUp, collisionSelectionNames.at(kNoSameBunchPileUp), config.noSameBunchPileup.value);
266267
this->addSelection(kIsVertexItsTpc, collisionSelectionNames.at(kIsVertexItsTpc), config.isVertexItsTpc.value);
267268
this->addSelection(kIsGoodZvtxFt0VsPv, collisionSelectionNames.at(kIsGoodZvtxFt0VsPv), config.isGoodZvtxFt0VsPv.value);

‎PWGCF/Femto/Core/kinkBuilder.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,7 @@ class KinkSelection : public baseselection::BaseSelection<float, o2::analysis::f
245245
mKeepTracksWithoutTof = config.keepTracksWithoutTof.value;
246246
this->addSelection(kChaDaughTpcProton, kinkSelectionNames.at(kChaDaughTpcProton), config.chaDauTpcProton.value, limits::kAbsUpperLimit, true, true, false);
247247
this->addSelection(kChaDaughTofProton, kinkSelectionNames.at(kChaDaughTofProton), config.chaDauTofProton.value, limits::kAbsUpperLimit, true, config.requireTof.value, false);
248+
this->addComments(kChaDaughTofProton, "requireTof = " + std::to_string(config.requireTof.value) + "; keepTracksWithoutTof = " + std::to_string(mKeepTracksWithoutTof));
248249
}
249250

250251
this->addSelection(kKinkTopoDcaMax, kinkSelectionNames.at(kKinkTopoDcaMax), config.kinkTopoDcaMax.value, limits::kUpperLimit, true, true, false);

‎PWGCF/Femto/Core/photonBuilder.h‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040

4141
#include <algorithm>
4242
#include <array>
43+
#include <limits>
4344
#include <string>
4445
#include <unordered_map>
4546
#include <vector>
@@ -179,7 +180,9 @@ class PhotonSelection : public baseselection::BaseSelection<float, datatypes::Ph
179180
this->addSelection(kV0RadiusMax, photonSelectionNames.at(kV0RadiusMax), config.v0RadiusMax.value, limits::kUpperLimit, true, true, false);
180181
this->addSelection(kDauAbsEtaMax, photonSelectionNames.at(kDauAbsEtaMax), config.dauAbsEtaMax.value, limits::kAbsUpperLimit, true, true, false);
181182
this->addSelection(kPosDauTpcNSigmaEl, photonSelectionNames.at(kPosDauTpcNSigmaEl), config.dauTpcNSigmaElAbsMax.value, limits::kAbsUpperLimit, true, true, false);
183+
this->addComments(kPosDauTpcNSigmaEl, "keepDaughtersWithoutTpc = " + std::to_string(mKeepDaughtersWithoutTpc));
182184
this->addSelection(kNegDauTpcNSigmaEl, photonSelectionNames.at(kNegDauTpcNSigmaEl), config.dauTpcNSigmaElAbsMax.value, limits::kAbsUpperLimit, true, true, false);
185+
this->addComments(kNegDauTpcNSigmaEl, "keepDaughtersWithoutTpc = " + std::to_string(mKeepDaughtersWithoutTpc));
183186

184187
this->setupSelectionHistogram<SelectionHistName>(registry);
185188
this->template setupFilterHistogram<FilterHistName>(
@@ -213,12 +216,17 @@ class PhotonSelection : public baseselection::BaseSelection<float, datatypes::Ph
213216
this->evaluateObservable(kDauAbsEtaMax, *std::max_element(etaAbsDaughters.begin(), etaAbsDaughters.end()));
214217

215218
// TPC electron PID: evaluate only when a TPC signal is available, matching
216-
// EMPhotonFilter::isSelectedSecondary(), which never rejects a daughter without TPC
219+
// EMPhotonFilter::isSelectedSecondary(), which never rejects a daughter without TPC.
220+
// If keepDaughtersWithoutTpc is false, feed a value that fails every limit instead of
221+
// skipping evaluation entirely, so this (always mandatory) cut correctly rejects the
222+
// candidate instead of silently letting it through
217223
auto evaluateDaughterTpcEl = [this](PhotonSels bit, bool hasTpc, float tpcNSigmaEl) {
218224
if (hasTpc) {
219225
this->evaluateObservable(bit, tpcNSigmaEl);
220226
} else if (mKeepDaughtersWithoutTpc) {
221227
this->evaluateObservable(bit, 0.f);
228+
} else {
229+
this->evaluateObservable(bit, std::numeric_limits<float>::max());
222230
}
223231
};
224232
evaluateDaughterTpcEl(kPosDauTpcNSigmaEl, posDaughter.hasTPC(), posDaughter.tpcNSigmaEl());

‎PWGCF/Femto/Core/selectionContainer.h‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,13 @@ class SelectionContainer
236236
mComments = comments;
237237
}
238238

239+
/// \brief Attach the same comment to every threshold of this selection.
240+
/// \param comment Comment string applied to all thresholds.
241+
void addComments(std::string const& comment)
242+
{
243+
mComments = std::vector<std::string>(getNSelections(), comment);
244+
}
245+
239246
/// \brief Get comments attached to the selection thresholds.
240247
/// \return Vector of comment strings.
241248
[[nodiscard]] std::string getComment(int selectionIndex) const

0 commit comments

Comments
 (0)