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