From e622572913b3b94a6d68cead718ed7611d58c1ab Mon Sep 17 00:00:00 2001
From: Volker Friese <v.friese@gsi.de>
Date: Fri, 3 May 2024 14:34:36 +0000
Subject: [PATCH] Implementation of a hit multiplicity trigger

---
 algo/CMakeLists.txt                    |  1 +
 algo/evbuild/Config.cxx                | 10 ++--
 algo/evbuild/Config.h                  |  1 +
 algo/evbuild/EventbuildChain.cxx       | 23 +++++---
 algo/evbuild/EventbuildChain.h         |  9 ++-
 algo/global/Reco.cxx                   | 55 +++++++++---------
 algo/test/_GTestTimeClusterTrigger.cxx |  3 +-
 algo/trigger/HitMultTrigger.cxx        | 70 +++++++++++++++++++++++
 algo/trigger/HitMultTrigger.h          | 77 ++++++++++++++++++++++++++
 algo/trigger/TimeClusterTrigger.cxx    | 17 ++----
 algo/trigger/TimeClusterTrigger.h      | 21 +++++--
 reco/tasks/CbmTaskTriggerDigi.cxx      | 17 ++++--
 12 files changed, 239 insertions(+), 65 deletions(-)
 create mode 100644 algo/trigger/HitMultTrigger.cxx
 create mode 100644 algo/trigger/HitMultTrigger.h

