Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions PWGCF/Femto/Core/baseSelection.h
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,11 @@ class BaseSelection
/// \param comments Vector of comment strings, one per selection threshold.
void addComments(int observableIndex, std::vector<std::string> const& comments) { mSelectionContainers.at(observableIndex).addComments(comments); }

/// \brief Attach the same comment to every selection threshold of a specific observable.
/// \param observableIndex Index of the observable.
/// \param comment Comment string applied to all thresholds.
void addComments(int observableIndex, std::string const& comment) { mSelectionContainers.at(observableIndex).addComments(comment); }

[[nodiscard]] bool isPassThrough() const { return mPassThrough; }

/// \brief Check whether all required and optional cuts are passed.
Expand Down
58 changes: 34 additions & 24 deletions PWGCF/Femto/Core/cascadeBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include <array>
#include <cmath>
#include <cstdint>
#include <limits>
#include <string>
#include <unordered_map>
#include <vector>
Expand Down Expand Up @@ -63,25 +64,25 @@ struct ConfCascadeFilters : o2::framework::ConfigurableGroup {
};

// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define CASCADE_DEFAULT_BITS \
o2::framework::Configurable<bool> passThrough{"passThrough", false, "If true, all Cascades are passed through. Bits for all selections are stored."}; \
o2::framework::Configurable<std::vector<std::string>> cascadeCpaMin{"cascadeCpaMin", {"0.95"}, "Minimum CPA as TFormula, x=pt"}; \
o2::framework::Configurable<std::vector<std::string>> cascadePaMax{"cascadePaMax", {}, "Maximum pointing angle (rad) as TFormula, x=pt. Empty to disable"}; \
o2::framework::Configurable<std::vector<float>> cascadeTransRadMin{"cascadeTransRadMin", {0.9f}, "Minimum transverse radius (cm)"}; \
o2::framework::Configurable<std::vector<float>> cascadeDcaDauMax{"cascadeDcaDauMax", {0.25f}, "Maximum DCA between the daughters at decay vertex (cm)"}; \
o2::framework::Configurable<std::vector<float>> lambdaCpaMin{"lambdaCpaMin", {0.78f}, "Minimum cosine of pointing angle"}; \
o2::framework::Configurable<std::vector<float>> lambdaTransRadMin{"lambdaTransRadMin", {0.9f}, "Minimum transverse radius (cm)"}; \
o2::framework::Configurable<std::vector<float>> lambdaDcaDauMax{"lambdaDcaDauMax", {0.5f}, "Maximum DCA between the daughters at decay vertex (cm)"}; \
o2::framework::Configurable<std::vector<float>> lambdaDcaToPvMin{"lambdaDcaToPvMin", {0.3f}, "Minimum DCA between the lambda and primary vertex"}; \
o2::framework::Configurable<std::vector<float>> dauAbsEtaMax{"dauAbsEtaMax", {0.8f}, "Maximum |eta| of all daughters"}; \
o2::framework::Configurable<std::vector<float>> dauAbsDcaxyMin{"dauAbsDcaxyMin", {0.05f}, "Minimum |DCAxy| of the daughters and bachelor from primary vertex (cm)"}; \
o2::framework::Configurable<std::vector<float>> dauTpcClustersMin{"dauTpcClustersMin", {80.f}, "Minimum number of TPC clusters for daughter tracks"}; \
o2::framework::Configurable<std::vector<float>> posDauTpc{"posDauTpc", {5.f}, "Maximum |nsigma_Pion/Proton| TPC for positive daughter tracks"}; \
o2::framework::Configurable<std::vector<float>> negDauTpc{"negDauTpc", {5.f}, "Maximum |nsigma_Pion/Proton| TPC for negative daughter tracks"}; \
o2::framework::Configurable<std::vector<float>> posDauTof{"posDauTof", {}, "Maximum |nsigma_Pion/Proton| TOF for positive daughter tracks"}; \
o2::framework::Configurable<std::vector<float>> negDauTof{"negDauTof", {}, "Maximum |nsigma_Pion/Proton| TOF for negative daughter tracks"}; \
o2::framework::Configurable<bool> requireTof{"requireTof", false, "If true, TOF PID is a mandatory selection"}; \
o2::framework::Configurable<bool> keepTracksWithoutTof{"keepTracksWithoutTof", true, "If true, candidates whose daughters have no TOF signal are kept"};
#define CASCADE_DEFAULT_BITS \
o2::framework::Configurable<bool> passThrough{"passThrough", false, "If true, all Cascades are passed through. Bits for all selections are stored."}; \
o2::framework::Configurable<std::vector<std::string>> cascadeCpaMin{"cascadeCpaMin", {"0.95"}, "Minimum CPA as TFormula, x=pt"}; \
o2::framework::Configurable<std::vector<std::string>> cascadePaMax{"cascadePaMax", {}, "Maximum pointing angle (rad) as TFormula, x=pt. Empty to disable"}; \
o2::framework::Configurable<std::vector<float>> cascadeTransRadMin{"cascadeTransRadMin", {0.9f}, "Minimum transverse radius (cm)"}; \
o2::framework::Configurable<std::vector<float>> cascadeDcaDauMax{"cascadeDcaDauMax", {0.25f}, "Maximum DCA between the daughters at decay vertex (cm)"}; \
o2::framework::Configurable<std::vector<float>> lambdaCpaMin{"lambdaCpaMin", {0.78f}, "Minimum cosine of pointing angle"}; \
o2::framework::Configurable<std::vector<float>> lambdaTransRadMin{"lambdaTransRadMin", {0.9f}, "Minimum transverse radius (cm)"}; \
o2::framework::Configurable<std::vector<float>> lambdaDcaDauMax{"lambdaDcaDauMax", {0.5f}, "Maximum DCA between the daughters at decay vertex (cm)"}; \
o2::framework::Configurable<std::vector<float>> lambdaDcaToPvMin{"lambdaDcaToPvMin", {0.3f}, "Minimum DCA between the lambda and primary vertex"}; \
o2::framework::Configurable<std::vector<float>> dauAbsEtaMax{"dauAbsEtaMax", {0.8f}, "Maximum |eta| of all daughters"}; \
o2::framework::Configurable<std::vector<float>> dauAbsDcaxyMin{"dauAbsDcaxyMin", {0.05f}, "Minimum |DCAxy| of the daughters and bachelor from primary vertex (cm)"}; \
o2::framework::Configurable<std::vector<float>> dauTpcClustersMin{"dauTpcClustersMin", {80.f}, "Minimum number of TPC clusters for daughter tracks"}; \
o2::framework::Configurable<std::vector<float>> posDauTpc{"posDauTpc", {5.f}, "Maximum |nsigma_Pion/Proton| TPC for positive daughter tracks"}; \
o2::framework::Configurable<std::vector<float>> negDauTpc{"negDauTpc", {5.f}, "Maximum |nsigma_Pion/Proton| TPC for negative daughter tracks"}; \
o2::framework::Configurable<std::vector<float>> posDauTof{"posDauTof", {}, "Maximum |nsigma_Pion/Proton| TOF for positive daughter tracks"}; \
o2::framework::Configurable<std::vector<float>> negDauTof{"negDauTof", {}, "Maximum |nsigma_Pion/Proton| TOF for negative daughter tracks"}; \
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"}; \
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)"};

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

