Skip to content

Commit a1b4417

Browse files
[PWGCF] Fixes in femto framework (#17007)
Co-authored-by: ALICE Action Bot <alibuild@cern.ch>
1 parent 4e6f5f7 commit a1b4417

11 files changed

Lines changed: 1297 additions & 537 deletions

File tree

PWGCF/Femto/Core/baseSelection.h

Lines changed: 118 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,10 @@
3030
#include <ios>
3131
#include <sstream>
3232
#include <string>
33+
#include <utility>
3334
#include <vector>
3435

35-
namespace o2::analysis::femto
36+
namespace o2::analysis::femto::baseselection
3637
{
3738

3839
/// \class BaseSelection
@@ -54,6 +55,11 @@ class BaseSelection
5455
/// \brief Destructor
5556
virtual ~BaseSelection() = default;
5657

58+
void init(bool passThrough)
59+
{
60+
mPassThrough = passThrough;
61+
}
62+
5763
/// \brief Add a static-value based selection for a specific observable.
5864
/// \param observableIndex Index of the observable.
5965
/// \param selectionName Name of the selection.
@@ -75,9 +81,14 @@ class BaseSelection
7581
LOG(fatal) << "Observable is not valid. Observable (index) has to be smaller than " << NumObservables;
7682
}
7783
// init selection container for selection at given index
78-
mSelectionContainers.at(observableIndex) = SelectionContainer<T, BitmaskType>(selectionName, selectionValues, limitType, skipMostPermissiveBit, isMinimalCut, isOptionalCut);
79-
80-
init(observableIndex);
84+
mSelectionContainers.at(observableIndex) = selectioncontainer::SelectionContainer<T, BitmaskType>(selectionName,
85+
selectionValues,
86+
limitType,
87+
mPassThrough ? false : skipMostPermissiveBit,
88+
mPassThrough ? false : isMinimalCut,
89+
mPassThrough ? false : isOptionalCut);
90+
91+
addSelection(observableIndex);
8192
}
8293

8394
/// \brief Add a function-based selection for a specific observable.
@@ -103,9 +114,15 @@ class BaseSelection
103114
if (static_cast<std::size_t>(observableIndex) >= NumObservables) {
104115
LOG(fatal) << "Observable is not valid. Observable (index) has to be smaller than " << NumObservables;
105116
}
106-
mSelectionContainers.at(observableIndex) = SelectionContainer<T, BitmaskType>(selectionName, lowerLimit, upperLimit, functions, limitType, skipMostPermissiveBit, isMinimalCut, isOptionalCut);
107-
108-
init(observableIndex);
117+
mSelectionContainers.at(observableIndex) = selectioncontainer::SelectionContainer<T, BitmaskType>(selectionName,
118+
lowerLimit,
119+
upperLimit,
120+
functions,
121+
limitType,
122+
mPassThrough ? false : skipMostPermissiveBit,
123+
mPassThrough ? false : isMinimalCut,
124+
mPassThrough ? false : isOptionalCut);
125+
addSelection(observableIndex);
109126
}
110127

111128
/// \brief Add a static-value based selection for a specific observable.
@@ -128,9 +145,13 @@ class BaseSelection
128145
LOG(fatal) << "Observable is not valid. Observable (index) has to be smaller than " << NumObservables;
129146
}
130147
// init selection container for selection at given index
131-
mSelectionContainers.at(observableIndex) = SelectionContainer<T, BitmaskType>(selectionName, selectionRanges, skipMostPermissiveBit, isMinimalCut, isOptionalCut);
148+
mSelectionContainers.at(observableIndex) = selectioncontainer::SelectionContainer<T, BitmaskType>(selectionName,
149+
selectionRanges,
150+
mPassThrough ? false : skipMostPermissiveBit,
151+
mPassThrough ? false : isMinimalCut,
152+
mPassThrough ? false : isOptionalCut);
132153

133-
init(observableIndex);
154+
addSelection(observableIndex);
134155
}
135156