diff --git a/algo/CMakeLists.txt b/algo/CMakeLists.txt
index 0a3ec4b62d..a6af3283b8 100644
--- a/algo/CMakeLists.txt
+++ b/algo/CMakeLists.txt
@@ -88,6 +88,7 @@ set(SRCS
   evbuild/EventBuilderConfig.cxx
   evbuild/EventbuildChain.cxx
   trigger/DigiTriggerConfig.cxx
+  trigger/HitMultTrigger.cxx
   trigger/TimeClusterTrigger.cxx
   trigger/V0Trigger.cxx
   trigger/V0TriggerConfig.cxx
diff --git a/algo/evbuild/Config.cxx b/algo/evbuild/Config.cxx
index 8b97e0b835..cf34f09388 100644
--- a/algo/evbuild/Config.cxx
+++ b/algo/evbuild/Config.cxx
@@ -14,6 +14,7 @@ namespace cbm::algo::evbuild
   // -----   Constructor from YAML   --------------------------------------------
   Config::Config(YAML::Node node)
     : fDigiTrigger(node["trigger"])
+    , fHitMultTrigger(node["hit_mult_trigger"])
     , fV0Trigger(node["v0trigger"])
     , fBuilder(node["eventbuilder"])
     , fSelector(node["selector"])
@@ -27,10 +28,11 @@ namespace cbm::algo::evbuild
   YAML::Node Config::ToYaml() const
   {
     YAML::Node result;
-    result["digitrigger"]  = fDigiTrigger.ToYaml();  // Digi trigger config
-    result["v0trigger"]    = fV0Trigger.ToYaml();    // V0 trigger config
-    result["eventbuilder"] = fBuilder.ToYaml();      // Event builder config
-    result["selector"]     = fSelector.ToYaml();     // Event selector config
+    result["trigger"]          = fDigiTrigger.ToYaml();     // Digi trigger config
+    result["hit_mult_trigger"] = fHitMultTrigger.ToYaml();  // Hit multiplicity trigger config
+    result["v0trigger"]        = fV0Trigger.ToYaml();       // V0 trigger config
+    result["eventbuilder"]     = fBuilder.ToYaml();         // Event builder config
+    result["selector"]         = fSelector.ToYaml();        // Event selector config
     return result;
   }
   // ----------------------------------------------------------------------------
diff --git a/algo/evbuild/Config.h b/algo/evbuild/Config.h
index eb46b2c742..974e2b8b03 100644
--- a/algo/evbuild/Config.h
+++ b/algo/evbuild/Config.h
@@ -31,6 +31,7 @@ namespace cbm::algo::evbuild
 
    public:                              // data members
     DigiTriggerConfig fDigiTrigger;     ///< Digi trigger configuration
+    DigiTriggerConfig fHitMultTrigger;  ///< Hit multiplicity trigger configuration
     V0TriggerConfig fV0Trigger;         ///< V0 trigger configuration
     EventBuilderConfig fBuilder;        ///< Event builder configuration
     DigiEventSelectorConfig fSelector;  ///< Event selector configuration
diff --git a/algo/evbuild/EventbuildChain.cxx b/algo/evbuild/EventbuildChain.cxx
index 8d9df4cff0..a1fdda658e 100644
--- a/algo/evbuild/EventbuildChain.cxx
+++ b/algo/evbuild/EventbuildChain.cxx
@@ -20,7 +20,8 @@ using namespace cbm::algo::evbuild;
 EventbuildChain::EventbuildChain(const Config& config, std::shared_ptr<HistogramSender> sender)
   : fConfig(config)
   , fTriggerDet(config.fDigiTrigger.Detector())
-  , fDigiTrigger(config.fDigiTrigger)
+  , fDigiMultTrigger(config.fDigiTrigger.Window(), config.fDigiTrigger.Threshold(), config.fDigiTrigger.DeadTime())
+  , fHitMultTrigger(config.fHitMultTrigger)
   , fV0Trigger()
   , fBuilder(config.fBuilder)
   , fSelector(config.fSelector)
@@ -65,8 +66,7 @@ EventbuildChain::~EventbuildChain() {}
 // -----   Run event building on a timeslice   --------------------------------
 typedef cbm::algo::ca::Vector<cbm::algo::ca::Track> TrackVector;
 
-EventbuildChain::ResultType EventbuildChain::Run(const DigiData& digiData,
-                                                 const cbm::algo::ca::Vector<cbm::algo::ca::Track>& tracks)
+EventbuildChain::ResultType EventbuildChain::Run(const DigiData& digiData, const RecoResults& recoData)
 {
 
   // --- Local variables
@@ -75,17 +75,24 @@ EventbuildChain::ResultType EventbuildChain::Run(const DigiData& digiData,
 
   // --- If V0Trigger is configured, use it
   if (fConfig.fV0Trigger.IsSet()) {
-    auto [v0Triggers, v0TriggerMon] = fV0Trigger(tracks, fConfig.fV0Trigger);
+    auto [v0Triggers, v0TriggerMon] = fV0Trigger(recoData.tracks, fConfig.fV0Trigger);
     triggers                        = std::move(v0Triggers);
     result.second.v0Trigger         = std::move(v0TriggerMon);
   }
 
+  // --- Else, check the hit multiplicity trigger
+  else if (fConfig.fHitMultTrigger.IsSet()) {
+    auto [hitTriggers, digiTriggerMon] = fHitMultTrigger(recoData);
+    triggers                           = std::move(hitTriggers);
+    result.second.hitMultTrigger       = std::move(digiTriggerMon);
+  }
+
   // --- Else, use the digi multiplicity trigger
   else if (fConfig.fDigiTrigger.IsSet()) {
     std::vector<double> digiTimes       = GetDigiTimes(digiData, fTriggerDet);
-    auto [digiTriggers, digiTriggerMon] = fDigiTrigger(digiTimes);
+    auto [digiTriggers, digiTriggerMon] = fDigiMultTrigger(digiTimes);
     triggers                            = std::move(digiTriggers);
-    result.second.digiTrigger           = std::move(digiTriggerMon);
+    result.second.digiMultTrigger       = std::move(digiTriggerMon);
   }
 
   // --- Else, throw exception
@@ -123,9 +130,11 @@ void EventbuildChain::Status() const
   L_(info) << "===   Eventbuilder configuration   ===================";
   if (fConfig.fV0Trigger.IsSet())
     L_(info) << fV0Trigger.ToString();
+  else if (fConfig.fHitMultTrigger.IsSet())
+    L_(info) << fHitMultTrigger.ToString();
   else {
     L_(info) << "--- Using digi multiplicity trigger with trigger detector " << ::ToString(fTriggerDet);
-    L_(info) << fDigiTrigger.ToString();
+    L_(info) << fDigiMultTrigger.ToString();
   }
   L_(info) << fBuilder.ToString();
   L_(info) << fSelector.ToString();
diff --git a/algo/evbuild/EventbuildChain.h b/algo/evbuild/EventbuildChain.h
index dda5be424b..ed6be01c66 100644
--- a/algo/evbuild/EventbuildChain.h
+++ b/algo/evbuild/EventbuildChain.h
@@ -11,6 +11,7 @@
 #include "DigiEventSelector.h"
 #include "EventBuilder.h"
 #include "HistogramSender.h"
+#include "HitMultTrigger.h"
 #include "RecoResults.h"
 #include "SubChain.h"
 #include "TimeClusterTrigger.h"
@@ -28,7 +29,8 @@ namespace cbm::algo::evbuild
 
   struct EventbuildChainMonitorData {
     EventBuilderMonitorData evbuild;
-    TimeClusterTriggerMonitorData digiTrigger;
+    TimeClusterTriggerMonitorData digiMultTrigger;
+    TimeClusterTriggerMonitorData hitMultTrigger;
     V0TriggerMoniData v0Trigger;
   };
 
@@ -54,7 +56,7 @@ namespace cbm::algo::evbuild
     ~EventbuildChain();
 
     /** @brief Execution **/
-    ResultType Run(const DigiData&, const cbm::algo::ca::Vector<cbm::algo::ca::Track>&);
+    ResultType Run(const DigiData&, const RecoResults&);
 
     /** @brief Status info to logger **/
     void Status() const;
@@ -65,7 +67,8 @@ namespace cbm::algo::evbuild
    private:                                              // members
     Config fConfig;                                      ///< Global configuration
     ECbmModuleId fTriggerDet = ECbmModuleId::kNotExist;  ///< Trigger detector
-    TimeClusterTrigger fDigiTrigger;                     ///< Digi multiplicity trigger algorithm
+    TimeClusterTrigger fDigiMultTrigger;                 ///< Digi multiplicity trigger algorithm
+    HitMultTrigger fHitMultTrigger;                      ///< Hit multiplicity trigger algorithm
     V0Trigger fV0Trigger;                                ///< V0 trigger algorithm
     EventBuilder fBuilder;                               ///< Event builder algorithm
     DigiEventSelector fSelector;                         ///< Event selector algorithm
diff --git a/algo/global/Reco.cxx b/algo/global/Reco.cxx
index 1796c2745f..249a912361 100644
--- a/algo/global/Reco.cxx
+++ b/algo/global/Reco.cxx
@@ -164,7 +164,7 @@ void Reco::Init(const Options& opts)
   if (Opts().Has(fles::Subsystem::STS) && Opts().Has(Step::LocalReco)) {
     sts::HitfinderPars hitFinderSetup =
       yaml::ReadFromFile<sts::HitfinderPars>(opts.ParamsDir() / parFiles.sts.hitfinder);
-    hitFinderSetup.landauTable        = sts::LandauTable::FromFile(opts.ParamsDir() / "LandauWidthTable.txt");
+    hitFinderSetup.landauTable = sts::LandauTable::FromFile(opts.ParamsDir() / "LandauWidthTable.txt");
     sts::HitfinderChainPars hitFinderPars;
     hitFinderPars.setup  = std::move(hitFinderSetup);
     hitFinderPars.memory = Params().sts.memory;
@@ -211,7 +211,8 @@ RecoResults Reco::Run(const fles::Timeslice& ts)
 
   ProcessingMonitor procMon;
 
-  RecoResults results;
+  RecoResults recoData;  /// transient
+  RecoResults results;   /// persistent (return object)
   {
     xpu::scoped_timer t_(fmt::format("TS {}", ts.index()), &procMon.time);
     xpu::t_add_bytes(ts_utils::SizeBytes(ts));
@@ -251,16 +252,16 @@ RecoResults Reco::Run(const fles::Timeslice& ts)
     }
 
     sts::HitfinderMon stsHitfinderMonitor;
-    PartitionedSpan<sts::Hit> stsHits;
-    PartitionedVector<sts::Cluster> stsClusters;
     if (fStsHitFinder) {
       bool storeClusters  = Opts().HasOutput(RecoData::Cluster);
       auto stsResults     = (*fStsHitFinder)(digis.fSts, storeClusters);
-      stsHits             = stsResults.hits;
-      stsClusters         = std::move(stsResults.clusters);
       stsHitfinderMonitor = std::move(stsResults.monitor);
+      recoData.stsHits    = stsResults.hits;
+      ;
+      recoData.stsClusters = std::move(stsResults.clusters);
       QueueStsRecoMetrics(stsHitfinderMonitor);
     }
+    L_(info) << "TS contains " << recoData.stsHits.NElements() << " STS Hits";
 
     PartitionedVector<tof::Hit> tofHits;
     if (Opts().Has(Step::LocalReco) && Opts().Has(fles::Subsystem::TOF)) {
@@ -269,13 +270,12 @@ RecoResults Reco::Run(const fles::Timeslice& ts)
       if (nUnknownRPC > 0) {
         L_(error) << "TOF Digis with unknown RPCs: " << nUnknownRPC;
       }
-
       auto [hits, hitmonitor, digiindices] = (*fTofHitFinder)(caldigis);
-      tofHits                              = std::move(hits);
+      recoData.tofHits                     = std::move(hits);
       QueueTofCalibMetrics(calmonitor);
       QueueTofRecoMetrics(hitmonitor);
     }
-    L_(info) << "TS contains " << tofHits.NElements() << " TOF Hits";
+    L_(info) << "TS contains " << recoData.tofHits.NElements() << " TOF Hits";
 
 
     PartitionedVector<trd::Hit> trdHits;
@@ -287,21 +287,22 @@ RecoResults Reco::Run(const fles::Timeslice& ts)
       allDigis.reserve(digis1d.size() + digis2d.size());
       std::copy(digis1d.begin(), digis1d.end(), std::back_inserter(allDigis));
       std::copy(digis2d.begin(), digis2d.end(), std::back_inserter(allDigis));
-      auto trdResults = (*fTrdHitfind)(allDigis);
-      trdHits         = std::move(std::get<0>(trdResults));
+      auto trdResults  = (*fTrdHitfind)(allDigis);
+      recoData.trdHits = std::move(std::get<0>(trdResults));
       QueueTrdRecoMetrics(std::get<1>(trdResults));
-      L_(info) << "TS has " << trdHits.NElements() << " TRD hits.";
+      L_(info) << "TS has " << recoData.trdHits.NElements() << " TRD hits.";
     }
 
     // --- Tracking
     TrackingChain::Output_t trackingOutput{};
     if (Opts().Has(Step::Tracking)) {
       TrackingChain::Input_t input{
-        .stsHits = stsHits,
-        .tofHits = tofHits,
-        .trdHits = trdHits,
+        .stsHits = recoData.stsHits,
+        .tofHits = recoData.tofHits,
+        .trdHits = recoData.trdHits,
       };
-      trackingOutput = fTracking->Run(input);
+      trackingOutput  = fTracking->Run(input);
+      recoData.tracks = std::move(trackingOutput.tracks);
       QueueTrackingMetrics(trackingOutput.monitorData);
     }
 
@@ -309,13 +310,13 @@ RecoResults Reco::Run(const fles::Timeslice& ts)
     std::vector<DigiEvent> events;
     evbuild::EventbuildChainMonitorData evbuildMonitor;
     if (Opts().Has(Step::DigiTrigger)) {
-      auto [ev, mon] = fEventBuild->Run(digis, trackingOutput.tracks);
+      auto [ev, mon] = fEventBuild->Run(digis, recoData);
       events         = std::move(ev);
       evbuildMonitor = mon;
       QueueEvbuildMetrics(evbuildMonitor);
     }
 
-
+    // --- Filter data for output
     if (Opts().HasOutput(RecoData::DigiTimeslice)) {
       results.bmonDigis  = std::move(digis.fBmon);
       results.stsDigis   = std::move(digis.fSts);
@@ -326,16 +327,16 @@ RecoResults Reco::Run(const fles::Timeslice& ts)
       results.richDigis  = std::move(digis.fRich);
     }
     if (Opts().HasOutput(RecoData::Track)) {
-      results.tracks             = std::move(trackingOutput.tracks);
+      results.tracks             = std::move(recoData.tracks);
       results.trackStsHitIndices = std::move(trackingOutput.stsHitIndices);
       results.trackTofHitIndices = std::move(trackingOutput.tofHitIndices);
     }
     if (Opts().HasOutput(RecoData::DigiEvent)) results.events = std::move(events);
-    if (Opts().HasOutput(RecoData::Cluster)) results.stsClusters = std::move(stsClusters);
+    if (Opts().HasOutput(RecoData::Cluster)) results.stsClusters = std::move(recoData.stsClusters);
     if (Opts().HasOutput(RecoData::Hit)) {
-      results.stsHits = std::move(stsHits);
-      results.tofHits = std::move(tofHits);
-      results.trdHits = std::move(trdHits);
+      results.stsHits = std::move(recoData.stsHits);
+      results.tofHits = std::move(recoData.tofHits);
+      results.trdHits = std::move(recoData.trdHits);
     }
   }
   PrintTimings(procMon.time);
@@ -506,8 +507,10 @@ void Reco::QueueEvbuildMetrics(const evbuild::EventbuildChainMonitorData& mon)
 
   double totalSelectionRatio = nDigisTotal > 0 ? double(nDigisInEventsTotal) / nDigisTotal : 0;
   GetMonitor().QueueMetric("cbmreco", tags,
-                           {{"digiTriggerTimeTotal", mon.digiTrigger.time.wall()},
-                            {"digiTriggerThroughput", mon.digiTrigger.time.throughput()},
+                           {{"digiTriggerTimeTotal", mon.digiMultTrigger.time.wall()},
+                            {"digiTriggerThroughput", mon.digiMultTrigger.time.throughput()},
+                            {"hitTriggerTimeTotal", mon.hitMultTrigger.time.wall()},
+                            {"hitTriggerThroughput", mon.hitMultTrigger.time.throughput()},
                             {"v0TriggerNumTrackPairs", mon.v0Trigger.numTrackPairs},
                             {"v0TriggerNumTrackPairsCoinc", mon.v0Trigger.numTrackPairsAfterTimeCut},
                             {"v0TriggerErrTracksUnsorted", mon.v0Trigger.errTracksUnsorted},
@@ -515,7 +518,7 @@ void Reco::QueueEvbuildMetrics(const evbuild::EventbuildChainMonitorData& mon)
                             {"v0TriggerThroughput", mon.v0Trigger.time.throughput()},
                             {"eventbuildTimeTotal", mon.evbuild.time.wall()},
                             {"eventbuildThroughput", mon.evbuild.time.throughput()},
-                            {"numTrigger", mon.digiTrigger.nTriggers},
+                            {"numTrigger", mon.digiMultTrigger.nTriggers},
                             {"numEvents", mon.evbuild.nEvents},
                             {"totalEvSelectionRatio", totalSelectionRatio}});
 }
diff --git a/algo/test/_GTestTimeClusterTrigger.cxx b/algo/test/_GTestTimeClusterTrigger.cxx
index dc23e61b25..168c614662 100644
--- a/algo/test/_GTestTimeClusterTrigger.cxx
+++ b/algo/test/_GTestTimeClusterTrigger.cxx
@@ -18,8 +18,7 @@ TEST(_GTestTimeClusterTrigger, CheckTriggerAlgorithmSimple)
   const double windowSize   = 1000.;
   const uint nMinNumber     = 100;
 
-  DigiTriggerConfig config = {ECbmModuleId::kSts, windowSize, nMinNumber, deadTime};
-  TimeClusterTrigger trigger(config);
+  TimeClusterTrigger trigger(windowSize, nMinNumber, deadTime);
 
   std::vector<double> dataIn;
   for (uint i = 0; i < nInput; i++) {
diff --git a/algo/trigger/HitMultTrigger.cxx b/algo/trigger/HitMultTrigger.cxx
new file mode 100644
index 0000000000..2ffc10d697
--- /dev/null
+++ b/algo/trigger/HitMultTrigger.cxx
@@ -0,0 +1,70 @@
+/* Copyright (C) 2024 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Volker Friese [committer], Dominik Smith */
+
+#include "HitMultTrigger.h"
+
+#include "CbmStsHit.h"
+#include "CbmTofHit.h"
+#include "CbmTrdHit.h"
+
+#include <iterator>
+#include <sstream>
+
+#include <xpu/host.h>
+
+namespace cbm::algo::evbuild
+{
+
+  // -----   Execution   ------------------------------------------------------
+  HitMultTrigger::Result HitMultTrigger::operator()(const RecoResults& recoData) const
+  {
+
+    xpu::push_timer("HitMultTrigger");
+    xpu::t_add_bytes(1);
+
+    Result result;
+    auto hitTimes             = GetHitTimes(recoData, fConfig.Detector());
+    auto [triggers, moniData] = fAlgo(hitTimes);
+    result.first              = std::move(triggers);
+    result.second             = std::move(moniData);
+    result.second.time        = xpu::pop_timer();
+
+    L_(info) << "HitMultTrigger: hits " << hitTimes.size() << ", triggers " << result.first.size();
+
+    return result;
+  };
+  // --------------------------------------------------------------------------
+
+
+  // -----   Get hit times from reconstructed data   --------------------------
+  std::vector<double> HitMultTrigger::GetHitTimes(const RecoResults& recoResults, ECbmModuleId system) const
+  {
+    std::vector<double> result;
+    switch (system) {
+      case ECbmModuleId::kSts: return GetTimeStamps<cbm::algo::sts::Hit>(recoResults.stsHits.Data()); break;
+      case ECbmModuleId::kTrd: return GetTimeStamps<cbm::algo::trd::Hit>(recoResults.trdHits.Data()); break;
+      case ECbmModuleId::kTof: return GetTimeStamps<cbm::algo::tof::Hit>(recoResults.tofHits.Data()); break;
+      default: {
+        L_(error) << "HitMultTrigger::GetHitTimes: Unknown system " << system;
+        break;
+      }
+    }  //? system
+
+    return result;
+  }
+  // --------------------------------------------------------------------------
+
+
+  // -----   Info to string ---------------------------------------------------
+  std::string HitMultTrigger::ToString() const
+  {
+    std::stringstream out;
+    out << "--- Using Hit Multiplicity Trigger with trigger detector " << ::ToString(fConfig.Detector());
+    out << "\n" << fAlgo.ToString();
+    return out.str();
+  }
+  // --------------------------------------------------------------------------
+
+
+}  // namespace cbm::algo::evbuild
diff --git a/algo/trigger/HitMultTrigger.h b/algo/trigger/HitMultTrigger.h
new file mode 100644
index 0000000000..946429c101
--- /dev/null
+++ b/algo/trigger/HitMultTrigger.h
@@ -0,0 +1,77 @@
+/* Copyright (C) 2024 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Volker Friese [committer] */
+
+#pragma once  // include this header only once per compilation unit
+
+#include "DigiTriggerConfig.h"
+#include "RecoResults.h"
+#include "TimeClusterTrigger.h"
+
+#include <utility>
+#include <vector>
+
+#include <xpu/host.h>
+
+namespace cbm::algo::evbuild
+{
+
+  /** @class HitMultTrigger
+   ** @brief Trigger class for finding time clusters of hit data
+   ** @author Volker Friese <v.friese@gsi.de>
+   ** @date 30 April 2024
+   **
+   ** The class takes as input an array of detector hits. It finds clusters of hits in time, using the generic TimeClusterTrigger algorithm.
+   **/
+  class HitMultTrigger {
+
+   public:
+    typedef std::pair<std::vector<double>, TimeClusterTriggerMonitorData> Result;
+
+    /** @brief Constructor
+     ** @param config Trigger configuration
+     **/
+    HitMultTrigger(const DigiTriggerConfig& config)
+      : fConfig(config)
+      , fAlgo(config.Window(), config.Threshold(), config.DeadTime()){};
+
+    /** @brief Execution
+     ** @param  recoData    Container of reco data
+     ** @return Vector of trigger times and monitoring data
+     **/
+    Result operator()(const RecoResults& recoData) const;
+
+    /** @brief Info to string **/
+    std::string ToString() const;
+
+   private:  // methods
+    /** @brief Extract the hit time stamps for the selected trigger detector 
+     ** @param recoData  Container of reco results
+     ** @param system    Trigger Detector
+     ** @return Sorted vector of hit time stamps
+     **/
+    std::vector<double> GetHitTimes(const RecoResults& recoData, ECbmModuleId system) const;
+
+    /** @brief Get vector of time stamps from a data container [template]
+     ** @param data Data container
+     ** @return Sorted vector of time stamps
+     **
+     ** The template argument class must implement the method Time().
+     **/
+    template<class T>
+    std::vector<double> GetTimeStamps(const gsl::span<const T> data) const
+    {
+      std::vector<double> result;
+      result.resize(data.size());
+      std::transform(data.begin(), data.end(), result.begin(), [](const T& obj) { return obj.Time(); });
+      std::sort(result.begin(), result.end());
+      return result;
+    }
+
+
+   private:                     // members
+    DigiTriggerConfig fConfig;  ///< Configuration
+    TimeClusterTrigger fAlgo;   ///< Algorithm
+  };
+
+}  // namespace cbm::algo::evbuild
diff --git a/algo/trigger/TimeClusterTrigger.cxx b/algo/trigger/TimeClusterTrigger.cxx
index 2eaf4f0ead..06b84f364f 100644
--- a/algo/trigger/TimeClusterTrigger.cxx
+++ b/algo/trigger/TimeClusterTrigger.cxx
@@ -27,11 +27,6 @@ namespace cbm::algo::evbuild
     xpu::push_timer("TimeClusterTrigger");
     xpu::t_add_bytes(dataVec.size() * sizeof(double));
 
-    // --- Parameters
-    double winSize     = fConfig.Window();
-    int32_t minNumData = fConfig.Threshold();
-    double deadTime    = fConfig.DeadTime();
-
     // --- Output data
     resultType result                      = {};
     vector<double>& triggerVec             = result.first;
@@ -43,11 +38,11 @@ namespace cbm::algo::evbuild
     while (current != dataVec.end()) {
 
       // If window size is exceeded, adjust window start
-      while (*current - *winStart > winSize)
+      while (*current - *winStart > fWinSize)
         winStart++;
 
       // Create trigger if threshold is reached
-      if (std::distance(winStart, current) >= minNumData - 1) {
+      if (std::distance(winStart, current) >= fMinNumData - 1) {
         triggerVec.push_back(0.5 * (*current + *winStart));
 
         // Increment monitoring counter with number of digis used
@@ -55,7 +50,7 @@ namespace cbm::algo::evbuild
 
         // Start new window after dead time
         winStart = current + 1;
-        while (winStart != dataVec.end() && *winStart - *current <= deadTime)
+        while (winStart != dataVec.end() && *winStart - *current <= fDeadTime)
           winStart++;
         current = winStart;
       }
@@ -77,9 +72,9 @@ namespace cbm::algo::evbuild
   string TimeClusterTrigger::ToString() const
   {
     stringstream out;
-    out << "--- Using TimeClusterTrigger with trigger window " << fConfig.Window() << " ns";
-    out << ", threshold " << fConfig.Threshold();
-    out << ", dead time " << fConfig.DeadTime() << " ns";
+    out << "--- Using TimeClusterTrigger with trigger window " << fWinSize << " ns";
+    out << ", threshold " << fMinNumData;
+    out << ", dead time " << fDeadTime << " ns";
     return out.str();
   }
 
diff --git a/algo/trigger/TimeClusterTrigger.h b/algo/trigger/TimeClusterTrigger.h
index 66ea149410..9e2e0a7df4 100644
--- a/algo/trigger/TimeClusterTrigger.h
+++ b/algo/trigger/TimeClusterTrigger.h
@@ -5,6 +5,7 @@
 #ifndef CBM_ALGO_TIMECLUSTERTRIGGER_H
 #define CBM_ALGO_TIMECLUSTERTRIGGER_H 1
 
+#include "Definitions.h"
 #include "DigiTriggerConfig.h"
 
 #include <cstddef>
@@ -44,14 +45,20 @@ namespace cbm::algo::evbuild
    public:
     typedef std::pair<std::vector<double>, TimeClusterTriggerMonitorData> resultType;
 
-    /** @brief Constructor **/
-    TimeClusterTrigger(const DigiTriggerConfig& config) : fConfig(config) {}
-
-    /** @brief Execution
-     ** @param  dataVec     Source data vector
+    /** @brief Constructor
      ** @param  winSize     Size of trigger window
      ** @param  minNumData  Threshold on number of data within the trigger window
      ** @param  deadTime    Minimum time between two triggers
+     **/
+    TimeClusterTrigger(double winSize, u32 minNumData, double deadTime)
+      : fWinSize(winSize)
+      , fMinNumData(minNumData)
+      , fDeadTime(deadTime)
+    {
+    }
+
+    /** @brief Execution
+     ** @param  dataVec     Source data vector
      ** @return Vector of trigger times and monitoring data
      **/
     resultType operator()(const std::vector<double>& dataVec) const;
@@ -61,7 +68,9 @@ namespace cbm::algo::evbuild
 
 
    private:
-    DigiTriggerConfig fConfig;
+    double fWinSize     = 0.;
+    int32_t fMinNumData = 0;
+    double fDeadTime    = 0.;
   };
 
 
diff --git a/reco/tasks/CbmTaskTriggerDigi.cxx b/reco/tasks/CbmTaskTriggerDigi.cxx
index 355fb85745..4bb45239e7 100644
--- a/reco/tasks/CbmTaskTriggerDigi.cxx
+++ b/reco/tasks/CbmTaskTriggerDigi.cxx
@@ -14,12 +14,11 @@
 #include "CbmStsDigi.h"
 #include "CbmTofDigi.h"
 #include "CbmTrdDigi.h"
+#include "TimeClusterTrigger.h"
 
 #include <FairRootManager.h>
 #include <Logger.h>
 
-#include "TimeClusterTrigger.h"
-
 #include <algorithm>
 #include <cassert>
 #include <iomanip>
@@ -54,7 +53,9 @@ void CbmTaskTriggerDigi::Exec(Option_t*)
       std::vector<double> systemDigiTimes = GetDigiTimes(system);
       digiTimes.insert(digiTimes.end(), systemDigiTimes.begin(), systemDigiTimes.end());
     }
-    if (fSystems.size() > 1) { std::sort(digiTimes.begin(), digiTimes.end()); }
+    if (fSystems.size() > 1) {
+      std::sort(digiTimes.begin(), digiTimes.end());
+    }
   }
 
   // --- Case: input digi branches
@@ -98,7 +99,9 @@ void CbmTaskTriggerDigi::Exec(Option_t*)
       }
       digiTimes.insert(digiTimes.end(), locDigiTimes.begin(), locDigiTimes.end());
     }
