From 2bcccc69579ab559654a99829771be6f1e2aafd2 Mon Sep 17 00:00:00 2001
From: "s.zharko@gsi.de" <s.zharko@gsi.de>
Date: Tue, 29 Oct 2024 14:19:01 +0100
Subject: [PATCH] KF: adding the material budget maps cacheing option to the
 KF-setup builder class

---
 algo/kf/core/geo/KfSetupBuilder.cxx      | 69 +++++++++++++++++++++---
 algo/kf/core/geo/KfSetupBuilder.h        | 19 ++++++-
 algo/kf/core/geo/KfTarget.cxx            | 11 ++++
 algo/kf/core/geo/KfTarget.h              |  4 ++
 macro/mcbm/mcbm_qa.C                     |  1 +
 macro/mcbm/mcbm_reco_event.C             |  1 +
 reco/kfnew/CbmKfTrackingSetupBuilder.cxx | 12 ++++-
 reco/kfnew/CbmKfTrackingSetupBuilder.h   | 10 ++++
 8 files changed, 119 insertions(+), 8 deletions(-)

diff --git a/algo/kf/core/geo/KfSetupBuilder.cxx b/algo/kf/core/geo/KfSetupBuilder.cxx
index 78767a5a34..0d88a20b1c 100644
--- a/algo/kf/core/geo/KfSetupBuilder.cxx
+++ b/algo/kf/core/geo/KfSetupBuilder.cxx
@@ -41,8 +41,13 @@ void SetupBuilder::Init()
     }
   }
 
+  bool bMaterialLoaded{false};
+  if (!fsMaterialCacheFile.empty()) {
+    bMaterialLoaded = this->LoadMaterial();
+  }
+
   // Add material to the target:
-  {
+  if (!bMaterialLoaded) {
     // Material budget
     double zMin{fTarget.GetZ() + kTargetCenterOffset};
     double zMax{fTarget.GetZ() + fTarget.GetDz() * kTargetMaterialOffset};
@@ -61,7 +66,9 @@ void SetupBuilder::Init()
       acceptanceSlope = std::max(acceptanceSlope, tCurr);
     }
 
-    fvMaterial.reserve(fGeoLayers.size());
+    if (!bMaterialLoaded) {
+      fvMaterial.reserve(fGeoLayers.size());
+    }
     double zLast{fTarget.GetZ() + fTarget.GetDz() * kTargetMaterialOffset};
     for (auto layerIt = fGeoLayers.cbegin(); layerIt != fGeoLayers.cend(); ++layerIt) {
       double z1 = layerIt->fZmax;
@@ -71,17 +78,25 @@ void SetupBuilder::Init()
       }
       double zNew{0.5 * (z1 + z2)};
       double xyMax{acceptanceSlope * (layerIt->fZref - fTarget.GetZ())};
-      auto material{fpMaterialMapFactory->GenerateMaterialMap(layerIt->fZref, zLast, zNew, xyMax, fMatMapNofBins)};
-      fvMaterial.push_back(std::move(material));
+      if (!bMaterialLoaded) {
+        auto material{fpMaterialMapFactory->GenerateMaterialMap(layerIt->fZref, zLast, zNew, xyMax, fMatMapNofBins)};
+        fvMaterial.push_back(std::move(material));
+      }
+
       // Note: square material maps to follow the material budget map regions
       fFieldFactory.AddSliceReference(xyMax, xyMax, layerIt->fZref);
       fModuleIndexFactory.AddComponent<int>(layerIt->fDetID, layerIt->fLocID, layerIt->fZref);
       zLast = zNew;
     }
   }
+
+  if (!bMaterialLoaded && !fsMaterialCacheFile.empty()) {
+    this->StoreMaterial();
+  }
+
   fbIfGeoLayersInit = true;
   fbReady           = true;
-  LOG(info) << "kf::SetupBuilder initilization: done";
+  LOG(info) << "kf::SetupBuilder initialization: done";
 }
 
 // ---------------------------------------------------------------------------------------------------------------------
@@ -98,6 +113,33 @@ std::string SetupBuilder::InitStatusMsg() const
   return msg.str();
 }
 
+// ---------------------------------------------------------------------------------------------------------------------
+//
+bool SetupBuilder::LoadMaterial()
+{
+  std::ifstream ifs(fsMaterialCacheFile, std::ios::binary);
+  if (!ifs) {  // File does not exist yet
+    return false;
+  }
+  try {
+    boost::archive::binary_iarchive ia(ifs);
+    MaterialMap targetMat;
+    ia >> targetMat;
+    ia >> fvMaterial;
+    fTarget.SetMaterial(targetMat);
+    // TODO: Provide consistency check (geometry hash)
+  }
+  catch (const std::exception& err) {
+    LOG(warn) << "kf::SetupBuilder::LoadMaterial: input file \"" << fsMaterialCacheFile
+              << "\" has inconsistent format "
+                 "or was corrupted. The material maps will be generated";
+    return false;
+  }
+  LOG(info) << "kf::SetupBuilder::LoadMaterial: the material maps were loaded from cache file \"" << fsMaterialCacheFile
+            << "\"";
+  return true;
+}
+
 // ---------------------------------------------------------------------------------------------------------------------
 //
 void SetupBuilder::Reset()
