Skip to content

Commit da2c876

Browse files
committed
Feat: add histograms for filters
1 parent c4d1e20 commit da2c876

8 files changed

Lines changed: 896 additions & 417 deletions

File tree

PWGCF/Femto/Core/baseSelection.h

Lines changed: 119 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,11 @@
3030
#include <ios>
3131
#include <sstream>
3232
#include <string>
33+
#include <string_view>
34+
#include <utility>
3335
#include <vector>
3436

35-
namespace o2::analysis::femto
37+
namespace o2::analysis::femto::baseselection
3638
{
3739

3840
/// \class BaseSelection
@@ -54,6 +56,11 @@ class BaseSelection
5456
/// \brief Destructor
5557
virtual ~BaseSelection() = default;
5658

59+
void init(bool passThrough)
60+
{
61+
mPassThrough = passThrough;
62+
}
63+
5764
/// \brief Add a static-value based selection for a specific observable.
5865
/// \param observableIndex Index of the observable.
5966
/// \param selectionName Name of the selection.
@@ -75,9 +82,14 @@ class BaseSelection
7582
LOG(fatal) << "Observable is not valid. Observable (index) has to be smaller than " << NumObservables;
7683
}
7784
// 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);
85+
mSelectionContainers.at(observableIndex) = selectioncontainer::SelectionContainer<T, BitmaskType>(selectionName,
86+
selectionValues,
87+
limitType,
88+
mPassThrough ? false : skipMostPermissiveBit,
89+
mPassThrough ? false : isMinimalCut,
90+
mPassThrough ? false : isOptionalCut);
91+
92+
addSelection(observableIndex);
8193
}
8294

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

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

133-
init(observableIndex);
155+
addSelection(observableIndex);
134156
}
135157

136158
/// \brief Add a boolean-based selection for a specific observable.
@@ -144,23 +166,28 @@ class BaseSelection
144166
std::string const& selectionName,
145167
int mode)
146168
{
169+
if (mPassThrough) {
170+
mSelectionContainers.at(observableIndex) = selectioncontainer::SelectionContainer<T, BitmaskType>(selectionName, std::vector<T>{1}, limits::LimitType::kEqual, false, false, false);
171+
return;
172+
}
173+
147174
switch (mode) {
148175
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);
176+
mSelectionContainers.at(observableIndex) = selectioncontainer::SelectionContainer<T, BitmaskType>(selectionName, std::vector<T>{1}, limits::LimitType::kEqual, false, false, true);
150177
break;
151178
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);
179+
mSelectionContainers.at(observableIndex) = selectioncontainer::SelectionContainer<T, BitmaskType>(selectionName, std::vector<T>{}, limits::LimitType::kEqual, false, false, false);
153180
break;
154181
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);
182+
mSelectionContainers.at(observableIndex) = selectioncontainer::SelectionContainer<T, BitmaskType>(selectionName, std::vector<T>{1}, limits::LimitType::kEqual, true, true, false);
156183
break;
157184
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);
185+
mSelectionContainers.at(observableIndex) = selectioncontainer::SelectionContainer<T, BitmaskType>(selectionName, std::vector<T>{1}, limits::LimitType::kEqual, false, false, false);
159186
break;
160187
default:
161188
LOG(fatal) << "Invalid switch for boolean selection";
162189
}
163-
init(observableIndex);
190+
addSelection(observableIndex);
164191
}
165192

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

285+
[[nodiscard]] bool isPassThrough() const { return mPassThrough; }
286+
258287
/// \brief Check whether all required and optional cuts are passed.
259288
/// \return True if all minimal cuts pass and, if optional cuts are present, at least one of them passes.
260289
[[nodiscard]] bool passesAllRequiredSelections() const
261290
{
291+
if (mPassThrough) {
292+
return true;
293+
}
262294
if (mHasMinimalSelection && !mHasOptionalSelection) {
263295
return mPassesMinimalSelections;
264296
}
@@ -363,7 +395,6 @@ class BaseSelection
363395
LOG(info) << " Selections:";
364396

365397
for (std::size_t j = 0; j < container.getNSelections(); ++j) {
366-
367398
std::stringstream line;
368399
std::string sel = container.getValueAsString(j);
369400
std::string comment = container.getComment(j);
@@ -392,9 +423,12 @@ class BaseSelection
392423
/// \tparam HistName Name of the histogram to create in the registry.
393424
/// \param registry Pointer to the histogram registry.
394425
template <auto& HistName>
395-
void setupContainers(o2::framework::HistogramRegistry* registry)
426+
void setupSelectionHistogram(o2::framework::HistogramRegistry* registry)
396427
{
397-
mHistRegistry = registry;
428+
if (!mHistRegistry) {
429+
mHistRegistry = registry;
430+
}
431+
398432
// create histogram with one bin per selection, plus two summary bins (all analyzed, all passed)
399433
int nBins = mNSelection + 2;
400434
mHistRegistry->add(HistName, "; Selection Bits; Entries", o2::framework::HistType::kTH1F, {{nBins, -0.5, nBins - 0.5}});
@@ -418,8 +452,64 @@ class BaseSelection
418452
mHistRegistry->get<TH1>(HIST(HistName))->GetXaxis()->SetBinLabel(mNSelection + 2, "All passed");
419453
}
420454

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

447537
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
538+
std::array<selectioncontainer::SelectionContainer<T, BitmaskType>, NumObservables> mSelectionContainers = {}; ///< Array of selection containers, one per observable
539+
std::bitset<sizeof(BitmaskType) * CHAR_BIT> mFinalBitmask = {}; ///< Assembled bitmask combining all observable selections
540+
std::size_t mNSelectionBits = 0; ///< Number of bits occupied in the bitmask (excludes skipped most-permissive bits)
541+
std::size_t mNSelection = 0; ///< Total number of configured selection thresholds across all observables
542+
std::size_t mNFilters = 0; ///< Total number of configured (pre)filter
543+
bool mHasMinimalSelection = false; ///< True if at least one observable has a mandatory (minimal) cut configured
544+
bool mPassesMinimalSelections = true; ///< True if all mandatory (minimal) cuts have been passed so far
545+
bool mHasOptionalSelection = false; ///< True if at least one observable has an optional cut configured
546+
bool mPassesOptionalSelections = false; ///< True if at least one optional cut has been passed
547+
bool mPassThrough = false;
456548
};
457-
} // namespace o2::analysis::femto
549+
} // namespace o2::analysis::femto::baseselection
458550

459551
#endif // PWGCF_FEMTO_CORE_BASESELECTION_H_

0 commit comments

Comments
 (0)