136157
/// \brief Add a boolean-based selection for a specific observable.
@@ -144,23 +165,28 @@ class BaseSelection
144165
std::string const& selectionName,
145166
int mode)
146167
{
168+
if (mPassThrough) {
169+
mSelectionContainers.at(observableIndex) = selectioncontainer::SelectionContainer<T, BitmaskType>(selectionName, std::vector<T>{1}, limits::LimitType::kEqual, false, false, false);
170+
return;
171+
}
172+
147173
switch (mode) {
148174
case -1: // cut is optional and we store a bit for it
149-
mSelectionContainers.at(observableIndex) = SelectionContainer<T, BitmaskType>(selectionName, std::vector<T>{1}, limits::LimitType::kEqual, false, false, true);
175+
mSelectionContainers.at(observableIndex) = selectioncontainer::SelectionContainer<T, BitmaskType>(selectionName, std::vector<T>{1}, limits::LimitType::kEqual, false, false, true);
150176
break;
151177
case 0: // cut is disabled; initialize with empty vector so evaluation bails out early
152-
mSelectionContainers.at(observableIndex) = SelectionContainer<T, BitmaskType>(selectionName, std::vector<T>{}, limits::LimitType::kEqual, false, false, false);
178+
mSelectionContainers.at(observableIndex) = selectioncontainer::SelectionContainer<T, BitmaskType>(selectionName, std::vector<T>{}, limits::LimitType::kEqual, false, false, false);
153179
break;
154180
case 1: // mandatory cut; only one threshold so the most permissive bit is skipped and no extra bit is stored
155-
mSelectionContainers.at(observableIndex) = SelectionContainer<T, BitmaskType>(selectionName, std::vector<T>{1}, limits::LimitType::kEqual, true, true, false);
181+
mSelectionContainers.at(observableIndex) = selectioncontainer::SelectionContainer<T, BitmaskType>(selectionName, std::vector<T>{1}, limits::LimitType::kEqual, true, true, false);
156182
break;
157183
case 2: // pass through mode; cut is neither minimal nor optional and we store all bits
158-
mSelectionContainers.at(observableIndex) = SelectionContainer<T, BitmaskType>(selectionName, std::vector<T>{1}, limits::LimitType::kEqual, false, false, false);
184+
mSelectionContainers.at(observableIndex) = selectioncontainer::SelectionContainer<T, BitmaskType>(selectionName, std::vector<T>{1}, limits::LimitType::kEqual, false, false, false);
159185
break;
160186
default:
161187
LOG(fatal) << "Invalid switch for boolean selection";
162188
}
163-
init(observableIndex);
189+
addSelection(observableIndex);
164190
}
165191

166192
/// \brief Update the limits of a function-based selection for a specific observable.
@@ -255,10 +281,15 @@ class BaseSelection
255281
/// \param comments Vector of comment strings, one per selection threshold.
256282
void addComments(int observableIndex, std::vector<std::string> const& comments) { mSelectionContainers.at(observableIndex).addComments(comments); }
257283