if constexpr (modes::isEqual(cascadeType, modes::Cascade::kXi)) {
mXiMassLowerLimit = filter.massXiMin.value;
Expand All @@ -256,7 +258,8 @@ class CascadeSelection : public baseselection::BaseSelection<float, o2::analysis
mOmegaMassLowerLimit = filter.rejectMassOmegaMin.value;
mOmegaMassUpperLimit = filter.rejectMassOmegaMax.value;
this->addSelection(kBachelorTpcPion, cascadeSelectionNames.at(kBachelorTpcPion), config.bachelorTpcPion.value, limits::kAbsUpperLimit, true, true, false);
this->addSelection(kBachelorTofPion, cascadeSelectionNames.at(kBachelorTofPion), config.bachelorTofPion.value, limits::kAbsUpperLimit, true, mRequireTof, false);
this->addSelection(kBachelorTofPion, cascadeSelectionNames.at(kBachelorTofPion), config.bachelorTofPion.value, limits::kAbsUpperLimit, mRequireTof, mRequireTof, false);
this->addComments(kBachelorTofPion, tofComment);
}
if constexpr (modes::isEqual(cascadeType, modes::Cascade::kOmega)) {
mOmegaMassLowerLimit = filter.massOmegaMin.value;
Expand All @@ -265,13 +268,16 @@ class CascadeSelection : public baseselection::BaseSelection<float, o2::analysis
mXiMassLowerLimit = filter.rejectMassXiMin.value;
mXiMassUpperLimit = filter.rejectMassXiMax.value;
this->addSelection(kBachelorTpcKaon, cascadeSelectionNames.at(kBachelorTpcKaon), config.bachelorTpcKaon.value, limits::kAbsUpperLimit, true, true, false);
this->addSelection(kBachelorTofKaon, cascadeSelectionNames.at(kBachelorTofKaon), config.bachelorTofKaon.value, limits::kAbsUpperLimit, true, mRequireTof, false);
this->addSelection(kBachelorTofKaon, cascadeSelectionNames.at(kBachelorTofKaon), config.bachelorTofKaon.value, limits::kAbsUpperLimit, mRequireTof, mRequireTof, false);
this->addComments(kBachelorTofKaon, tofComment);
}