-    if (fSystems.size() > 1) { std::sort(digiTimes.begin(), digiTimes.end()); }
+    if (fSystems.size() > 1) {
+      std::sort(digiTimes.begin(), digiTimes.end());
+    }
   }
 
   // --- Call the trigger algorithm
@@ -226,7 +229,9 @@ InitStatus CbmTaskTriggerDigi::Init()
   // --- Check input data
   // --- DigiTimeslice: Unpacked data from FLES
   fTimeslice = ioman->InitObjectAs<const CbmDigiTimeslice*>("DigiTimeslice.");
-  if (fTimeslice) { LOG(info) << "--- Found branch DigiTimeslice."; }
+  if (fTimeslice) {
+    LOG(info) << "--- Found branch DigiTimeslice.";
+  }
   // --- DigiManager: Simulated digi data
   else {
     fDigiMan = CbmDigiManager::Instance();
@@ -254,7 +259,7 @@ InitStatus CbmTaskTriggerDigi::Init()
   LOG(info) << "--- Registered branch Trigger";
 
   // --- Configure algorithm
-  fAlgo.reset(new cbm::algo::evbuild::TimeClusterTrigger(*fConfig));
+  fAlgo.reset(new cbm::algo::evbuild::TimeClusterTrigger(fConfig->Window(), fConfig->Threshold(), fConfig->DeadTime()));
   LOG(info) << "--- Using trigger detector " << ::ToString(fConfig->Detector());
   LOG(info) << fAlgo->ToString();
 
-- 
GitLab