284+
[[nodiscard]] bool isPassThrough() const { return mPassThrough; }
285+
258286
/// \brief Check whether all required and optional cuts are passed.
259287
/// \return True if all minimal cuts pass and, if optional cuts are present, at least one of them passes.
260288
[[nodiscard]] bool passesAllRequiredSelections() const
261289
{
290+
if (mPassThrough) {
291+
return true;
292+
}
262293
if (mHasMinimalSelection && !mHasOptionalSelection) {
263294
return mPassesMinimalSelections;
264295
}
@@ -363,7 +394,6 @@ class BaseSelection
363394
LOG(info) << " Selections:";
364395

365396
for (std::size_t j = 0; j < container.getNSelections(); ++j) {
366-
367397
std::stringstream line;
368398
std::string sel = container.getValueAsString(j);
369399
std::string comment = container.getComment(j);
@@ -392,9 +422,12 @@ class BaseSelection
392422
/// \tparam HistName Name of the histogram to create in the registry.
393423
/// \param registry Pointer to the histogram registry.
394424
template <auto& HistName>
395-
void setupContainers(o2::framework::HistogramRegistry* registry)
425+
void setupSelectionHistogram(o2::framework::HistogramRegistry* registry)
396426
{
397-
mHistRegistry = registry;
427+
if (!mHistRegistry) {
428+
mHistRegistry = registry;
429+
}
430+
398431
// create histogram with one bin per selection, plus two summary bins (all analyzed, all passed)
399432
int nBins = mNSelection + 2;
400433
mHistRegistry->add(HistName, "; Selection Bits; Entries", o2::framework::HistType::kTH1F, {{nBins, -0.5, nBins - 0.5}});
@@ -418,8 +451,64 @@ class BaseSelection
418451
mHistRegistry->get<TH1>(HIST(HistName))->GetXaxis()->SetBinLabel(mNSelection + 2, "All passed");
419452
}
420453

454+
/// \brief Setup a histogram tracking how many candidates pass each individual filter bound.
455+
/// \tparam FilterHistName Name of the histogram (must be unique in registry).
456+
/// \param registry Pointer to the histogram registry.
457+
/// \param filters Ordered list of (name, value) pairs, one per filter bound. The position in
458+
/// this vector must match the caller's filter enum values (e.g. filters[kPtMin]
459+
/// describes the kPtMin bound), since that's what fillFilter() indexes into.
460+
template <auto& FilterHistName>
461+
void setupFilterHistogram(o2::framework::HistogramRegistry* registry,
462+
std::vector<std::pair<std::string, T>> const& filters)
463+
{
464+
if (!mHistRegistry) {
465+
mHistRegistry = registry;
466+
}
467+
468+
mNFilters = filters.size();
469+
// two extra bins: "All analyzed" and "All passed", mirroring setupContainers()
470+
registry->add(FilterHistName, "; Filter; Entries", o2::framework::HistType::kTH1F,
471+
{{static_cast<int>(mNFilters) + 2, -0.5, static_cast<double>(mNFilters) + 1.5}});
472+
473+
auto* axis = registry->get<TH1>(HIST(FilterHistName))->GetXaxis();
474+
for (std::size_t i = 0; i < mNFilters; ++i) {
475+
std::ostringstream label;
476+
label << "FilterName" << selectioncontainer::ValueDelimiter << filters[i].first << selectioncontainer::SectionDelimiter
477+
<< "FilterValue" << selectioncontainer::ValueDelimiter << filters[i].second;
478+
axis->SetBinLabel(static_cast<int>(i) + 1, label.str().c_str());
479+
}
480+
axis->SetBinLabel(static_cast<int>(mNFilters) + 1, "All analyzed");
481+
axis->SetBinLabel(static_cast<int>(mNFilters) + 2, "All passed");
482+
}
483+
484+
/// \brief Fill one bin of the filter histogram if the corresponding filter bound was passed.
485+
/// \tparam FilterHistName Name of the histogram (same one passed to setupFilterHistogram).
486+
/// \param filterIndex Index of the filter bound (an enum value from the caller's filter enum).
487+
/// \param passed Whether the candidate passed this specific filter bound.
488+
template <auto& FilterHistName>
489+
void fillFilter(int filterIndex, bool passed) const
490+
{
491+
if (passed) {
492+
mHistRegistry->fill(HIST(FilterHistName), filterIndex);
493+
}
494+
}
495+
496+
/// \brief Fill the two summary bins of the filter histogram: called once per candidate,
497+
/// after all individual filter bounds have been checked.
498+
/// \tparam FilterHistName Name of the histogram (same one passed to setupFilterHistogram).
499+
/// \param passedAll Whether the candidate passed every configured filter bound.
500+
template <auto& FilterHistName>
501+
void fillFilterSummary(bool passedAll) const
502+
503+
{
504+
mHistRegistry->fill(HIST(FilterHistName), static_cast<double>(mNFilters));
505+
if (passedAll || mPassThrough) {
506+
mHistRegistry->fill(HIST(FilterHistName), static_cast<double>(mNFilters) + 1);
507+
}
508+
}
509+
421510
protected:
422-
void init(int observableIndex)
511+
void addSelection(int observableIndex)
423512
{
424513
// check if any selections are configured
425514
if (mSelectionContainers.at(observableIndex).isEmpty()) {
@@ -445,15 +534,17 @@ class BaseSelection
445534
}
446535

447536
o2::framework::HistogramRegistry* mHistRegistry = nullptr;
448-
std::array<SelectionContainer<T, BitmaskType>, NumObservables> mSelectionContainers = {}; ///< Array of selection containers, one per observable
449-
std::bitset<sizeof(BitmaskType) * CHAR_BIT> mFinalBitmask = {}; ///< Assembled bitmask combining all observable selections
450-
std::size_t mNSelectionBits = 0; ///< Number of bits occupied in the bitmask (excludes skipped most-permissive bits)
451-
std::size_t mNSelection = 0; ///< Total number of configured selection thresholds across all observables
452-
bool mHasMinimalSelection = false; ///< True if at least one observable has a mandatory (minimal) cut configured
453-
bool mPassesMinimalSelections = true; ///< True if all mandatory (minimal) cuts have been passed so far
454-
bool mHasOptionalSelection = false; ///< True if at least one observable has an optional cut configured
455-
bool mPassesOptionalSelections = false; ///< True if at least one optional cut has been passed
537+
std::array<selectioncontainer::SelectionContainer<T, BitmaskType>, NumObservables> mSelectionContainers = {}; ///< Array of selection containers, one per observable
538+
std::bitset<sizeof(BitmaskType) * CHAR_BIT> mFinalBitmask = {}; ///< Assembled bitmask combining all observable selections
539+
std::size_t mNSelectionBits = 0; ///< Number of bits occupied in the bitmask (excludes skipped most-permissive bits)
540+
std::size_t mNSelection = 0; ///< Total number of configured selection thresholds across all observables
541+
std::size_t mNFilters = 0; ///< Total number of configured (pre)filter
542+
bool mHasMinimalSelection = false; ///< True if at least one observable has a mandatory (minimal) cut configured
543+
bool mPassesMinimalSelections = true; ///< True if all mandatory (minimal) cuts have been passed so far
544+
bool mHasOptionalSelection = false; ///< True if at least one observable has an optional cut configured
545+
bool mPassesOptionalSelections = false; ///< True if at least one optional cut has been passed
546+
bool mPassThrough = false;
456547
};
457-
} // namespace o2::analysis::femto
548+
} // namespace o2::analysis::femto::baseselection
458549

459550
#endif // PWGCF_FEMTO_CORE_BASESELECTION_H_

0 commit comments

Comments
 (0)