this->addSelection(kPosDauTpc, cascadeSelectionNames.at(kPosDauTpc), config.posDauTpc.value, limits::kAbsUpperLimit, true, true, false);
this->addSelection(kNegDauTpc, cascadeSelectionNames.at(kNegDauTpc), config.negDauTpc.value, limits::kAbsUpperLimit, true, true, false);
this->addSelection(kPosDauTof, cascadeSelectionNames.at(kPosDauTof), config.posDauTof.value, limits::kAbsUpperLimit, true, mRequireTof, false);
this->addSelection(kNegDauTof, cascadeSelectionNames.at(kNegDauTof), config.negDauTof.value, limits::kAbsUpperLimit, true, mRequireTof, false);
this->addSelection(kPosDauTof, cascadeSelectionNames.at(kPosDauTof), config.posDauTof.value, limits::kAbsUpperLimit, mRequireTof, mRequireTof, false);
this->addSelection(kNegDauTof, cascadeSelectionNames.at(kNegDauTof), config.negDauTof.value, limits::kAbsUpperLimit, mRequireTof, mRequireTof, false);
this->addComments(kPosDauTof, tofComment);
this->addComments(kNegDauTof, tofComment);

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

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

Expand Down
3 changes: 3 additions & 0 deletions PWGCF/Femto/Core/charmHadronBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ enum CharmHadronFilters {
kEtaMax,
kPhiMin,
kPhiMax,
kUseYCut,
kYMin,
kYMax,
kMassMin,
Expand All @@ -198,6 +199,7 @@ const std::unordered_map<CharmHadronFilters, std::string> charmHadronFilterNames
{kEtaMax, "Maximum eta"},
{kPhiMin, "Minimum phi"},
{kPhiMax, "Maximum phi"},
{kUseYCut, "Use rapidity cut instead of eta cut"},
{kYMin, "Minimum rapidity"},
{kYMax, "Maximum rapidity"},
{kMassMin, "Minimum invariant mass"},
Expand Down Expand Up @@ -266,6 +268,7 @@ class CharmHadronSelection : public baseselection::BaseSelection<float, o2::anal
{charmHadronFilterNames.at(kEtaMax), mEtaMax},
{charmHadronFilterNames.at(kPhiMin), mPhiMin},
{charmHadronFilterNames.at(kPhiMax), mPhiMax},
{charmHadronFilterNames.at(kUseYCut), static_cast<float>(mUseYCut)},
{charmHadronFilterNames.at(kYMin), mYMin},
{charmHadronFilterNames.at(kYMax), mYMax},
{charmHadronFilterNames.at(kMassMin), mMassMin},
Expand Down
1 change: 1 addition & 0 deletions PWGCF/Femto/Core/collisionBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ class CollisionSelection : public baseselection::BaseSelection<float, o2::analys

this->addSelection(kSel8, collisionSelectionNames.at(kSel8), config.sel8.value);
this->addSelection(kRctFlags, collisionSelectionNames.at(kRctFlags), config.rctFlags.value);
this->addComments(kRctFlags, "label = " + confRct.label.value + "; useZdc = " + std::to_string(confRct.useZdc.value) + "; treatLimitedAcceptanceAsBad = " + std::to_string(confRct.treatLimitedAcceptanceAsBad.value));
this->addSelection(kNoSameBunchPileUp, collisionSelectionNames.at(kNoSameBunchPileUp), config.noSameBunchPileup.value);
this->addSelection(kIsVertexItsTpc, collisionSelectionNames.at(kIsVertexItsTpc), config.isVertexItsTpc.value);
this->addSelection(kIsGoodZvtxFt0VsPv, collisionSelectionNames.at(kIsGoodZvtxFt0VsPv), config.isGoodZvtxFt0VsPv.value);
Expand Down
1 change: 1 addition & 0 deletions PWGCF/Femto/Core/kinkBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@ class KinkSelection : public baseselection::BaseSelection<float, o2::analysis::f
mKeepTracksWithoutTof = config.keepTracksWithoutTof.value;
this->addSelection(kChaDaughTpcProton, kinkSelectionNames.at(kChaDaughTpcProton), config.chaDauTpcProton.value, limits::kAbsUpperLimit, true, true, false);
this->addSelection(kChaDaughTofProton, kinkSelectionNames.at(kChaDaughTofProton), config.chaDauTofProton.value, limits::kAbsUpperLimit, true, config.requireTof.value, false);
this->addComments(kChaDaughTofProton, "requireTof = " + std::to_string(config.requireTof.value) + "; keepTracksWithoutTof = " + std::to_string(mKeepTracksWithoutTof));
}