@@ -135,9 +177,24 @@ void SetupBuilder::Store(const Setup<double>& setup, const std::string& fileName
   std::ofstream ofs(fileName, std::ios::binary);
   if (!ofs) {
     std::stringstream msg;
-    msg << "kf::SetupBuilder::Store: failed openning file \"" << fileName << "\" to store the setup";
+    msg << "kf::SetupBuilder::Store: failed opening file \"" << fileName << "\" to store the setup";
     throw std::runtime_error(msg.str());
   }
   boost::archive::binary_oarchive oa(ofs);
   oa << setup;
 }
+
+// ---------------------------------------------------------------------------------------------------------------------
+//
+void SetupBuilder::StoreMaterial() const
+{
+  std::ofstream ofs(fsMaterialCacheFile, std::ios::binary);
+  if (!ofs) {
+    std::stringstream msg;
+    msg << "kf::SetupBuilder::Store: failed opening cache file \"" << fsMaterialCacheFile << "\" to store the setup";
+    throw std::runtime_error(msg.str());
+  }
+  boost::archive::binary_oarchive oa(ofs);
+  oa << fTarget.GetMaterial();
+  oa << fvMaterial;
+}
diff --git a/algo/kf/core/geo/KfSetupBuilder.h b/algo/kf/core/geo/KfSetupBuilder.h
index 4e360a05e8..649f64e279 100644
--- a/algo/kf/core/geo/KfSetupBuilder.h
+++ b/algo/kf/core/geo/KfSetupBuilder.h
@@ -120,6 +120,14 @@ namespace cbm::algo::kf
       fpMaterialMapFactory = pMaterialFactory;
     }
 
+    /// \brief Sets the material budget cache file name
+    /// \param filename  Material budget cache file name
+    ///
+    /// If provided, the instance will try to read the material budget maps from the file. If the file does not exist,
+    /// or the geometry hash was changed since the last time, the material budget maps will be recreated on the flight
+    /// and stored to the file.
+    void SetMaterialCacheFile(const std::string& filename) { fsMaterialCacheFile = filename; }
+
     /// \brief Sets target initialization properties
     /// \param x  Target x-coordinate [cm]
     /// \param y  Target y-coordinate [cm]
@@ -149,7 +157,7 @@ namespace cbm::algo::kf
     static Setup<T> Load(const std::string& fileName);
 
    private:
-    /// \brief  Initializes, validates and cashes the parameters
+    /// \brief  Initializes, validates and caches the parameters
     /// \throw  std::runtime_error If pre-initialization was incomplete
     /// \note   Does not touch the field function and field type/mode
     void Init();
@@ -157,6 +165,14 @@ namespace cbm::algo::kf
     /// \brief Prints initialization status message
     std::string InitStatusMsg() const;
 
+    /// \brief Reads material from file
+    /// \return  true   Material budget was read from file
+    /// \return  false  Material budget could not read (file does not exist or has incorrect properties)
+    bool LoadMaterial();
+
+    /// \brief Stores material to file
+    void StoreMaterial() const;
+
     // TODO: Define target material more precisely
     static constexpr double kTargetCenterOffset{0.05};                 // Offset from target center [cm]
     static constexpr double kTargetMaterialOffset{2.};                 // Offset of the target material [in dz]
@@ -165,6 +181,7 @@ namespace cbm::algo::kf
     static constexpr int kTargetMaterialMapNofBins{20};
 
     std::set<GeoLayer<int>> fGeoLayers{};                                ///< Set of geo layers
+    std::string fsMaterialCacheFile{""};                                 ///< A cache file for the material
     std::vector<MaterialMap> fvMaterial{};                               ///< Material map container
     std::shared_ptr<IMaterialMapFactory> fpMaterialMapFactory{nullptr};  ///< Material map creator
     ModuleIndexMapFactory fModuleIndexFactory;                           ///< Module index factory
diff --git a/algo/kf/core/geo/KfTarget.cxx b/algo/kf/core/geo/KfTarget.cxx
index bc8b00a188..1552f28d1d 100644
--- a/algo/kf/core/geo/KfTarget.cxx
+++ b/algo/kf/core/geo/KfTarget.cxx
@@ -37,6 +37,17 @@ void Target<T>::SetMaterial(const MaterialMap& material)
   fMaterial = material;
 }
 
+// ---------------------------------------------------------------------------------------------------------------------
+//
+template<typename T>
+void Target<T>::SetMaterial(MaterialMap&& material)
+{
+  if (material.IsUndefined()) {
+    throw std::logic_error("Target:ReceiveMaterial(): attempt to pass an undefined instance of the material map");
+  }
+  fMaterial = std::move(material);
+}
+
 // ---------------------------------------------------------------------------------------------------------------------
 //
 template<typename T>
