From a18234e03011a033115602e58d6919936da2672f Mon Sep 17 00:00:00 2001
From: Volker Friese <v.friese@gsi.de>
Date: Sat, 27 Apr 2024 15:02:34 +0000
Subject: [PATCH] Integrate v0 trigger into the online processing chain

---
 algo/CMakeLists.txt                |  1 +
 algo/evbuild/Config.cxx            | 10 +++--
 algo/evbuild/Config.h              |  4 +-
 algo/evbuild/EventbuildChain.cxx   | 57 +++++++++++++++++-------
 algo/evbuild/EventbuildChain.h     | 14 +++---
 algo/global/Reco.cxx               | 44 ++++++++++--------
 algo/trigger/DigiTriggerConfig.cxx |  7 ++-
 algo/trigger/DigiTriggerConfig.h   |  5 +++
 algo/trigger/V0Trigger.cxx         | 36 ++++++++++++---
 algo/trigger/V0Trigger.h           | 24 +++++-----
 algo/trigger/V0TriggerConfig.cxx   | 48 ++++++++++++++++++++
 algo/trigger/V0TriggerConfig.h     | 71 ++++++++++++++++++++++++++++++
 reco/tasks/CbmReco.cxx             | 17 +++----
 13 files changed, 265 insertions(+), 73 deletions(-)
 create mode 100644 algo/trigger/V0TriggerConfig.cxx
 create mode 100644 algo/trigger/V0TriggerConfig.h

