From 0212f09d36a965f09c31e1efc3f7876f1629396e Mon Sep 17 00:00:00 2001
From: Volker Friese <v.friese@gsi.de>
Date: Fri, 14 Jul 2023 16:51:05 +0200
Subject: [PATCH] Generalize DigiEventSelector. Refs #2918

---
 algo/evbuild/EventbuildChain.cxx      |  12 +--
 algo/evbuild/EventbuildChain.h        |  14 +--
 algo/evselector/DigiEventSelector.cxx | 126 +++++++++++++++-----------
 algo/evselector/DigiEventSelector.h   |  63 +++++++++----
 algo/global/Reco.cxx                  |   4 +-
 algo/test/_GTestDigiEventSelector.cxx | 117 ++++++++++++++++++++----
 core/data/base/CbmDigiData.h          |  16 ++++
 core/data/psd/CbmPsdDigiData.h        |   3 +
 reco/tasks/CbmReco.cxx                |   8 +-
 reco/tasks/CbmTaskBuildEvents.h       |  23 ++---
 10 files changed, 264 insertions(+), 122 deletions(-)

diff --git a/algo/evbuild/EventbuildChain.cxx b/algo/evbuild/EventbuildChain.cxx
index 0b83b9d5a4..ac234de30f 100644
--- a/algo/evbuild/EventbuildChain.cxx
+++ b/algo/evbuild/EventbuildChain.cxx
@@ -28,11 +28,11 @@ void EventbuildChain::Init(const MainConfig& config)
   }
 
   // --- Configure the event selector
-  size_t minStatSts   = fConfig.fSelectMinStationsSts;
-  size_t minStatTof   = fConfig.fSelectMinStationsTof;
-  size_t minDigisBmon = fConfig.fSelectMinDigisBmon;
-  auto selectPar      = cbm::algo::DigiEventSelectorParams {minStatSts, minStatTof, minDigisBmon};
-  fSelector.SetParams(selectPar);
+  evbuild::DigiEventSelectorParams selectPar;
+  selectPar.fMinNumDigis[ECbmModuleId::kT0]   = fConfig.fSelectMinDigisBmon;
+  selectPar.fMinNumLayers[ECbmModuleId::kSts] = fConfig.fSelectMinStationsSts;
+  selectPar.fMinNumLayers[ECbmModuleId::kTof] = fConfig.fSelectMinStationsTof;
+  fSelector                                   = std::make_unique<evbuild::DigiEventSelector>(selectPar);
 
   // --- Configure the event QA
   const double border    = 10.;  // TODO: Histogram border hard-coded