diff --git a/algo/kf/core/geo/KfTarget.h b/algo/kf/core/geo/KfTarget.h
index e901c6c58f..000828896f 100644
--- a/algo/kf/core/geo/KfTarget.h
+++ b/algo/kf/core/geo/KfTarget.h
@@ -79,6 +79,10 @@ namespace cbm::algo::kf
     /// \param material  Material map
     void SetMaterial(const MaterialMap& material);
 
+    /// \brief Sets material map (move semantics)
+    /// \param material  Material map
+    void SetMaterial(MaterialMap&& material);
+
     /// \brief Sets x-coordinate of the nominal target center
     /// \param x  x-coordinate [cm]
     void SetX(const T& x) { fX = x; }
diff --git a/macro/mcbm/mcbm_qa.C b/macro/mcbm/mcbm_qa.C
index 8f41a3c635..5310cba1e0 100644
--- a/macro/mcbm/mcbm_qa.C
+++ b/macro/mcbm/mcbm_qa.C
@@ -135,6 +135,7 @@ void mcbm_qa(Int_t nEvents = 0,
   gROOT->LoadMacro(setupFile);
   gROOT->ProcessLine(setupFunct);
   CbmSetup* setup = CbmSetup::Instance();
+  setup->GetProvider()->GetSetup().SetTag(setupName.Data());
   //  setup->RemoveModule(ECbmModuleId::kTrd);
   // ------------------------------------------------------------------------
 
diff --git a/macro/mcbm/mcbm_reco_event.C b/macro/mcbm/mcbm_reco_event.C
index 157b7a3580..7ece135982 100644
--- a/macro/mcbm/mcbm_reco_event.C
+++ b/macro/mcbm/mcbm_reco_event.C
@@ -55,6 +55,7 @@ void mcbm_reco_event(Int_t nEvents = 10, TString dataset = "data/test",
   gROOT->LoadMacro(setupFile);
   gROOT->ProcessLine(setupFunct);
   CbmSetup* setup = CbmSetup::Instance();
+  setup->GetProvider()->GetSetup().SetTag(setupName);
   //  setup->RemoveModule(ECbmModuleId::kTrd);
   // ------------------------------------------------------------------------
 
diff --git a/reco/kfnew/CbmKfTrackingSetupBuilder.cxx b/reco/kfnew/CbmKfTrackingSetupBuilder.cxx
index 1b4a0d62d6..07c29edc06 100644
--- a/reco/kfnew/CbmKfTrackingSetupBuilder.cxx
+++ b/reco/kfnew/CbmKfTrackingSetupBuilder.cxx
@@ -144,7 +144,17 @@ TrackingSetupBuilder* TrackingSetupBuilder::Instance()
   if (fpInstance == nullptr) {
     std::lock_guard<std::mutex> lock(fMutex);
     fpInstance = new TrackingSetupBuilder{};
-    fpInstance->Init();  // Init for the first time?
+    fpInstance->CheckDetectorPresence();
+
+    auto setupTag = CbmSetup::Instance()->GetProvider()->GetSetup().GetTag();
+    if (setupTag.empty()) {
+      throw std::logic_error("The setup tag in CbmSetup is not defined");
+    }
+    //TString sinkName = FairRootManager::Instance()->GetSink()->GetFileName();
+
+    // Creating the cache file in "./", so the macros from different directories could use the same file
+    std::string sCacheFile = Form("./%s.mat.kf.bin", setupTag.c_str());
+    fpInstance->SetMaterialCacheFile(sCacheFile);
   }
   return fpInstance;
 }
diff --git a/reco/kfnew/CbmKfTrackingSetupBuilder.h b/reco/kfnew/CbmKfTrackingSetupBuilder.h
index 00ab71c5a5..a3d2ff0cec 100644
--- a/reco/kfnew/CbmKfTrackingSetupBuilder.h
+++ b/reco/kfnew/CbmKfTrackingSetupBuilder.h
@@ -12,6 +12,7 @@
 #include "CbmDefs.h"
 #include "CbmEnumArray.h"
 #include "KfSetupBuilder.h"
+#include "TString.h"
 
 #include <mutex>
 #include <tuple>
@@ -50,6 +51,14 @@ namespace cbm::kf
     TrackingSetupBuilder& operator=(const TrackingSetupBuilder&) = delete;
     TrackingSetupBuilder& operator=(TrackingSetupBuilder&&) = delete;
 
+    /// \brief  Sets a name for the material budget cache-file
+    /// \param  filename  A name of the file
+    ///
+    /// If provided, the instance will try to read the material budget maps from the file. If the file does not exist,
+    /// or the geometry hash was changed since the last time, the material budget maps will be recreated on the flight
+    /// and stored to the file.
+    void SetMaterialCacheFile(const TString& filename) { fBuilder.SetMaterialCacheFile(filename.Data()); }
+
    private:
     template<typename T>
     using DetectorIDArray_t = cbm::core::EnumArray<cbm::algo::ca::EDetectorID, T>;
@@ -68,6 +77,7 @@ namespace cbm::kf
     void CheckDetectorPresence();
 
     /// \brief  Initializes the instance
+    /// \note   Is executed on the first call of MakeSetup function
     void Init();
 
     // Material map creator properties (TODO: Provide setters, if needed)
-- 
GitLab