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