@@ -65,7 +65,7 @@ std::vector<CbmDigiEvent> EventbuildChain::Run(const CbmDigiTimeslice& timeslice
   std::vector<CbmDigiEvent> events = fEventBuilder(timeslice, triggers).first;
 
   // --- Apply event selector
-  auto notSelected = [&](CbmDigiEvent& ev) { return !fSelector(ev); };
+  auto notSelected = [&](CbmDigiEvent& ev) { return !((*fSelector)(ev)); };
   auto removeIt    = std::remove_if(events.begin(), events.end(), notSelected);
   events.erase(removeIt, events.end());
 
diff --git a/algo/evbuild/EventbuildChain.h b/algo/evbuild/EventbuildChain.h
index b45094c2b4..314bb3ff9d 100644
--- a/algo/evbuild/EventbuildChain.h
+++ b/algo/evbuild/EventbuildChain.h
@@ -39,13 +39,13 @@ namespace cbm::algo
     std::vector<CbmDigiEvent> Run(const CbmDigiTimeslice&);
 
 
-  private:                            // members
-    MainConfig fConfig;               ///< Configuration / parameters
-    TimeClusterTrigger fTrigger;      ///< Trigger algorithm
-    EventBuilder fEventBuilder;       ///< Event builder algorithm
-    DigiEventSelector fSelector;      ///< Event selector algorithm
-    qa::DigiEventQa fQa;              ///< Event QA algorithm
-    qa::DigiEventQaConfig fQaConfig;  ///< Configuration for event QA
+  private:                                                            // members
+    MainConfig fConfig;                                               ///< Configuration / parameters
+    TimeClusterTrigger fTrigger;                                      ///< Trigger algorithm
+    EventBuilder fEventBuilder;                                       ///< Event builder algorithm
+    std::unique_ptr<evbuild::DigiEventSelector> fSelector = nullptr;  ///< Event selector algorithm
+    qa::DigiEventQa fQa;                                              ///< Event QA algorithm
+    qa::DigiEventQaConfig fQaConfig;                                  ///< Configuration for event QA
 
 
   private:  // methods
diff --git a/algo/evselector/DigiEventSelector.cxx b/algo/evselector/DigiEventSelector.cxx
index a259ca67af..59743bec71 100644
--- a/algo/evselector/DigiEventSelector.cxx
+++ b/algo/evselector/DigiEventSelector.cxx
@@ -9,100 +9,120 @@
 #include "tof/TofConfig.h"
 
 #include <iterator>
-#include <map>
 #include <unordered_map>
 #include <unordered_set>
 
 #include "AlgoFairloggerCompat.h"
 
 
-namespace cbm::algo
+namespace cbm::algo::evbuild
 {
 
+  // -----   Test one digi event   --------------------------------------------
   bool DigiEventSelector::operator()(const CbmDigiEvent& event) const
   {
-    //TO DO: Investigate possible algorithms that use bitset instead of std::unordered_map.
-    //This requires consequtive addresses for modules and stations.
 
-    // --- Digi number in BMON
-    if (!(event.fData.fT0.fDigis.size() >= fParams.fMinNumDigisBmon)) return false;
-
-    // --- Number of STS stations
-    if (0 < fParams.fStsMinStations) {
-      if (!CheckSts(event.fData.fSts.fDigis)) return false;
+    // --- Test number of digis per detector system
+    for (auto& entry : fParams.fMinNumDigis) {
+      if (!(event.fData.Size(entry.first) >= entry.second)) {
+        return false;
+        break;
+      }
     }
 
-    // --- Number of TOF layers
-    if (0 < fParams.fTofMinLayers) {
-      if (!CheckTof(event.fData.fTof.fDigis)) return false;
+    // --- Test number of layers with digis per detector system
+    for (auto& entry : fParams.fMinNumLayers) {
+      if (entry.second == 0) continue;
+      switch (entry.first) {
+        case ECbmModuleId::kSts:
+          if (!CheckStsStations(event.fData.fSts.fDigis, entry.second)) return false;
+          break;
+        case ECbmModuleId::kTof:
+          if (!CheckTofLayers(event.fData.fTof.fDigis, entry.second)) return false;
+          break;
+        default:
+          throw std::runtime_error("Number of layers for " + ToString(entry.first) + " is not implemented");
+          break;
+      }
     }
 
     return true;
   }
+  // --------------------------------------------------------------------------
 
-  bool DigiEventSelector::CheckSts(const std::vector<CbmStsDigi>& digis) const
+
+  // -----   Check number of STS stations   -----------------------------------
+  bool DigiEventSelector::CheckStsStations(const std::vector<CbmStsDigi>& digis, size_t minNum) const
   {
+
+    // A station is considered activated if it has at least one activated module. A module is considered
+    // activated if it has at least one digi on each of the sensor sides.
     const uint16_t chanPerSide = 1024;
-    /// check for requested number of different STS stations
-    std::unordered_set<uint32_t> setStations;
-    std::unordered_map<int32_t, bool> mModules;
+    std::unordered_set<uint32_t> stations;      // active stations
+    std::unordered_map<int32_t, bool> modules;  // active modules, value false means front side hit, true is back side
 
     for (auto& digi : digis) {
       const int32_t addr = digi.GetAddress();
-      auto itModule      = mModules.find(addr);
-      if (itModule == mModules.end()) {
-        mModules[addr] = digi.GetChannel() / chanPerSide;  // = 0,1 for sides
-      }
+      auto module        = modules.find(addr);
+      if (module == modules.end()) modules[addr] = digi.GetChannel() / chanPerSide;  // = 0,1 for sides
       else {
-        if (digi.GetChannel() / chanPerSide != itModule->second) {
-          /// Found other side => non-zero cluster chance, insert into stations set
+        if (digi.GetChannel() / chanPerSide != module->second) {  // other sensor side found, chance for cluster
           const uint32_t stationAddr = CbmStsAddress::GetElementId(addr, EStsElementLevel::kStsUnit);
-          auto itStation             = setStations.find(stationAddr);
-          if (itStation == setStations.end()) {
-            setStations.insert(stationAddr);
-            if (setStations.size() == fParams.fStsMinStations) break;
+          if (stations.count(stationAddr) == 0) {
+            stations.insert(stationAddr);
+            if (stations.size() == minNum) break;
           }
         }
       }
     }
-    if (setStations.size() < fParams.fStsMinStations) { return false; }
-    else {
+
+    if (stations.size() < minNum) return false;
+    else
       return true;
-    }
   }
+  // TODO: (VF 14/07/23) I do not like this implementation very much. It is mCBM-specific in the sense
+  // that what actually is checked are the units, not the stations. Only for mCBM geometries, stations
+  // and units are identical. Moreover, I feel that the association of digis (addresses) to stations
+  // should be implemented in the STS geometry handler, like for TOF in the method below.
+  // And the number of channels per sensor side is hard-coded here. Not nice.
+  // --------------------------------------------------------------------------
+
 
-  bool DigiEventSelector::CheckTof(const std::vector<CbmTofDigi>& digis) const
+  // -----   Check number of TOF layers   -------------------------------------
+  bool DigiEventSelector::CheckTofLayers(const std::vector<CbmTofDigi>& digis, size_t minNum) const
   {
-    /// check for requested number of different RPCs
-    std::unordered_set<int32_t> setRpcs;
-    std::unordered_set<int32_t> setTofStation;
-    std::unordered_map<int32_t, bool> mStrips;
+    // A station is considered activated if it has at least one activated RPC. An RPC is considered
+    // activated if it has at least one activated strip. A strip is considered activated
+    // if it has digis on each side.
+    std::unordered_set<int32_t> rpcs;          // active RPCs (address)
+    std::unordered_set<int32_t> stations;      // active TOF stations
+    std::unordered_map<int32_t, bool> strips;  // active strips
     for (auto& digi : digis) {
-      const int32_t addr      = digi.GetAddress();
-      const int32_t stripAddr = CbmTofAddress::GetStripFullId(addr);
+      const int32_t digiAddr  = digi.GetAddress();
+      const int32_t stripAddr = CbmTofAddress::GetStripFullId(digiAddr);
 
-      auto itStrip = mStrips.find(stripAddr);
-      if (itStrip == mStrips.end()) { mStrips[stripAddr] = digi.GetSide(); }
+      auto strip = strips.find(stripAddr);
+      if (strip == strips.end()) strips[stripAddr] = digi.GetSide();
       else {
-        if (digi.GetSide() != itStrip->second) {
-          /// Found other end => full strip, insert into counter set
-          const int32_t rpcAddr = CbmTofAddress::GetRpcFullId(addr);
-          auto itRpc            = setRpcs.find(rpcAddr);
-          if (itRpc == setRpcs.end()) {
-            const int32_t smId         = CbmTofAddress::GetSmId(addr);
-            const int32_t smType       = CbmTofAddress::GetSmType(addr);
-            const int32_t rpcId        = CbmTofAddress::GetRpcId(addr);
+        if (digi.GetSide() != strip->second) {  // Found other end => full strip, insert into counter set
+          const int32_t rpcAddr = CbmTofAddress::GetRpcFullId(digiAddr);
+          if (rpcs.count(rpcAddr) == 0) {                                     // new RPC activated
+            const int32_t smId         = CbmTofAddress::GetSmId(digiAddr);    // Super-module ID
+            const int32_t smType       = CbmTofAddress::GetSmType(digiAddr);  // Super-module type
+            const int32_t rpcId        = CbmTofAddress::GetRpcId(digiAddr);   // RPC ID
             const int32_t TofStationId = TofConfig::GetTofTrackingStation(smType, smId, rpcId);
-            setTofStation.insert(TofStationId);
-            if (setTofStation.size() == fParams.fTofMinLayers) break;
+            stations.insert(TofStationId);
+            if (stations.size() == minNum) break;
           }
         }
       }
     }
-    if (setTofStation.size() < fParams.fTofMinLayers) { return false; }
-    else {
+
+    if (stations.size() < minNum) return false;
+    else
       return true;
-    }
   }
+  // --------------------------------------------------------------------------
+
 
-}  // namespace cbm::algo
+}  // namespace cbm::algo::evbuild
diff --git a/algo/evselector/DigiEventSelector.h b/algo/evselector/DigiEventSelector.h
index 2a1f4e10b1..a19e98c681 100644
--- a/algo/evselector/DigiEventSelector.h
+++ b/algo/evselector/DigiEventSelector.h
@@ -8,40 +8,65 @@
 #include "CbmDigiEvent.h"
 
 #include <cstdint>
+#include <map>
 
-namespace cbm::algo
+namespace cbm::algo::evbuild
 {
 
+  /** @struct DigiEventSelectorParams
+   ** @author Dominik Smith <d.smith@ gsi.de>
+   ** @author Shreya Roy <s.roy@gsi.de>
+   ** @author Volker Friese <v.friese@gsi.de>
+   ** @since 26.01.2023
+   */
   struct DigiEventSelectorParams {
-    size_t fStsMinStations  = 0;
-    size_t fTofMinLayers    = 0;
-    size_t fMinNumDigisBmon = 0;
+    std::map<ECbmModuleId, size_t> fMinNumDigis  = {};
+    std::map<ECbmModuleId, size_t> fMinNumLayers = {};
   };
 
+
   /** @class DigiEventSelector
-  ** @author Dominik Smith <d.smith@gsi.de>
- ** @author Shreya Roy <s.roy@gsi.de>
-** @author Volker Friese <v.friese@gsi.de>
- 
-** @since 2022
- ** @brief
- **
- **/
+   ** @author Dominik Smith <d.smith@ gsi.de>
+   ** @author Shreya Roy <s.roy@gsi.de>
+   ** @author Volker Friese <v.friese@gsi.de>
+   ** @since 26.01.2023
+   **
+   ** @brief Algorithm to select CbmDigiEvents based on the number of digis and the number of
+   ** activated layers in each detector system.
+   **/
   class DigiEventSelector {
 
   public:
-    DigiEventSelector() {};
+    /** @brief Constructor with parameters **/
     DigiEventSelector(DigiEventSelectorParams params) { fParams = params; }
+
+    /** @brief Test one event for the selection criteria
+     ** @param event DigiEvent
+     ** @return true if event satisfies the criteria; else false
+     **/
     bool operator()(const CbmDigiEvent& event) const;
 
-    void SetParams(DigiEventSelectorParams params) { fParams = params; }
 
-  private:
-    DigiEventSelectorParams fParams;
+  private:  // methods
+    /** @brief Test for the number of STS stations
+     ** @param digis Vector of STS digis
+     ** @param minNum Requested minimum of active STS stations
+     ** @return True if the number of active STS layers is above the threshold
+     **/
+    bool CheckStsStations(const std::vector<CbmStsDigi>& digis, size_t minNum) const;
+
+    /** @brief Test for the number of TOF layers
+     ** @param digis Vector of TOF digis
+     ** @param minNum Requested minimum of active TOF layers
+     ** @return True if the number of active TOF layers is above the threshold
+     **/
+    bool CheckTofLayers(const std::vector<CbmTofDigi>& digis, size_t minNum) const;
 
-    bool CheckSts(const std::vector<CbmStsDigi>& digis) const;
-    bool CheckTof(const std::vector<CbmTofDigi>& digis) const;
+
+  private:                            // members
+    DigiEventSelectorParams fParams;  ///< Selection parameters
   };
-}  // namespace cbm::algo
+
+}  // namespace cbm::algo::evbuild
 
 #endif /* CBM_ALGO_DIGIEVENTSELECTOR_H */
diff --git a/algo/global/Reco.cxx b/algo/global/Reco.cxx
index 9ded3dc0be..3a5f1a6e31 100644
--- a/algo/global/Reco.cxx
+++ b/algo/global/Reco.cxx
@@ -225,8 +225,8 @@ void Reco::QueueStsRecoMetrics(const sts::HitfinderMonitor& monitor)
   GetMonitor().QueueMetric("cbmreco", {{"hostname", fles::system::current_hostname()}, {"child", Opts().ChildId()}},
                            {
                              {"stsRecoTimeTotal", monitor.fTimeTotal},
-                             {"stsRecoNumClusters", monitor.fNumClusterTotal},
-                             {"stsRecoNumHits", monitor.fNumHitsTotal},
+                             {"stsRecoNumClusters", (unsigned long) monitor.fNumClusterTotal},
+                             {"stsRecoNumHits", (unsigned long) monitor.fNumHitsTotal},
                              {"stsRecoNumClusterBucketOverflow", monitor.fNumClusterBucketOverflow},
                              {"stsRecoNumHitBucketOverflow", monitor.fNumHitBucketOverflow},
                            });
diff --git a/algo/test/_GTestDigiEventSelector.cxx b/algo/test/_GTestDigiEventSelector.cxx
index 77952b8080..375b5f9180 100644
--- a/algo/test/_GTestDigiEventSelector.cxx
+++ b/algo/test/_GTestDigiEventSelector.cxx
@@ -14,7 +14,91 @@ TEST(_GTestDigiEventSelector, CheckDigiEventSelectorAlgorithmSimple)
 {
   SCOPED_TRACE("CheckDigiEventSelectorAlgorithSimple");
 
-  //Test STS
+
+  // --- Test number of digis per system
+  size_t nBmon  = 1;
+  size_t nSts   = 2;
+  size_t nMuch  = 3;
+  size_t nRich  = 4;
+  size_t nTrd   = 5;
+  size_t nTrd2d = 6;
+  size_t nTof   = 7;
+  size_t nPsd   = 8;
+  CbmDigiEvent event;
+  for (size_t i = 0; i < nBmon; i++)
+    event.fData.fT0.fDigis.push_back(CbmBmonDigi());
+  for (size_t i = 0; i < nSts; i++)
+    event.fData.fSts.fDigis.push_back(CbmStsDigi());
+  for (size_t i = 0; i < nMuch; i++)
+    event.fData.fMuch.fDigis.push_back(CbmMuchDigi());
+  for (size_t i = 0; i < nRich; i++)
+    event.fData.fRich.fDigis.push_back(CbmRichDigi());
+  for (size_t i = 0; i < nTrd; i++)
+    event.fData.fTrd.fDigis.push_back(CbmTrdDigi());
+  for (size_t i = 0; i < nTrd2d; i++)
+    event.fData.fTrd2d.fDigis.push_back(CbmTrdDigi());
+  for (size_t i = 0; i < nTof; i++)
+    event.fData.fTof.fDigis.push_back(CbmTofDigi());
+  for (size_t i = 0; i < nPsd; i++)
+    event.fData.fPsd.fDigis.push_back(CbmPsdDigi());
+
+  // --- Check with created numbers of digis
+  cbm::algo::evbuild::DigiEventSelectorParams filterParPass;
+  filterParPass.fMinNumDigis[ECbmModuleId::kT0]    = nBmon;
+  filterParPass.fMinNumDigis[ECbmModuleId::kSts]   = nSts;
+  filterParPass.fMinNumDigis[ECbmModuleId::kMuch]  = nMuch;
+  filterParPass.fMinNumDigis[ECbmModuleId::kRich]  = nRich;
+  filterParPass.fMinNumDigis[ECbmModuleId::kTrd]   = nTrd;
+  filterParPass.fMinNumDigis[ECbmModuleId::kTrd2d] = nTrd2d;
+  filterParPass.fMinNumDigis[ECbmModuleId::kTof]   = nTof;
+  filterParPass.fMinNumDigis[ECbmModuleId::kPsd]   = nPsd;
+  cbm::algo::evbuild::DigiEventSelector evfilter1a(filterParPass);
+  EXPECT_EQ(evfilter1a(event), true);
+
+  cbm::algo::evbuild::DigiEventSelectorParams filterParFail;
+
+  filterParFail                                 = filterParPass;
+  filterParFail.fMinNumDigis[ECbmModuleId::kT0] = nBmon + 1;
+  cbm::algo::evbuild::DigiEventSelector evfilter1b(filterParFail);
+  EXPECT_EQ(evfilter1b(event), false);
+
+  filterParFail                                  = filterParPass;
+  filterParFail.fMinNumDigis[ECbmModuleId::kSts] = nSts + 1;
+  cbm::algo::evbuild::DigiEventSelector evfilter1c(filterParFail);
+  EXPECT_EQ(evfilter1c(event), false);
+
+  filterParFail                                   = filterParPass;
+  filterParFail.fMinNumDigis[ECbmModuleId::kMuch] = nMuch + 1;
+  cbm::algo::evbuild::DigiEventSelector evfilter1d(filterParFail);
+  EXPECT_EQ(evfilter1d(event), false);
+
+  filterParFail                                   = filterParPass;
+  filterParFail.fMinNumDigis[ECbmModuleId::kRich] = nRich + 1;
+  cbm::algo::evbuild::DigiEventSelector evfilter1e(filterParFail);
+  EXPECT_EQ(evfilter1e(event), false);
+
+  filterParFail                                  = filterParPass;
+  filterParFail.fMinNumDigis[ECbmModuleId::kTrd] = nTrd + 1;
+  cbm::algo::evbuild::DigiEventSelector evfilter1f(filterParFail);
+  EXPECT_EQ(evfilter1f(event), false);
+
+  filterParFail                                    = filterParPass;
+  filterParFail.fMinNumDigis[ECbmModuleId::kTrd2d] = nTrd2d + 1;
+  cbm::algo::evbuild::DigiEventSelector evfilter1g(filterParFail);
+  EXPECT_EQ(evfilter1g(event), false);
+
+  filterParFail                                  = filterParPass;
+  filterParFail.fMinNumDigis[ECbmModuleId::kTof] = nTof + 1;
+  cbm::algo::evbuild::DigiEventSelector evfilter1h(filterParFail);
+  EXPECT_EQ(evfilter1h(event), false);
+
+  filterParFail                                  = filterParPass;
+  filterParFail.fMinNumDigis[ECbmModuleId::kPsd] = nPsd + 1;
+  cbm::algo::evbuild::DigiEventSelector evfilter1i(filterParFail);
+  EXPECT_EQ(evfilter1i(event), false);
+
+
+  // --- Test number of STS stations
   {
     const uint maxStsStations = 8;
     const uint maxStsModules  = 12;
@@ -42,21 +126,22 @@ TEST(_GTestDigiEventSelector, CheckDigiEventSelectorAlgorithmSimple)
       }
 
       //Prepare event filter
-      cbm::algo::DigiEventSelectorParams filterParams;
-      filterParams.fStsMinStations = numStations;
-      cbm::algo::DigiEventSelector evfilter(filterParams);
+      cbm::algo::evbuild::DigiEventSelectorParams filterParams;
+      filterParams.fMinNumLayers[ECbmModuleId::kSts] = numStations;
+      cbm::algo::evbuild::DigiEventSelector evfilter11(filterParams);
 
-      EXPECT_EQ(evfilter(eventIn), true);
+      EXPECT_EQ(evfilter11(eventIn), true);
 
       //Test if digi without partner is properly disregarded
-      filterParams.fStsMinStations = numStations + 1;
-      evfilter.SetParams(filterParams);
+      filterParams.fMinNumLayers[ECbmModuleId::kSts] = numStations + 1;
+      cbm::algo::evbuild::DigiEventSelector evfilter12(filterParams);
 
-      EXPECT_EQ(evfilter(eventIn), false);
+      EXPECT_EQ(evfilter12(eventIn), false);
     }
   }
 
-  //Test TOF
+
+  // --- Test number of TOF layers
   {
     //Prepare input
     CbmDigiEvent eventIn;
@@ -78,16 +163,16 @@ TEST(_GTestDigiEventSelector, CheckDigiEventSelectorAlgorithmSimple)
           const int32_t TofStationId = cbm::algo::TofConfig::GetTofTrackingStation(smType, sm, rpc);
           setTofStation.insert(TofStationId);
 
-          cbm::algo::DigiEventSelectorParams filterParams;
-          filterParams.fTofMinLayers = setTofStation.size();
-          cbm::algo::DigiEventSelector evfilter(filterParams);
+          cbm::algo::evbuild::DigiEventSelectorParams filterParams;
+          filterParams.fMinNumLayers[ECbmModuleId::kTof] = setTofStation.size();
+          cbm::algo::evbuild::DigiEventSelector evfilter21(filterParams);
 
-          EXPECT_EQ(evfilter(eventIn), true);
+          EXPECT_EQ(evfilter21(eventIn), true);
 
-          filterParams.fTofMinLayers = setTofStation.size() + 1;
-          evfilter.SetParams(filterParams);
+          filterParams.fMinNumLayers[ECbmModuleId::kTof] = setTofStation.size() + 1;
+          cbm::algo::evbuild::DigiEventSelector evfilter22(filterParams);
 
-          EXPECT_EQ(evfilter(eventIn), false);
+          EXPECT_EQ(evfilter22(eventIn), false);
         }
       }
     }
diff --git a/core/data/base/CbmDigiData.h b/core/data/base/CbmDigiData.h
index 36282ed401..45e4274f3a 100644
--- a/core/data/base/CbmDigiData.h
+++ b/core/data/base/CbmDigiData.h
@@ -77,6 +77,22 @@ public:
     fFsd.Clear();
     fRich.Clear();
   }
+
+  /** @brief Size of detector data **/
+  size_t Size(ECbmModuleId system) const
+  {
+    switch (system) {
+      case ECbmModuleId::kT0: return fT0.Size(); break;
+      case ECbmModuleId::kSts: return fSts.Size(); break;
+      case ECbmModuleId::kMuch: return fMuch.Size(); break;
+      case ECbmModuleId::kTrd: return fTrd.Size(); break;
+      case ECbmModuleId::kTrd2d: return fTrd2d.Size(); break;
+      case ECbmModuleId::kTof: return fTof.Size(); break;
+      case ECbmModuleId::kPsd: return fPsd.Size(); break;
+      case ECbmModuleId::kRich: return fRich.Size(); break;
+      default: return 0; break;
+    }
+  }
 };
 
 BOOST_CLASS_VERSION(CbmDigiData, 5)