this->addSelection(kKinkTopoDcaMax, kinkSelectionNames.at(kKinkTopoDcaMax), config.kinkTopoDcaMax.value, limits::kUpperLimit, true, true, false);
Expand Down
10 changes: 9 additions & 1 deletion PWGCF/Femto/Core/photonBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@

#include <algorithm>
#include <array>
#include <limits>
#include <string>
#include <unordered_map>
#include <vector>
Expand Down Expand Up @@ -179,7 +180,9 @@ class PhotonSelection : public baseselection::BaseSelection<float, datatypes::Ph
this->addSelection(kV0RadiusMax, photonSelectionNames.at(kV0RadiusMax), config.v0RadiusMax.value, limits::kUpperLimit, true, true, false);
this->addSelection(kDauAbsEtaMax, photonSelectionNames.at(kDauAbsEtaMax), config.dauAbsEtaMax.value, limits::kAbsUpperLimit, true, true, false);
this->addSelection(kPosDauTpcNSigmaEl, photonSelectionNames.at(kPosDauTpcNSigmaEl), config.dauTpcNSigmaElAbsMax.value, limits::kAbsUpperLimit, true, true, false);
this->addComments(kPosDauTpcNSigmaEl, "keepDaughtersWithoutTpc = " + std::to_string(mKeepDaughtersWithoutTpc));
this->addSelection(kNegDauTpcNSigmaEl, photonSelectionNames.at(kNegDauTpcNSigmaEl), config.dauTpcNSigmaElAbsMax.value, limits::kAbsUpperLimit, true, true, false);
this->addComments(kNegDauTpcNSigmaEl, "keepDaughtersWithoutTpc = " + std::to_string(mKeepDaughtersWithoutTpc));

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

// TPC electron PID: evaluate only when a TPC signal is available, matching
// EMPhotonFilter::isSelectedSecondary(), which never rejects a daughter without TPC
// EMPhotonFilter::isSelectedSecondary(), which never rejects a daughter without TPC.
// If keepDaughtersWithoutTpc is false, feed a value that fails every limit instead of
// skipping evaluation entirely, so this (always mandatory) cut correctly rejects the
// candidate instead of silently letting it through
auto evaluateDaughterTpcEl = [this](PhotonSels bit, bool hasTpc, float tpcNSigmaEl) {
if (hasTpc) {
this->evaluateObservable(bit, tpcNSigmaEl);
} else if (mKeepDaughtersWithoutTpc) {
this->evaluateObservable(bit, 0.f);
} else {
this->evaluateObservable(bit, std::numeric_limits<float>::max());
}
};
evaluateDaughterTpcEl(kPosDauTpcNSigmaEl, posDaughter.hasTPC(), posDaughter.tpcNSigmaEl());
Expand Down
7 changes: 7 additions & 0 deletions PWGCF/Femto/Core/selectionContainer.h
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,13 @@ class SelectionContainer
mComments = comments;
}

/// \brief Attach the same comment to every threshold of this selection.
/// \param comment Comment string applied to all thresholds.
void addComments(std::string const& comment)
{
mComments = std::vector<std::string>(getNSelections(), comment);
}

/// \brief Get comments attached to the selection thresholds.
/// \return Vector of comment strings.
[[nodiscard]] std::string getComment(int selectionIndex) const
Expand Down
Loading
Loading