diff --git a/algo/CMakeLists.txt b/algo/CMakeLists.txt
index 7da25ea04a..6e2033806a 100644
--- a/algo/CMakeLists.txt
+++ b/algo/CMakeLists.txt
@@ -88,6 +88,7 @@ set(SRCS
   trigger/DigiTriggerConfig.cxx
   trigger/TimeClusterTrigger.cxx
   trigger/V0Trigger.cxx
+  trigger/V0TriggerConfig.cxx
   evselector/DigiEventSelector.cxx
   evselector/DigiEventSelectorConfig.cxx
   unpack/CommonUnpacker.cxx
diff --git a/algo/evbuild/Config.cxx b/algo/evbuild/Config.cxx
index b690b5b1db..8b97e0b835 100644
--- a/algo/evbuild/Config.cxx
+++ b/algo/evbuild/Config.cxx
@@ -13,7 +13,8 @@ namespace cbm::algo::evbuild
 
   // -----   Constructor from YAML   --------------------------------------------
   Config::Config(YAML::Node node)
-    : fTrigger(node["trigger"])
+    : fDigiTrigger(node["trigger"])
+    , fV0Trigger(node["v0trigger"])
     , fBuilder(node["eventbuilder"])
     , fSelector(node["selector"])
   {
@@ -26,9 +27,10 @@ namespace cbm::algo::evbuild
   YAML::Node Config::ToYaml() const
   {
     YAML::Node result;
-    result["trigger"]      = fTrigger.ToYaml();   // Digi trigger config
-    result["eventbuilder"] = fBuilder.ToYaml();   // Event builder config
-    result["selector"]     = fSelector.ToYaml();  // Event selector config
+    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
     return result;
   }
   // ----------------------------------------------------------------------------
diff --git a/algo/evbuild/Config.h b/algo/evbuild/Config.h
index 181cccaf65..eb46b2c742 100644
--- a/algo/evbuild/Config.h
+++ b/algo/evbuild/Config.h
@@ -9,6 +9,7 @@
 #include "DigiEventSelectorConfig.h"
 #include "DigiTriggerConfig.h"
 #include "EventBuilderConfig.h"
+#include "V0Trigger.h"
 
 #include <yaml-cpp/yaml.h>
 
@@ -29,7 +30,8 @@ namespace cbm::algo::evbuild
     YAML::Node ToYaml() const;
 
    public:                              // data members
-    DigiTriggerConfig fTrigger;         ///< Digi trigger configuration
+    DigiTriggerConfig fDigiTrigger;     ///< Digi 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 79ab194194..8d9df4cff0 100644
--- a/algo/evbuild/EventbuildChain.cxx
+++ b/algo/evbuild/EventbuildChain.cxx
@@ -18,8 +18,10 @@ using namespace cbm::algo::evbuild;
 
 // -----   Constructor   ------------------------------------------------------
 EventbuildChain::EventbuildChain(const Config& config, std::shared_ptr<HistogramSender> sender)
-  : fTriggerDet(config.fTrigger.Detector())
-  , fTrigger(config.fTrigger)
+  : fConfig(config)
+  , fTriggerDet(config.fDigiTrigger.Detector())
+  , fDigiTrigger(config.fDigiTrigger)
+  , fV0Trigger()
   , fBuilder(config.fBuilder)
   , fSelector(config.fSelector)
   , fQa(DigiEventQaConfig(config.fBuilder, 10., 100))
@@ -61,17 +63,39 @@ EventbuildChain::~EventbuildChain() {}
 // ----------------------------------------------------------------------------
 
 // -----   Run event building on a timeslice   --------------------------------
-EventbuildChain::ResultType EventbuildChain::Run(const DigiData& 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)
 {
 
-  // --- Extract the digi time stamps of the trigger detector
-  std::vector<double> digiTimes = GetDigiTimes(timeslice, fTriggerDet);
+  // --- Local variables
+  std::vector<double> triggers;
+  ResultType result;
 
-  // --- Call the trigger algorithm
-  auto [triggers, triggerMon] = fTrigger(digiTimes);
+  // --- If V0Trigger is configured, use it
+  if (fConfig.fV0Trigger.IsSet()) {
+    auto [v0Triggers, v0TriggerMon] = fV0Trigger(tracks, fConfig.fV0Trigger);
+    triggers                        = std::move(v0Triggers);
+    result.second.v0Trigger         = std::move(v0TriggerMon);
+  }
+
+  // --- Else, use the digi multiplicity trigger
+  else if (fConfig.fDigiTrigger.IsSet()) {
+    std::vector<double> digiTimes       = GetDigiTimes(digiData, fTriggerDet);
+    auto [digiTriggers, digiTriggerMon] = fDigiTrigger(digiTimes);
+    triggers                            = std::move(digiTriggers);
+    result.second.digiTrigger           = std::move(digiTriggerMon);
+  }
+
+  // --- Else, throw exception
+  else
+    throw std::runtime_error("no trigger is configured");
 
   // --- Perform event building
-  auto [events, evbuildMon] = fBuilder(timeslice, triggers, fSelector);
+  auto [events, evbuildMon] = fBuilder(digiData, triggers, fSelector);
+  result.first              = std::move(events);
+  result.second.evbuild     = evbuildMon;
 
   /// => Histograms serialization and emission
   if (fSender) {
@@ -86,12 +110,7 @@ EventbuildChain::ResultType EventbuildChain::Run(const DigiData& timeslice)
   }
 
   // --- Some log
-  L_(info) << "Triggers: " << triggers.size() << ", events " << events.size();
-
-  ResultType result;
-  result.first          = std::move(events);
-  result.second.trigger = triggerMon;
-  result.second.evbuild = evbuildMon;
+  L_(info) << "Triggers: " << triggers.size() << ", events " << result.first.size();
 
   return result;
 }
@@ -101,9 +120,13 @@ EventbuildChain::ResultType EventbuildChain::Run(const DigiData& timeslice)
 // -----   Status info   ------------------------------------------------------
 void EventbuildChain::Status() const
 {
-  L_(info) << "===   Eventbuiler configuration   ====================";
-  L_(info) << "--- Using trigger detector " << ::ToString(fTriggerDet);
-  L_(info) << fTrigger.ToString();
+  L_(info) << "===   Eventbuilder configuration   ===================";
+  if (fConfig.fV0Trigger.IsSet())
+    L_(info) << fV0Trigger.ToString();
+  else {
+    L_(info) << "--- Using digi multiplicity trigger with trigger detector " << ::ToString(fTriggerDet);
+    L_(info) << fDigiTrigger.ToString();
+  }
   L_(info) << fBuilder.ToString();
   L_(info) << fSelector.ToString();
   L_(info) << fQa.ToString();
diff --git a/algo/evbuild/EventbuildChain.h b/algo/evbuild/EventbuildChain.h
index 5c9c6c1709..dda5be424b 100644
--- a/algo/evbuild/EventbuildChain.h
+++ b/algo/evbuild/EventbuildChain.h
@@ -6,12 +6,15 @@
 #define CBM_ALGO_EVBUILD_EVBUILDCHAIN_H 1
 
 #include "CbmDefs.h"
+#include "Config.h"
 #include "DigiEventQa.h"
 #include "DigiEventSelector.h"
 #include "EventBuilder.h"
 #include "HistogramSender.h"
+#include "RecoResults.h"
 #include "SubChain.h"
 #include "TimeClusterTrigger.h"
+#include "V0Trigger.h"
 
 #include <memory>
 
@@ -23,11 +26,10 @@ namespace cbm::algo
 namespace cbm::algo::evbuild
 {
 
-  class Config;
-
   struct EventbuildChainMonitorData {
     EventBuilderMonitorData evbuild;
-    TimeClusterTriggerMonitorData trigger;
+    TimeClusterTriggerMonitorData digiTrigger;
+    V0TriggerMoniData v0Trigger;
   };
 
   /** @class EventbuildChain
@@ -52,7 +54,7 @@ namespace cbm::algo::evbuild
     ~EventbuildChain();
 
     /** @brief Execution **/
-    ResultType Run(const DigiData&);
+    ResultType Run(const DigiData&, const cbm::algo::ca::Vector<cbm::algo::ca::Track>&);
 
     /** @brief Status info to logger **/
     void Status() const;
@@ -61,8 +63,10 @@ namespace cbm::algo::evbuild
     void RegisterTrackingSetup(std::shared_ptr<TrackingSetup> pSetup) { fSelector.RegisterTrackingSetup(pSetup); }
 
    private:                                              // members
+    Config fConfig;                                      ///< Global configuration
     ECbmModuleId fTriggerDet = ECbmModuleId::kNotExist;  ///< Trigger detector
-    TimeClusterTrigger fTrigger;                         ///< Trigger algorithm
+    TimeClusterTrigger fDigiTrigger;                     ///< Digi multiplicity trigger algorithm
+    V0Trigger fV0Trigger;                                ///< V0 trigger algorithm
     EventBuilder fBuilder;                               ///< Event builder algorithm
     DigiEventSelector fSelector;                         ///< Event selector algorithm
     DigiEventQa fQa;                                     ///< Event QA algorithm
diff --git a/algo/global/Reco.cxx b/algo/global/Reco.cxx
index 2e0c5b0995..5862bfd83e 100644
--- a/algo/global/Reco.cxx
+++ b/algo/global/Reco.cxx
@@ -250,15 +250,6 @@ RecoResults Reco::Run(const fles::Timeslice& ts)
                << " FSD=" << digis.fFsd.size();
     }
 
-    // --- Digi event building
-    std::vector<DigiEvent> events;
-    evbuild::EventbuildChainMonitorData evbuildMonitor;
-    if (Opts().Has(Step::DigiTrigger)) {
-      auto [ev, mon] = fEventBuild->Run(digis);
-      events         = std::move(ev);
-      evbuildMonitor = mon;
-      QueueEvbuildMetrics(evbuildMonitor);
-    }
 
     // --- Raw digi QAs
     if (fSender != nullptr && Opts().Has(Subsystem::STS) && Opts().Has(Step::Unpack)) {
@@ -310,21 +301,28 @@ RecoResults Reco::Run(const fles::Timeslice& ts)
     }
 
     // --- Tracking
+    TrackingChain::Output_t trackingOutput{};
     if (Opts().Has(Step::Tracking)) {
       TrackingChain::Input_t input{
         .stsHits = stsHits,
         .tofHits = tofHits,
         .trdHits = trdHits,
       };
-      TrackingChain::Output_t trackingOutput = fTracking->Run(input);
-      if (Opts().HasOutput(RecoData::Track)) {
-        results.tracks             = std::move(trackingOutput.tracks);
-        results.trackStsHitIndices = std::move(trackingOutput.stsHitIndices);
-        results.trackTofHitIndices = std::move(trackingOutput.tofHitIndices);
-      }
+      trackingOutput = fTracking->Run(input);
       QueueTrackingMetrics(trackingOutput.monitorData);
     }
 
+    // --- Event building
+    std::vector<DigiEvent> events;
+    evbuild::EventbuildChainMonitorData evbuildMonitor;
+    if (Opts().Has(Step::DigiTrigger)) {
+      auto [ev, mon] = fEventBuild->Run(digis, trackingOutput.tracks);
+      events         = std::move(ev);
+      evbuildMonitor = mon;
+      QueueEvbuildMetrics(evbuildMonitor);
+    }
+
+
     if (Opts().HasOutput(RecoData::DigiTimeslice)) {
       results.bmonDigis  = std::move(digis.fBmon);
       results.stsDigis   = std::move(digis.fSts);
@@ -334,6 +332,11 @@ RecoResults Reco::Run(const fles::Timeslice& ts)
       results.tofDigis   = std::move(digis.fTof);
       results.richDigis  = std::move(digis.fRich);
     }
+    if (Opts().HasOutput(RecoData::Track)) {
+      results.tracks             = std::move(trackingOutput.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::Hit)) {
@@ -510,11 +513,16 @@ void Reco::QueueEvbuildMetrics(const evbuild::EventbuildChainMonitorData& mon)
 
   double totalSelectionRatio = nDigisTotal > 0 ? double(nDigisInEventsTotal) / nDigisTotal : 0;
   GetMonitor().QueueMetric("cbmreco", tags,
-                           {{"digiTriggerTimeTotal", mon.trigger.time.wall()},
-                            {"digiTriggerThroughput", mon.trigger.time.throughput()},
+                           {{"digiTriggerTimeTotal", mon.digiTrigger.time.wall()},
+                            {"digiTriggerThroughput", mon.digiTrigger.time.throughput()},
+                            {"v0TriggerNumTrackPairs", mon.v0Trigger.numTrackPairs},
+                            {"v0TriggerNumTrackPairsCoinc", mon.v0Trigger.numTrackPairsAfterTimeCut},
+                            {"v0TriggerErrTracksUnsorted", mon.v0Trigger.errTracksUnsorted},
+                            {"v0TriggerTimeTotal", mon.v0Trigger.time.wall()},
+                            {"v0TriggerThroughput", mon.v0Trigger.time.throughput()},
                             {"eventbuildTimeTotal", mon.evbuild.time.wall()},
                             {"eventbuildThroughput", mon.evbuild.time.throughput()},
-                            {"numTrigger", mon.trigger.nTriggers},
+                            {"numTrigger", mon.digiTrigger.nTriggers},
                             {"numEvents", mon.evbuild.nEvents},
                             {"totalEvSelectionRatio", totalSelectionRatio}});
 }
diff --git a/algo/trigger/DigiTriggerConfig.cxx b/algo/trigger/DigiTriggerConfig.cxx
index 9fbc2ed1f0..cc844930af 100644
--- a/algo/trigger/DigiTriggerConfig.cxx
+++ b/algo/trigger/DigiTriggerConfig.cxx
@@ -11,7 +11,10 @@ namespace cbm::algo::evbuild
   // -----   Constructor from YAML   ------------------------------------------
   DigiTriggerConfig::DigiTriggerConfig(YAML::Node config)
   {
-    if (!config) throw std::runtime_error("no configuration node for DigiTrigger");
+    if (!config) {
+      fIsSet = false;
+      return;
+    }
 
     auto detector = config["detector"];
     if (!detector) throw std::runtime_error("trigger detector is not specified");
@@ -28,6 +31,8 @@ namespace cbm::algo::evbuild
     auto deadTime = config["deadtime"];
     if (!deadTime) throw std::runtime_error("trigger dead time is not specified");
     fDeadTime = deadTime.as<double>();
+
+    fIsSet = true;
   }
   // --------------------------------------------------------------------------
 
diff --git a/algo/trigger/DigiTriggerConfig.h b/algo/trigger/DigiTriggerConfig.h
index ce321b1165..e9ea6f9ec2 100644
--- a/algo/trigger/DigiTriggerConfig.h
+++ b/algo/trigger/DigiTriggerConfig.h
@@ -34,6 +34,7 @@ namespace cbm::algo::evbuild
       , fWindow(window)
       , fThreshold(threshold)
       , fDeadTime(deadTime)
+      , fIsSet(true)
     {
     }
 
@@ -49,6 +50,9 @@ namespace cbm::algo::evbuild
     /** @brief Trigger detector **/
     ECbmModuleId Detector() const { return fDetector; }
 
+    /** @brief Check whether config was set **/
+    bool IsSet() const { return fIsSet; }
+
     /** @brief Trigger threshold **/
     size_t Threshold() const { return fThreshold; }
 
@@ -64,6 +68,7 @@ namespace cbm::algo::evbuild
     double fWindow;          ///< Trigger window size [ns]
     size_t fThreshold;       ///< Minimum number if digis in trigger window
     double fDeadTime;        ///< Minimal time between two trigger [ns]
+    bool fIsSet{false};      ///< Flag config being properly set
   };
 
 
diff --git a/algo/trigger/V0Trigger.cxx b/algo/trigger/V0Trigger.cxx
index 81b5a9377a..041ad7469b 100644
--- a/algo/trigger/V0Trigger.cxx
+++ b/algo/trigger/V0Trigger.cxx
@@ -5,14 +5,21 @@
 #include "V0Trigger.h"
 
 #include <iterator>
+#include <sstream>
 
-namespace cbm::algo
+#include <xpu/host.h>
+
+namespace cbm::algo::evbuild
 {
 
 
   V0Trigger::Result V0Trigger::operator()(const TrackVector& tracks, const V0TriggerConfig& config) const
   {
 
+    xpu::push_timer("V0Trigger");
+    xpu::t_add_bytes(tracks.size() * sizeof(cbm::algo::ca::Track));
+
+
     Result result;
 
     for (auto trackIter1 = tracks.begin(); trackIter1 != tracks.end(); trackIter1++) {
@@ -26,18 +33,26 @@ namespace cbm::algo
           continue;
         }
         result.second.numTrackPairs++;
-        if (time2 - time1 > config.cutTime) break;
-        result.second.numTrackPairsWithinTimeCut++;
+        if (time2 - time1 > config.CutTime()) break;
+        result.second.numTrackPairsAfterTimeCut++;
 
         // Check PCA cuts
         auto [zVertex, dist] = CalcPCA(trackIter1->fParFirst, trackIter2->fParFirst);
-        if (zVertex > config.cutZ && dist < config.cutDist) {
-          double tVertex = 0.5 * (time1 + time2);
-          result.first.push_back(tVertex);
+        if (zVertex > config.CutZ()) {
+          result.second.numTrackPairsAfterZCut++;
+          if (dist < config.CutDist()) {
+            result.second.numTrackPairsAfterDistCut++;
+            double tVertex = 0.5 * (time1 + time2);
+            result.first.push_back(tVertex);
+          }
         }
       }
     }
 
+    result.second.time = xpu::pop_timer();
+    L_(info) << "V0Trigger: tracks " << tracks.size() << ", track pairs " << result.second.numTrackPairs
+             << ", after time cut " << result.second.numTrackPairsAfterTimeCut << ", after z cut "
+             << result.second.numTrackPairsAfterZCut << ", after dist cut " << result.second.numTrackPairsAfterDistCut;
     return result;
   };
 
@@ -74,5 +89,12 @@ namespace cbm::algo
     return std::make_pair(z, dist);
   }
 
+  std::string V0Trigger::ToString() const
+  {
+    std::stringstream out;
+    out << "--- Using V0Trigger ---";
+    return out.str();
+  }
+
 
-}  // namespace cbm::algo
+}  // namespace cbm::algo::evbuild
diff --git a/algo/trigger/V0Trigger.h b/algo/trigger/V0Trigger.h
index d35884b4cc..38b4b3c46f 100644
--- a/algo/trigger/V0Trigger.h
+++ b/algo/trigger/V0Trigger.h
@@ -6,21 +6,15 @@
 
 #include "CaTrack.h"
 #include "CaVector.h"