diff --git a/core/data/psd/CbmPsdDigiData.h b/core/data/psd/CbmPsdDigiData.h
index 3c5d6dda4a..0a64076dbb 100644
--- a/core/data/psd/CbmPsdDigiData.h
+++ b/core/data/psd/CbmPsdDigiData.h
@@ -43,6 +43,9 @@ public:
   /** @brief Clear content **/
   void Clear() { fDigis.clear(); }
 
+  /** @brief Size **/
+  size_t Size() const { return fDigis.size(); }
+
   // --- ROOT serializer
 #ifndef NO_ROOT
   ClassDefNV(CbmPsdDigiData, 1);
diff --git a/reco/tasks/CbmReco.cxx b/reco/tasks/CbmReco.cxx
index 32d1b02070..7b68270a69 100644
--- a/reco/tasks/CbmReco.cxx
+++ b/reco/tasks/CbmReco.cxx
@@ -134,10 +134,10 @@ int32_t CbmReco::Run()
   run.AddTask(trigger.release());
 
   // --- Event selector parameters
-  size_t minStatSts   = fConfig.fSelectMinStationsSts;
-  size_t minStatTof   = fConfig.fSelectMinStationsTof;
-  size_t minDigisBmon = fConfig.fSelectMinDigisBmon;
-  auto selectPar      = cbm::algo::DigiEventSelectorParams {minStatSts, minStatTof, minDigisBmon};
+  cbm::algo::evbuild::DigiEventSelectorParams selectPar;
+  selectPar.fMinNumDigis[ECbmModuleId::kT0]   = fConfig.fSelectMinDigisBmon;
+  selectPar.fMinNumLayers[ECbmModuleId::kSts] = fConfig.fSelectMinStationsSts;
+  selectPar.fMinNumLayers[ECbmModuleId::kTof] = fConfig.fSelectMinStationsTof;
 
   // --- Event building
   auto evtBuild = make_unique<CbmTaskBuildEvents>();
diff --git a/reco/tasks/CbmTaskBuildEvents.h b/reco/tasks/CbmTaskBuildEvents.h
index 056d3d1006..495d285083 100644
--- a/reco/tasks/CbmTaskBuildEvents.h
+++ b/reco/tasks/CbmTaskBuildEvents.h
@@ -69,16 +69,9 @@ public:
    ** @param params Struct with minimum number of layers for different detectors
    **/
 
-  void SetDigiEventSelector(cbm::algo::DigiEventSelectorParams params)
+  void SetDigiEventSelector(cbm::algo::evbuild::DigiEventSelectorParams params)
   {
-    if (fSelector == nullptr) {
-      // New selector, straightforward
-      fSelector = std::make_unique<cbm::algo::DigiEventSelector>(params);
-    }
-    else {
-      // Re-use existing selector as functor without state
-      fSelector->SetParams(params);
-    }
+    fSelector.reset(new cbm::algo::evbuild::DigiEventSelector(params));
   }
 
 
@@ -98,12 +91,12 @@ private:  // methods
    **/
   size_t GetNumDigis(const CbmDigiData& data, ECbmModuleId system);
 
-private:                                                    // members
-  const CbmDigiTimeslice* fTimeslice   = nullptr;           //! Input data (from unpacking)
-  CbmDigiManager* fDigiMan             = nullptr;           //! Input data (from simulation)
-  const std::vector<double>* fTriggers = nullptr;           //! Input data (triggers)
-  std::vector<CbmDigiEvent>* fEvents   = nullptr;           //! Output data (events)
-  std::unique_ptr<cbm::algo::DigiEventSelector> fSelector;  //! Event selector
+private:                                                             // members
+  const CbmDigiTimeslice* fTimeslice   = nullptr;                    //! Input data (from unpacking)
+  CbmDigiManager* fDigiMan             = nullptr;                    //! Input data (from simulation)
+  const std::vector<double>* fTriggers = nullptr;                    //! Input data (triggers)
+  std::vector<CbmDigiEvent>* fEvents   = nullptr;                    //! Output data (events)
+  std::unique_ptr<cbm::algo::evbuild::DigiEventSelector> fSelector;  //! Event selector
 
   cbm::algo::EventBuilder fAlgo {};  //! Algorithm
 
-- 
GitLab