+#include "V0TriggerConfig.h"
 
 #include <utility>
 #include <vector>
 
-namespace cbm::algo
-{
+#include <xpu/host.h>
 
-  /** @struct V0TriggerConfig
-   ** @brief Configuration (cut values) for the V0Trigger class
-   **/
-  struct V0TriggerConfig {
-    double cutTime{};  /// Maximum time difference of tracks
-    double cutZ{};     /// Minimum z position at closest approach
-    double cutDist{};  /// Maximum distance at closest approach
-  };
+namespace cbm::algo::evbuild
+{
 
 
   /** @struct V0TriggerMoniData
@@ -29,7 +23,10 @@ namespace cbm::algo
   struct V0TriggerMoniData {
     size_t errTracksUnsorted{0};
     size_t numTrackPairs{0};
-    size_t numTrackPairsWithinTimeCut{0};
+    size_t numTrackPairsAfterTimeCut{0};
+    size_t numTrackPairsAfterZCut{0};
+    size_t numTrackPairsAfterDistCut{0};
+    xpu::timings time;  ///< Time for trigger building
   };
 
   /** @class V0Trigger
@@ -60,6 +57,9 @@ namespace cbm::algo
      **/
     Result operator()(const TrackVector& tracks, const V0TriggerConfig& config) const;
 
+    /** @brief Info to string **/
+    std::string ToString() const;
+
 
    private:
     /** @brief Calculation of closest approach of two tracks (straight lines)
@@ -74,4 +74,4 @@ namespace cbm::algo
     std::pair<double, double> CalcPCA(const TrackParam& track1, const TrackParam& track2) const;
   };
 
-}  // namespace cbm::algo
+}  // namespace cbm::algo::evbuild
diff --git a/algo/trigger/V0TriggerConfig.cxx b/algo/trigger/V0TriggerConfig.cxx
new file mode 100644
index 0000000000..566565a842
--- /dev/null
+++ b/algo/trigger/V0TriggerConfig.cxx
@@ -0,0 +1,48 @@
+/* Copyright (C) 2024 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Volker Friese [committer] */
+
+#include "V0TriggerConfig.h"
+
+
+namespace cbm::algo::evbuild
+{
+
+  // -----   Constructor from YAML   ------------------------------------------
+  V0TriggerConfig::V0TriggerConfig(YAML::Node config)
+  {
+    if (!config) {
+      fIsSet = false;
+      return;
+    }
+
+    auto cutTime = config["max_time_diff"];
+    if (!cutTime) throw std::runtime_error("time difference cut is not specified");
+    fCutTime = cutTime.as<double>();
+
+    auto cutZ = config["min_z_pca"];
+    if (!cutZ) throw std::runtime_error("PCA position cut is not specified");
+    fCutZ = cutZ.as<double>();
+
+    auto cutDist = config["max_dist_at_pca"];
+    if (!cutDist) throw std::runtime_error("track distance cut is not specified");
+    fCutDist = cutDist.as<size_t>();
+
+    fIsSet = true;
+  }
+  // --------------------------------------------------------------------------
+
+
+  // -----   Save to YAML   ---------------------------------------------------
+  YAML::Node V0TriggerConfig::ToYaml() const
+  {
+    YAML::Node result;
+    result["max_time_diff"]   = fCutTime;
+    result["min_z_pca"]       = fCutZ;
+    result["max_dist_at_pca"] = fCutDist;
+    return result;
+  }
+  // --------------------------------------------------------------------------
+
+
+}  // namespace cbm::algo::evbuild
diff --git a/algo/trigger/V0TriggerConfig.h b/algo/trigger/V0TriggerConfig.h
new file mode 100644
index 0000000000..b54535b1e8
--- /dev/null
+++ b/algo/trigger/V0TriggerConfig.h
@@ -0,0 +1,71 @@
+/* Copyright (C) 2024 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Volker Friese [committer] */
+
+#pragma once
+
+#include "CbmDefs.h"
+
+#include <map>
+
+#include <yaml-cpp/yaml.h>
+
+
+namespace cbm::algo::evbuild
+{
+
+  /** @class V0TriggerConfig
+   ** @brief Configuration of the V0 trigger class (trigger on displaced vertices)
+   ** @author Volker Friese <v.friese@gsi.de>
+   ** @since 19.04.2024
+   **/
+  class V0TriggerConfig {
+
+   public:
+    /** @brief Default constructor **/
+    V0TriggerConfig(){};
+
+    /** @brief Constructor with parameters
+     ** @param cutTime  Maximum time difference of tracks [ns]
+     ** @param cutZ     Minimum z position at closest approach
+     ** @param cutDist  Maximum distance at closest approach
+     **/
+    V0TriggerConfig(double cutTime, double cutZ, double cutDist)
+      : fCutTime(cutTime)
+      , fCutZ(cutZ)
+      , fCutDist(cutDist)
+      , fIsSet(true)
+    {
+    }
+
+    /** @brief Constructor from YAML **/
+    V0TriggerConfig(YAML::Node config);
+
+    /** @brief Destructor **/
+    ~V0TriggerConfig() = default;
+
+    /** @brief Cut on time difference between tracks **/
+    double CutTime() const { return fCutTime; }
+
+    /** @brief Cut on z position of closest approach **/
+    double CutZ() const { return fCutZ; }
+
+    /** @brief Cut on distance of tracks at closest approach **/
+    double CutDist() const { return fCutDist; }
+
+    /** @brief Check whether config was set **/
+    bool IsSet() const { return fIsSet; }
+
+    /** @brief Save to YAML **/
+    YAML::Node ToYaml() const;
+
+
+   private:
+    double fCutTime{0.};  /// Maximum time difference of tracks
+    double fCutZ{0.};     /// Minimum z position at closest approach
+    double fCutDist{0.};  /// Maximum distance at closest approach
+    bool fIsSet{false};   /// Flag whether a configuration was set
+  };
+
+
+}  // namespace cbm::algo::evbuild
diff --git a/reco/tasks/CbmReco.cxx b/reco/tasks/CbmReco.cxx
index a08eb468b1..8d198b7c6e 100644
--- a/reco/tasks/CbmReco.cxx
+++ b/reco/tasks/CbmReco.cxx
@@ -10,6 +10,8 @@
 #include "CbmTaskTriggerDigi.h"
 #include "CbmTaskUnpack.h"
 #include "CbmTsEventHeader.h"
+#include "DigiEventSelector.h"
+#include "DigiEventSelectorConfig.h"
 
 #include <FairFileSource.h>
 #include <FairRootFileSink.h>
@@ -25,9 +27,6 @@
 
 #include <yaml-cpp/yaml.h>
 
-#include "DigiEventSelector.h"
-#include "DigiEventSelectorConfig.h"
-
 using std::make_unique;
 using std::string;
 
@@ -36,7 +35,7 @@ using std::string;
 CbmReco::CbmReco(Config config, string source, string outFile, string configFile, int32_t numTs, uint16_t port,
                  cbm::Monitor* monitor)
   : fConfig(config)
-  , fSourceNames {source}
+  , fSourceNames{source}
   , fOutputFileName(outFile)
   , fConfigFileName(configFile)
   , fNumTs(numTs)
@@ -102,7 +101,8 @@ int32_t CbmReco::Run()
   }
   else {
     auto source = make_unique<CbmSourceTs>(fSourceNames);
-    if (source) LOG(info) << "Reco: Using sources " << ListSources();
+    if (source)
+      LOG(info) << "Reco: Using sources " << ListSources();
     else {
       LOG(error) << "Reco: Could not open sources " << ListSources() << "; aborting.";
       return -1;
@@ -112,7 +112,8 @@ int32_t CbmReco::Run()
 
   // --- Output file
   auto sink = make_unique<FairRootFileSink>(fOutputFileName);
-  if (sink) LOG(info) << "Reco: Using output file " << fOutputFileName;
+  if (sink)
+    LOG(info) << "Reco: Using output file " << fOutputFileName;
   else {
     LOG(error) << "Reco: Could not open output " << fOutputFileName << "; aborting.";
     return -1;
@@ -138,8 +139,8 @@ int32_t CbmReco::Run()
 
   // --- Digi trigger
   auto trigger = make_unique<CbmTaskTriggerDigi>();
-  trigger->AddSystem(evbuildConfig.fTrigger.Detector());
-  trigger->SetConfig(evbuildConfig.fTrigger);
+  trigger->AddSystem(evbuildConfig.fDigiTrigger.Detector());
+  trigger->SetConfig(evbuildConfig.fDigiTrigger);
   trigger->SetOutputBranchPersistent("Trigger", false);
   run.AddTask(trigger.release());
 
-- 
GitLab