From 4b459cc0f3b6962ad868313d15454019f840b8b0 Mon Sep 17 00:00:00 2001
From: Lukas Chlad <l.chlad@gsi.de>
Date: Wed, 9 Aug 2023 11:43:48 +0000
Subject: [PATCH] FSD integration into SIM

---
 core/data/CbmDataLinkDef.h                    |   3 +-
 core/data/fsd/CbmFsdHit.cxx                   |  11 +-
 core/data/fsd/CbmFsdHit.h                     |  12 +-
 core/data/fsd/CbmFsdPoint.cxx                 |   3 +-
 core/data/fsd/CbmFsdPoint.h                   |  15 +-
 core/detectors/fsd/CMakeLists.txt             |  23 +-
 core/detectors/fsd/CbmFsdBaseLinkDef.h        |   3 +-
 core/detectors/fsd/CbmFsdContFact.cxx         |  15 +-
 .../fsd/CbmFsdDetectorMapManager.cxx          | 111 -------
 core/detectors/fsd/CbmFsdDetectorMapManager.h |  64 ----
 ...sdDetectorData.h => CbmFsdDetectorSpecs.h} |  23 +-
 core/detectors/fsd/CbmFsdDigiPar.cxx          |  79 +++++
 core/detectors/fsd/CbmFsdDigiPar.h            |  47 +++
 core/detectors/fsd/CbmFsdGeoHandler.cxx       | 173 +++++++++++
 core/detectors/fsd/CbmFsdGeoHandler.h         |  88 ++++++
 sim/detectors/CMakeLists.txt                  |   1 +
 sim/detectors/fsd/CMakeLists.txt              |  30 ++
 sim/detectors/fsd/CbmFsdDigitize.cxx          | 286 ++++++++++++++++++
 sim/detectors/fsd/CbmFsdDigitize.h            |  99 ++++++
 sim/detectors/fsd/CbmFsdMC.cxx                | 135 +++++++++
 sim/detectors/fsd/CbmFsdMC.h                  | 165 ++++++++++
 sim/detectors/fsd/CbmFsdSimLinkDef.h          |  15 +
 sim/response/CMakeLists.txt                   |   1 +
 sim/response/base/CbmDigitization.cxx         |  12 +
 sim/transport/base/CbmMCEventFilter.cxx       |   3 +
 sim/transport/base/CbmStack.cxx               |   2 +-
 sim/transport/geosetup/CMakeLists.txt         |   1 +
 .../geosetup/CbmGeoSetupDbProvider.cxx        |   4 +-
 .../geosetup/CbmGeoSetupProvider.cxx          |  10 +-
 .../geosetup/CbmGeoSetupRepoProvider.cxx      |   1 +
 sim/transport/steer/CbmTransportConfig.cxx    |   1 +
 31 files changed, 1212 insertions(+), 224 deletions(-)
 delete mode 100644 core/detectors/fsd/CbmFsdDetectorMapManager.cxx
 delete mode 100644 core/detectors/fsd/CbmFsdDetectorMapManager.h
 rename core/detectors/fsd/{CbmFsdDetectorData.h => CbmFsdDetectorSpecs.h} (50%)
 create mode 100644 core/detectors/fsd/CbmFsdDigiPar.cxx
 create mode 100644 core/detectors/fsd/CbmFsdDigiPar.h
 create mode 100644 core/detectors/fsd/CbmFsdGeoHandler.cxx
 create mode 100644 core/detectors/fsd/CbmFsdGeoHandler.h
 create mode 100644 sim/detectors/fsd/CMakeLists.txt
 create mode 100644 sim/detectors/fsd/CbmFsdDigitize.cxx
 create mode 100644 sim/detectors/fsd/CbmFsdDigitize.h
 create mode 100644 sim/detectors/fsd/CbmFsdMC.cxx
 create mode 100644 sim/detectors/fsd/CbmFsdMC.h
 create mode 100644 sim/detectors/fsd/CbmFsdSimLinkDef.h

diff --git a/core/data/CbmDataLinkDef.h b/core/data/CbmDataLinkDef.h
index 4c0a08a60d..68d3650dc5 100644
--- a/core/data/CbmDataLinkDef.h
+++ b/core/data/CbmDataLinkDef.h
@@ -103,7 +103,8 @@
 #pragma link C++ class CbmFsdDigiData + ;
 #pragma link C++ class CbmFsdHit + ;
 #pragma link C++ class CbmFsdPoint + ;
-#pragma link C++ class CbmFsdAddress + ;
+#pragma link C++ namespace CbmFsdAddress;
+#pragma link C++ enum CbmFsdAddress::Level;
 
 // --- data/global
 #pragma link C++ class CbmGlobalTrack + ;
diff --git a/core/data/fsd/CbmFsdHit.cxx b/core/data/fsd/CbmFsdHit.cxx
index 1f6082ec61..dc814a2de6 100644
--- a/core/data/fsd/CbmFsdHit.cxx
+++ b/core/data/fsd/CbmFsdHit.cxx
@@ -14,12 +14,16 @@
 #include <Logger.h>  // for Logger, LOG
 
 // -----   Default constructor   -------------------------------------------
-CbmFsdHit::CbmFsdHit() : CbmPixelHit(), fModuleID(-1), fEdep(-1)
+CbmFsdHit::CbmFsdHit() : CbmPixelHit(), fUnitId(-1), fModuleId(-1), fEdep(-1)
 {
   SetType(kFSDHIT);
   SetTime(0.);
 }
-CbmFsdHit::CbmFsdHit(int32_t module, double edep) : CbmPixelHit(), fModuleID(module), fEdep(edep)
+CbmFsdHit::CbmFsdHit(int32_t unit, int32_t module, double edep)
+  : CbmPixelHit()
+  , fUnitId(unit)
+  , fModuleId(module)
+  , fEdep(edep)
 {
   SetType(kFSDHIT);
   SetTime(0.);
@@ -35,7 +39,8 @@ void CbmFsdHit::Print(Option_t*) const { LOG(info) << ToString(); }
 std::string CbmFsdHit::ToString() const
 {
   std::stringstream ss;
-  ss << "module : " << fModuleID << "position: [" << GetX() << "," << GetY() << "," << GetZ() << "] "
+  ss << "unit : " << fUnitId << "module : " << fModuleId << "position: [" << GetX() << "," << GetY() << "," << GetZ()
+     << "] "
      << " ELoss " << fEdep;
   return ss.str();
 }
diff --git a/core/data/fsd/CbmFsdHit.h b/core/data/fsd/CbmFsdHit.h
index 8989a540c9..d833bfa800 100644
--- a/core/data/fsd/CbmFsdHit.h
+++ b/core/data/fsd/CbmFsdHit.h
@@ -28,7 +28,7 @@ public:
   /**   Default constructor   **/
   CbmFsdHit();
 
-  CbmFsdHit(int32_t module, double edep);
+  CbmFsdHit(int32_t unit, int32_t module, double edep);
 
 
   /**   Destructor   **/
@@ -40,8 +40,11 @@ public:
   double GetEdep() const { return fEdep; }
   void SetEdep(double edep) { fEdep = edep; }
 
-  int32_t GetModuleID() const { return fModuleID; }
-  void SetModuleID(int32_t mod) { fModuleID = mod; }
+  int32_t GetModuleId() const { return fModuleId; }
+  void SetModuleId(int32_t mod) { fModuleId = mod; }
+
+  int32_t GetUnitId() const { return fUnitId; }
+  void SetUnitId(int32_t unit) { fUnitId = unit; }
 
   void Print(Option_t* = "") const;
 
@@ -50,7 +53,8 @@ public:
 private:
   /**   Data members  **/
 
-  int32_t fModuleID;
+  int32_t fModuleId;
+  int32_t fUnitId;
   double fEdep;
 
 
diff --git a/core/data/fsd/CbmFsdPoint.cxx b/core/data/fsd/CbmFsdPoint.cxx
index b200a6de42..baddb48790 100644
--- a/core/data/fsd/CbmFsdPoint.cxx
+++ b/core/data/fsd/CbmFsdPoint.cxx
@@ -10,7 +10,7 @@
 #include <sstream>  // for stringstream
 
 // -----   Default constructor   -------------------------------------------
-CbmFsdPoint::CbmFsdPoint() : FairMCPoint(), fModuleID(0) {}
+CbmFsdPoint::CbmFsdPoint() : FairMCPoint() {}
 // -------------------------------------------------------------------------
 
 
@@ -18,7 +18,6 @@ CbmFsdPoint::CbmFsdPoint() : FairMCPoint(), fModuleID(0) {}
 CbmFsdPoint::CbmFsdPoint(int32_t trackID, int32_t detID, TVector3 pos, TVector3 mom, double tof, double length,
                          double eLoss)
   : FairMCPoint(trackID, detID, pos, mom, tof, length, eLoss)
-  , fModuleID(0)
 {
 }
 // -------------------------------------------------------------------------
diff --git a/core/data/fsd/CbmFsdPoint.h b/core/data/fsd/CbmFsdPoint.h
index 329c2bb9c1..764ab7442d 100644
--- a/core/data/fsd/CbmFsdPoint.h
+++ b/core/data/fsd/CbmFsdPoint.h
@@ -30,7 +30,7 @@ public:
   /** Constructor with arguments
    *@param trackID  Index of MCTrack
    *@param detID    Detector ID
-   *@param pos      Ccoordinates at entrance to active volume [cm]
+   *@param pos      Coordinates at entrance to active volume [cm]
    *@param mom      Momentum of track at entrance [GeV]
    *@param tof      Time since event start [ns]
    *@param length   Track length since creation [cm]
@@ -39,10 +39,6 @@ public:
   CbmFsdPoint(int32_t trackID, int32_t detID, TVector3 pos, TVector3 mom, double tof, double length, double eLoss);
 
 
-  /** Copy constructor **/
-  //  CbmFsdPoint(const CbmFsdPoint& point) { *this = point; };
-
-
   /** Destructor **/
   virtual ~CbmFsdPoint();
 
@@ -50,17 +46,8 @@ public:
   /** Output to screen **/
   virtual void Print(const Option_t* opt) const;
 
-  /** Modifiers **/
-  void SetModuleID(int32_t mod) { fModuleID = mod; }
-  /** Accessors **/
-  int32_t GetModuleID() const { return fModuleID; }
-
   std::string ToString() const;
 
-private:
-  int32_t fModuleID;  //number of module
-
-
   ClassDef(CbmFsdPoint, 1)
 };
 
diff --git a/core/detectors/fsd/CMakeLists.txt b/core/detectors/fsd/CMakeLists.txt
index e3582ed7bc..3781292489 100644
--- a/core/detectors/fsd/CMakeLists.txt
+++ b/core/detectors/fsd/CMakeLists.txt
@@ -4,24 +4,27 @@ set(INCLUDE_DIRECTORIES
 
 set(SRCS
   CbmFsdContFact.cxx
-  CbmFsdDetectorMapManager.cxx
+  CbmFsdGeoHandler.cxx
+  CbmFsdDigiPar.cxx
   )
 
 set(LIBRARY_NAME CbmFsdBase)
 set(LINKDEF ${LIBRARY_NAME}LinkDef.h)
 set(PUBLIC_DEPENDENCIES 
+  ROOT::Core
   FairRoot::ParBase
   )
 
-set(PRIVATE_DEPENDENCIES 
-  FairLogger::FairLogger 
-  ROOT::Core 
+set(PRIVATE_DEPENDENCIES  
+  CbmData
+  FairLogger::FairLogger
+  ROOT::Geom
+  ${VMCLIB}
   )
 
-generate_cbm_library()
-
 # Install file which has no corresponding source file
-install(FILES
-        CbmFsdDetectorData.h
-        DESTINATION include
-       )
+set(HEADERS
+  CbmFsdDetectorSpecs.h
+  )
+
+generate_cbm_library()
diff --git a/core/detectors/fsd/CbmFsdBaseLinkDef.h b/core/detectors/fsd/CbmFsdBaseLinkDef.h
index 26be7730a4..9bb21eb15a 100644
--- a/core/detectors/fsd/CbmFsdBaseLinkDef.h
+++ b/core/detectors/fsd/CbmFsdBaseLinkDef.h
@@ -11,6 +11,7 @@
 #pragma link off all functions;
 
 #pragma link C++ class CbmFsdContFact + ;
-#pragma link C++ class CbmFsdDetectorMapManager + ;
+#pragma link C++ class CbmFsdGeoHandler + ;
+#pragma link C++ class CbmFsdDigiPar + ;
 
 #endif
diff --git a/core/detectors/fsd/CbmFsdContFact.cxx b/core/detectors/fsd/CbmFsdContFact.cxx
index 550b8ebcf2..e685b4a818 100644
--- a/core/detectors/fsd/CbmFsdContFact.cxx
+++ b/core/detectors/fsd/CbmFsdContFact.cxx
@@ -13,6 +13,8 @@
  */
 #include "CbmFsdContFact.h"
 
+#include "CbmFsdDigiPar.h"
+
 #include <FairContFact.h>   // for FairContainer
 #include <FairRuntimeDb.h>  // for FairRuntimeDb
 #include <Logger.h>         // for LOG
@@ -39,6 +41,13 @@ void CbmFsdContFact::setAllContainers()
 {
   /** Creates the Container objects with all accepted contexts and adds them to
    *  the list of containers for the FsdBase library.*/
+
+  FairContainer* p1 =
+    new FairContainer("CbmFsdDigiPar", "Digitization parameters for the FSD detector",
+                      "Needed parameters to adjust FsdDigitizer according to the geometry and read-out propetries");
+  p1->addContext("Needed parameters to adjust FsdDigitizer according to the geometry and read-out propetries");
+
+  containers->Add(p1);
 }
 
 FairParSet* CbmFsdContFact::createContainer(FairContainer* c)
@@ -48,7 +57,11 @@ FairParSet* CbmFsdContFact::createContainer(FairContainer* c)
    * of this container, the name is concatinated with the context. */
   const char* name = c->GetName();
   LOG(info) << " -I container name " << name;
-  FairParSet* p = 0;
+  FairParSet* p = nullptr;
+
+  if (strcmp(name, "CbmFsdDigiPar") == 0) {
+    p = new CbmFsdDigiPar(c->getConcatName().Data(), c->GetTitle(), c->getContext());
+  }
 
   return p;
 }
diff --git a/core/detectors/fsd/CbmFsdDetectorMapManager.cxx b/core/detectors/fsd/CbmFsdDetectorMapManager.cxx
deleted file mode 100644
index bfbed6a7e1..0000000000
--- a/core/detectors/fsd/CbmFsdDetectorMapManager.cxx
+++ /dev/null
@@ -1,111 +0,0 @@
-/* Copyright (C) 2023 Physikalisches Institut, Eberhard Karls Universitaet Tuebingen, Tuebingen
-   SPDX-License-Identifier: GPL-3.0-only
-   Authors: Lukas Chlad [committer] */
-
-#include "CbmFsdDetectorMapManager.h"
-
-#include "CbmFsdDetectorData.h"  // for CbmFsdModuleData
-
-#include <Logger.h>  // for LOG, Logger
-
-#include <TGeoBBox.h>     // for TGeoBBox
-#include <TGeoManager.h>  // for TGeoManager, gGeoManager
-#include <TGeoMatrix.h>   // for TGeoMatrix
-#include <TGeoNode.h>     // for TGeoIterator, TGeoNode
-#include <TGeoVolume.h>   // for TGeoVolume
-#include <TRandom.h>      // for TRandom, gRandom
-
-#include <string>   // for operator<, stoul
-#include <utility>  // for pair
-
-#include <stddef.h>  // for size_t
-
-using namespace std;
-
-CbmFsdDetectorMapManager::CbmFsdDetectorMapManager() : fModulePathToIdMap(), fModuleIdToDataMap(), fModuleIds()
-{
-  Init();
-}
-
-CbmFsdDetectorMapManager::~CbmFsdDetectorMapManager() {}
-
-void CbmFsdDetectorMapManager::Init()
-{
-
-  fModulePathToIdMap.clear();
-  fModuleIdToDataMap.clear();
-  fModuleIds.clear();
-
-  Int_t currentModuleId = 0;
-
-  TGeoIterator geoIterator(gGeoManager->GetTopNode()->GetVolume());
-
-  TGeoNode* curNode;
-  TString branchStr("fsd_");  // stored in path of nodes, fsd_geoTag_0
-  TString moduleStr("fsdmodule");
-  TString scintStr("scintillator");
-  geoIterator.Reset();  // safety to reset to "cave" befor the loop starts
-  while ((curNode = geoIterator())) {
-    TString nodePath;
-    geoIterator.GetPath(nodePath);
-    if (!nodePath.Contains(branchStr)) {
-      geoIterator.Skip();  // skip current branch when it is not FSD => should speed up
-      continue;            // don't do anything for this branch
-    }
-
-    TString nodeName(curNode->GetName());
-    // FSD detector -> all modules names contain "fsdmodule"
-    if (TString(curNode->GetVolume()->GetName()).Contains(moduleStr)) {
-
-      const TGeoMatrix* curMatrix = geoIterator.GetCurrentMatrix();
-      const Double_t* curNodeTr   = curMatrix->GetTranslation();
-      string path                 = string(nodePath.Data());
-
-      TGeoVolume* curScintillator = nullptr;
-      for (int idn = 0; idn < curNode->GetNdaughters(); idn++) {
-        TGeoNode* daughterNode = curNode->GetDaughter(idn);
-        if (TString(daughterNode->GetVolume()->GetName()).Contains(scintStr)) {
-          curScintillator = daughterNode->GetVolume();
-          break;
-        }
-      }
-      const TGeoBBox* shape = (const TGeoBBox*) (curScintillator->GetShape());
-
-      fModulePathToIdMap.insert(pair<string, Int_t>(path, currentModuleId));
-      CbmFsdModuleData* moduleData = new CbmFsdModuleData();
-      moduleData->fX               = curNodeTr[0];
-      moduleData->fY               = curNodeTr[1];
-      moduleData->fZ               = curNodeTr[2];
-      moduleData->dX               = shape->GetDX();
-      moduleData->dY               = shape->GetDY();
-      moduleData->dZ               = shape->GetDZ();
-      moduleData->fId              = currentModuleId;
-      fModuleIdToDataMap.insert(pair<Int_t, CbmFsdModuleData*>(moduleData->fId, moduleData));
-      fModuleIds.push_back(currentModuleId);
-      currentModuleId++;
-    }
-  }
-
-  LOG(info) << "CbmFsdDetectorMapManager is initialized";
-  LOG(info) << "fModulePathToIdMap.size() = " << fModulePathToIdMap.size();
-  LOG(info) << "fModuleIdToDataMap.size() = " << fModuleIdToDataMap.size();
-}
-
-Int_t CbmFsdDetectorMapManager::GetModuleIdByPath(const string& path)
-{
-  std::map<string, Int_t>::iterator it;
-  it = fModulePathToIdMap.find(path);
-  if (it == fModulePathToIdMap.end()) return -1;
-  return it->second;
-}
-
-
-CbmFsdModuleData* CbmFsdDetectorMapManager::GetModuleDataById(Int_t modId)
-{
-  std::map<Int_t, CbmFsdModuleData*>::iterator it;
-  it = fModuleIdToDataMap.find(modId);
-  if (it == fModuleIdToDataMap.end()) return nullptr;
-  return it->second;
-}
-
-vector<Int_t> CbmFsdDetectorMapManager::GetModuleIds() { return fModuleIds; }
diff --git a/core/detectors/fsd/CbmFsdDetectorMapManager.h b/core/detectors/fsd/CbmFsdDetectorMapManager.h
deleted file mode 100644
index 5afe83850a..0000000000
--- a/core/detectors/fsd/CbmFsdDetectorMapManager.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/* Copyright (C) 2023 Physikalisches Institut, Eberhard Karls Universitaet Tuebingen, Tuebingen
-   SPDX-License-Identifier: GPL-3.0-only
-   Authors: Lukas Chlad [committer] */
-
-#ifndef CBMFSDDETECTORMAPMANAGER_H
-#define CBMFSDDETECTORMAPMANAGER_H
-
-#include <RtypesCore.h>  // for Int_t, Double_t
-
-#include <iostream>  // for string
-#include <map>       // for map
-#include <vector>    // for vector
-
-struct CbmFsdModuleData;
-
-class CbmFsdDetectorMapManager {
-
-private:
-  /** Default constructor **/
-  CbmFsdDetectorMapManager();
-
-  /** Default destructor **/
-  ~CbmFsdDetectorMapManager();
-
-public:
-  CbmFsdDetectorMapManager(const CbmFsdDetectorMapManager&) = delete;
-  CbmFsdDetectorMapManager& operator=(const CbmFsdDetectorMapManager&) = delete;
-
-  /**
-	 * Return Instance of CbmFsdDetectorMapManager.
-	 */
-  static CbmFsdDetectorMapManager& GetInstance()
-  {
-    static CbmFsdDetectorMapManager fInstance;
-    return fInstance;
-  }
-
-  /*
-	 * \brief Return digi moduleId by path to node.
-	 */
-  Int_t GetModuleIdByPath(const std::string& path);
-
-  /*
-	 * \brief Return CbmFsdModuleData by digi moduleId.
-	 */
-  CbmFsdModuleData* GetModuleDataById(Int_t id);
-
-  /*
-     * \brief Return ids of all modules
-     */
-  std::vector<Int_t> GetModuleIds();
-
-private:
-  std::map<std::string, Int_t> fModulePathToIdMap;
-  std::map<Int_t, CbmFsdModuleData*> fModuleIdToDataMap;
-  std::vector<Int_t> fModuleIds;  // vector of all  module ids
-
-  /*
-	 * \brief Initialize maps.
-	 */
-  void Init();
-};
-
-#endif /* CBMFSDDETECTORMAPMANAGER_H */
diff --git a/core/detectors/fsd/CbmFsdDetectorData.h b/core/detectors/fsd/CbmFsdDetectorSpecs.h
similarity index 50%
rename from core/detectors/fsd/CbmFsdDetectorData.h
rename to core/detectors/fsd/CbmFsdDetectorSpecs.h
index 5bab1cfbae..bb7c83f618 100644
--- a/core/detectors/fsd/CbmFsdDetectorData.h
+++ b/core/detectors/fsd/CbmFsdDetectorSpecs.h
@@ -2,14 +2,16 @@
    SPDX-License-Identifier: GPL-3.0-only
    Authors: Lukas Chlad [committer] */
 
-#ifndef CBMFSDDETECTORDATA_H
-#define CBMFSDDETECTORDATA_H
+#ifndef CBMFSDDETECTORSPECS_H
+#define CBMFSDDETECTORSPECS_H
 
 #include <RtypesCore.h>  // for Double_t, Int_t
+#include <TString.h>     // for TString
 
-struct CbmFsdModuleData {
-  // module index
-  Int_t fId;
+struct CbmFsdModuleSpecs {
+  // location indices
+  Int_t fUnitId;
+  Int_t fModuleId;
   // module center position
   Double_t fX;
   Double_t fY;
@@ -20,4 +22,13 @@ struct CbmFsdModuleData {
   Double_t dZ;
 };
 
-#endif /* CBMFSDDETECTORDATA_H */
+struct CbmFsdUnitSpecs {
+  // name of unit
+  TString fUnitName;
+  // unit id
+  Int_t fUnitId;
+  // number of modules in unit
+  Int_t fNumModules;
+};
+
+#endif /* CBMFSDDETECTORSPECS_H */
diff --git a/core/detectors/fsd/CbmFsdDigiPar.cxx b/core/detectors/fsd/CbmFsdDigiPar.cxx
new file mode 100644
index 0000000000..72ba522681
--- /dev/null
+++ b/core/detectors/fsd/CbmFsdDigiPar.cxx
@@ -0,0 +1,79 @@
+/* Copyright (C) 2023 Physikalisches Institut, Eberhard Karls Universitaet Tuebingen, Tuebingen
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Florian Uhlig, Lukas Chlad [committer] */
+
+#include "CbmFsdDigiPar.h"
+
+#include <FairParGenericSet.h>  // for FairParGenericSet
+#include <FairParamList.h>      // for FairParamList
+#include <Logger.h>             // for LOG, Logger
+
+ClassImp(CbmFsdDigiPar)
+
+  CbmFsdDigiPar::CbmFsdDigiPar(const char* name, const char* title, const char* context)
+  : FairParGenericSet(name, title, context)
+  , fNumPhotoDets(-1)
+  , fTimeResolution(-1.)
+  , fEnergyResolution(-1.)
+{
+  detName = "Fsd";
+}
+
+CbmFsdDigiPar::~CbmFsdDigiPar(void) { clear(); }
+
+void CbmFsdDigiPar::clear(void)
+{
+  status = kFALSE;
+  resetInputVersions();
+}
+
+void CbmFsdDigiPar::putParams(FairParamList* l)
+{
+  if (!l) { return; }
+
+  l->add("NumPhotoDets", fNumPhotoDets);
+  l->add("NumUnits", fNumUnits);
+  l->add("TimeResolution", fTimeResolution);
+  l->add("EnergyResolution", fEnergyResolution);
+  l->add("DeadTime", fDeadTime);
+}
+
+Bool_t CbmFsdDigiPar::getParams(FairParamList* l)
+{
+  if (!l) { return kFALSE; }
+
+  LOG(debug2) << "Get the FSD digitization parameters.";
+
+  if (!l->fill("NumPhotoDets", &fNumPhotoDets)) return kFALSE;
+  if (!l->fill("NumUnits", &fNumUnits)) return kFALSE;
+
+  fTimeResolution.Set(fNumUnits);
+  fEnergyResolution.Set(fNumUnits);
+  fDeadTime.Set(fNumUnits);
+  if (!l->fill("TimeResolution", &fTimeResolution)) return kFALSE;
+  if (!l->fill("EnergyResolution", &fEnergyResolution)) return kFALSE;
+  if (!l->fill("DeadTime", &fDeadTime)) return kFALSE;
+
+  return kTRUE;
+}
+
+Double_t CbmFsdDigiPar::GetTimeResolution(Int_t iUnitId) const
+{
+  if (iUnitId < fNumUnits) return fTimeResolution[iUnitId];
+  else
+    return -1.;
+}
+
+Double_t CbmFsdDigiPar::GetEnergyResolution(Int_t iUnitId) const
+{
+  if (iUnitId < fNumUnits) return fEnergyResolution[iUnitId];
+  else
+    return -1.;
+}
+
+Double_t CbmFsdDigiPar::GetDeadTime(Int_t iUnitId) const
+{
+  if (iUnitId < fNumUnits) return fDeadTime[iUnitId];
+  else
+    return -1.;
+}
diff --git a/core/detectors/fsd/CbmFsdDigiPar.h b/core/detectors/fsd/CbmFsdDigiPar.h
new file mode 100644
index 0000000000..b0502615c8
--- /dev/null
+++ b/core/detectors/fsd/CbmFsdDigiPar.h
@@ -0,0 +1,47 @@
+/* Copyright (C) 2023 Physikalisches Institut, Eberhard Karls Universitaet Tuebingen, Tuebingen
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Florian Uhlig, Lukas Chlad [committer] */
+
+#ifndef CBMFSDDIGIPAR_H
+#define CBMFSDDIGIPAR_H
+
+#include <FairParGenericSet.h>  // for FairParGenericSet
+
+#include <Rtypes.h>      // for THashConsistencyHolder, ClassDef
+#include <RtypesCore.h>  // for Int_t, Double_t
+#include <TArrayD.h>     // for TArrayD
+
+class FairParamList;
+
+class CbmFsdDigiPar : public FairParGenericSet {
+public:
+  CbmFsdDigiPar(
+    const char* name = "CbmFsdDigiPar", const char* title = "Digitization parameters for the FSD detector",
+    const char* context = "Needed parameters to adjust FsdDigitizer according to the geometry and read-out propetries");
+
+  CbmFsdDigiPar(const CbmFsdDigiPar&) = delete;
+  CbmFsdDigiPar& operator=(const CbmFsdDigiPar&) = delete;
+
+  ~CbmFsdDigiPar(void);
+
+  void clear(void);
+  void putParams(FairParamList*);
+  Bool_t getParams(FairParamList*);
+
+  Int_t GetNumPhotoDets() const { return fNumPhotoDets; }
+  Int_t GetNumUnits() const { return fNumUnits; }
+  Double_t GetTimeResolution(Int_t iUnitId) const;
+  Double_t GetEnergyResolution(Int_t iUnitId) const;
+  Double_t GetDeadTime(Int_t iUnitId) const;
+
+private:
+  Int_t fNumPhotoDets;        // number of photo detectors per module
+  Int_t fNumUnits;            // number of units within given FSD geo version
+  TArrayD fTimeResolution;    // value to smear the timing via gaussian
+  TArrayD fEnergyResolution;  // value to smear the energy measurement via gaussian
+  TArrayD fDeadTime;          // value to separate digis in time-based
+
+  ClassDef(CbmFsdDigiPar, 1)
+};
+
+#endif
diff --git a/core/detectors/fsd/CbmFsdGeoHandler.cxx b/core/detectors/fsd/CbmFsdGeoHandler.cxx
new file mode 100644
index 0000000000..465e5ff215
--- /dev/null
+++ b/core/detectors/fsd/CbmFsdGeoHandler.cxx
@@ -0,0 +1,173 @@
+/* Copyright (C) 2023 Physikalisches Institut, Eberhard Karls Universitaet Tuebingen, Tuebingen
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Lukas Chlad [committer] */
+
+#include "CbmFsdGeoHandler.h"
+
+#include "CbmFsdAddress.h"        // for CbmFsdAddress
+#include "CbmFsdDetectorSpecs.h"  // for CbmFsdModuleSpecs
+
+#include <Logger.h>  // for LOG, Logger
+
+#include <TGeoBBox.h>     // for TGeoBBox
+#include <TGeoManager.h>  // for TGeoManager, gGeoManager
+#include <TGeoMatrix.h>   // for TGeoMatrix
+#include <TGeoNode.h>     // for TGeoIterator, TGeoNode
+#include <TGeoVolume.h>   // for TGeoVolume
+#include <TVirtualMC.h>   // for TVirtualMC, gMC
+
+#include <string>   // for operator<, stoul
+#include <utility>  // for pair
+
+#include <stddef.h>  // for size_t
+
+using namespace std;
+
+// constructor with initialization of maps
+CbmFsdGeoHandler::CbmFsdGeoHandler() { InitMaps(); }
+
+void CbmFsdGeoHandler::InitMaps()
+{
+  fUnitIdToSpecsMap.clear();
+  fModuleIdToSpecsMap.clear();
+
+  Int_t currentModuleId = -1;
+  Int_t currentUnitId   = -1;
+
+  TGeoIterator geoIterator(gGeoManager->GetTopNode()->GetVolume());
+
+  TGeoNode* curNode;
+  geoIterator.Reset();  // safety to reset to "cave" befor the loop starts
+  while ((curNode = geoIterator())) {
+    TString nodePath;
+    geoIterator.GetPath(nodePath);
+    if (!nodePath.Contains(fBranchStr)) {
+      geoIterator.Skip();  // skip current branch when it is not FSD => should speed up
+      continue;            // don't do anything for this branch
+    }
+
+    TString nodeName(curNode->GetName());
+    if (nodeName.Contains(fUnitStr)) {
+      currentUnitId = curNode->GetNumber();
+
+      CbmFsdUnitSpecs* unitSpecs = new CbmFsdUnitSpecs();
+      unitSpecs->fUnitId         = currentUnitId;
+      unitSpecs->fUnitName       = static_cast<TString>(curNode->GetVolume()->GetName());
+      unitSpecs->fNumModules     = curNode->GetNdaughters();
+      fUnitIdToSpecsMap.insert(pair<Int_t, CbmFsdUnitSpecs*>(currentUnitId, unitSpecs));
+    }
+    if (nodeName.Contains(fModuleStr)) {
+      currentModuleId             = curNode->GetNumber();
+      const TGeoMatrix* curMatrix = geoIterator.GetCurrentMatrix();
+      const Double_t* curNodeTr   = curMatrix->GetTranslation();
+
+      TGeoVolume* curScintillator = nullptr;
+      for (int idn = 0; idn < curNode->GetNdaughters(); idn++) {
+        TGeoNode* daughterNode = curNode->GetDaughter(idn);
+        if (TString(daughterNode->GetName()).Contains(fActiveMatStr)) {
+          curScintillator = daughterNode->GetVolume();
+          break;
+        }
+      }
+      const TGeoBBox* shape = (const TGeoBBox*) (curScintillator->GetShape());
+
+      CbmFsdModuleSpecs* moduleSpecs = new CbmFsdModuleSpecs();
+      moduleSpecs->fX                = curNodeTr[0];
+      moduleSpecs->fY                = curNodeTr[1];
+      moduleSpecs->fZ                = curNodeTr[2];
+      moduleSpecs->dX                = shape->GetDX();
+      moduleSpecs->dY                = shape->GetDY();
+      moduleSpecs->dZ                = shape->GetDZ();
+      moduleSpecs->fModuleId         = currentModuleId;
+      moduleSpecs->fUnitId           = currentUnitId;
+      fModuleIdToSpecsMap.insert(pair<Int_t, CbmFsdModuleSpecs*>(currentModuleId, moduleSpecs));
+    }
+  }
+
+  LOG(info) << "CbmFsdGeoHandler has initialized maps";
+  LOG(info) << "fUnitIdToSpecsMap.size() = " << fUnitIdToSpecsMap.size();
+  LOG(info) << "fModuleIdToSpecsMap.size() = " << fModuleIdToSpecsMap.size();
+}
+
+CbmFsdModuleSpecs* CbmFsdGeoHandler::GetModuleSpecsById(Int_t modId)
+{
+  std::map<Int_t, CbmFsdModuleSpecs*>::iterator it;
+  it = fModuleIdToSpecsMap.find(modId);
+  if (it == fModuleIdToSpecsMap.end()) return nullptr;
+  return it->second;
+}
+
+CbmFsdUnitSpecs* CbmFsdGeoHandler::GetUnitSpecsById(Int_t unitId)
+{
+  std::map<Int_t, CbmFsdUnitSpecs*>::iterator it;
+  it = fUnitIdToSpecsMap.find(unitId);
+  if (it == fUnitIdToSpecsMap.end()) return nullptr;
+  return it->second;
+}
+
+int32_t CbmFsdGeoHandler::GetAddress(TString geoPath) const
+{
+  Int_t moduleID = GetCopyNumberByKey(geoPath, fModuleStr);
+  Int_t unitID   = GetCopyNumberByKey(geoPath, fUnitStr);
+
+  return CbmFsdAddress::GetAddress(unitID, moduleID);
+}
+
+int32_t CbmFsdGeoHandler::GetCurrentAddress(TVirtualMC* vmc) const
+{
+  Int_t moduleID = -1;
+  Int_t unitID   = -1;
+
+  Int_t upstreamOffset = 0;
+  while (((TString) vmc->CurrentVolOffName(upstreamOffset)).Length() > 0) {
+    if (((TString) vmc->CurrentVolOffName(upstreamOffset)).Contains(fUnitStr))
+      vmc->CurrentVolOffID(upstreamOffset, unitID);
+    if (((TString) vmc->CurrentVolOffName(upstreamOffset)).Contains(fModuleStr))
+      vmc->CurrentVolOffID(upstreamOffset, moduleID);
+    upstreamOffset++;
+  }
+
+  return CbmFsdAddress::GetAddress(unitID, moduleID);
+}
+
+int32_t CbmFsdGeoHandler::GetCurrentAddress(TGeoManager* geoMan) const
+{
+  Int_t moduleID = -1;
+  Int_t unitID   = -1;
+
+  Int_t upstreamOffset = 0;
+  while (upstreamOffset <= geoMan->GetLevel()) {
+    TGeoNode* node = geoMan->GetMother(upstreamOffset);
+    if (((TString) node->GetVolume()->GetName()).Contains(fUnitStr)) unitID = node->GetNumber();
+    if (((TString) node->GetVolume()->GetName()).Contains(fModuleStr)) moduleID = node->GetNumber();
+    upstreamOffset++;
+  }
+
+  return CbmFsdAddress::GetAddress(unitID, moduleID);
+}
+
+Int_t CbmFsdGeoHandler::GetCopyNumberByKey(TString geoPath, TString key) const
+{
+  Int_t copyNum = -1;
+
+  // sanity checks
+  if (!geoPath.Contains(fBranchStr)) { LOG(warning) << __func__ << ": In geoPath " << fBranchStr << " was not found!"; }
+  else if (!geoPath.Contains(key)) {
+    LOG(warning) << __func__ << ": In geoPath " << key << " was not found!";
+  }
+  else {
+    Ssiz_t keyStart     = geoPath.Index(key);
+    TString keyName     = geoPath(keyStart, geoPath.Index("/", keyStart) - keyStart);
+    Ssiz_t copyNumStart = keyName.Last('_') + 1;
+    TString copyNumStr  = keyName(copyNumStart, keyName.Length() - copyNumStart);
+    if (!copyNumStr.IsDigit()) {
+      LOG(warning) << __func__ << ": Expected numerical part from " << geoPath << " using key " << key << " is "
+                   << copyNumStr << " which does not contain only digits!";
+    }
+    else {
+      copyNum = copyNumStr.Atoi();
+    }
+  }
+
+  return copyNum;
+}
diff --git a/core/detectors/fsd/CbmFsdGeoHandler.h b/core/detectors/fsd/CbmFsdGeoHandler.h
new file mode 100644
index 0000000000..e7ed7f947e
--- /dev/null
+++ b/core/detectors/fsd/CbmFsdGeoHandler.h
@@ -0,0 +1,88 @@
+/* Copyright (C) 2023 Physikalisches Institut, Eberhard Karls Universitaet Tuebingen, Tuebingen
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Lukas Chlad [committer] */
+
+#ifndef CBMFSDGEOHANDLER_H
+#define CBMFSDGEOHANDLER_H
+
+#include <RtypesCore.h>  // for Int_t, Bool_t
+#include <TString.h>     // for TString
+
+#include <iostream>  // for string
+#include <map>       // for map
+#include <vector>    // for vector
+
+struct CbmFsdModuleSpecs;
+struct CbmFsdUnitSpecs;
+class TVirtualMC;
+class TGeoManager;
+
+class CbmFsdGeoHandler {
+
+private:
+  /** Default constructor **/
+  CbmFsdGeoHandler();
+
+  /** Default destructor **/
+  ~CbmFsdGeoHandler() = default;
+
+  /** @brief Helper function to extract copy number from geoPath using key word
+   ** @return integer corresponding to copy number of the key word
+   **/
+  Int_t GetCopyNumberByKey(TString geoPath, TString key) const;
+
+public:
+  CbmFsdGeoHandler(const CbmFsdGeoHandler&) = delete;
+  CbmFsdGeoHandler& operator=(const CbmFsdGeoHandler&) = delete;
+
+  /**
+	 * Return Instance of CbmFsdGeoHandler.
+	 */
+  static CbmFsdGeoHandler& GetInstance()
+  {
+    static CbmFsdGeoHandler fInstance;
+    return fInstance;
+  }
+
+  /*
+	 * \brief Return CbmFsdModuleSpecs by digi moduleId.
+	 */
+  CbmFsdModuleSpecs* GetModuleSpecsById(Int_t id);
+
+  /*
+	 * \brief Return CbmFsdUnitSpecs by digi unitId.
+	 */
+  CbmFsdUnitSpecs* GetUnitSpecsById(Int_t id);
+
+  /*
+	 * \brief Initialize maps.
+	 */
+  void InitMaps();
+
+  /** @brief Get the unique address from geometry path string
+   ** @return integer corresponding to CbmFsdAddress scheme
+   **/
+  int32_t GetAddress(TString geoPath) const;
+
+  /** @brief Get the unique address from TVirtualMC
+   ** @return integer corresponding to CbmFsdAddress scheme
+   **/
+  int32_t GetCurrentAddress(TVirtualMC* vmc) const;
+
+  /** @brief Get the unique address from TGeoManager
+   ** @return integer corresponding to CbmFsdAddress scheme
+   **/
+  int32_t GetCurrentAddress(TGeoManager* geoMan) const;
+
+private:
+  std::map<Int_t, CbmFsdModuleSpecs*> fModuleIdToSpecsMap;
+  std::map<Int_t, CbmFsdUnitSpecs*> fUnitIdToSpecsMap;
+
+  // these key TStrings must corresponds to what one introduce in create geo macros!!
+  const TString fBranchStr    = "fsd_";
+  const TString fUnitStr      = "unit";
+  const TString fModuleStr    = "module";
+  const TString fActiveMatStr = "scint";
+};
+
+#endif /* CBMFSDGEOHANDLER_H */
diff --git a/sim/detectors/CMakeLists.txt b/sim/detectors/CMakeLists.txt
index 8c50987d7d..1399b1cd19 100644
--- a/sim/detectors/CMakeLists.txt
+++ b/sim/detectors/CMakeLists.txt
@@ -5,6 +5,7 @@ add_subdirectory(bmon)
 add_subdirectory(much)
 add_subdirectory(mvd)
 add_subdirectory(psd)
+add_subdirectory(fsd)
 add_subdirectory(rich)
 add_subdirectory(sts)
 add_subdirectory(tof)
diff --git a/sim/detectors/fsd/CMakeLists.txt b/sim/detectors/fsd/CMakeLists.txt
new file mode 100644
index 0000000000..a73ae12258
--- /dev/null
+++ b/sim/detectors/fsd/CMakeLists.txt
@@ -0,0 +1,30 @@
+set(INCLUDE_DIRECTORIES
+  ${CMAKE_CURRENT_SOURCE_DIR}
+  )
+
+set(SRCS
+  CbmFsdMC.cxx
+  CbmFsdDigitize.cxx
+  )
+
+set(LIBRARY_NAME CbmFsdSim)
+set(LINKDEF ${LIBRARY_NAME}LinkDef.h)
+set(PUBLIC_DEPENDENCIES
+  CbmBase
+  CbmData
+  FairRoot::Base
+  ROOT::Core
+  ROOT::Physics
+  )
+
+set(PRIVATE_DEPENDENCIES
+  CbmSimBase
+  CbmFsdBase
+  FairRoot::ParBase
+  FairLogger::FairLogger
+  ROOT::Geom
+  ROOT::MathCore
+  ${VMCLIB}
+  )
+
+generate_cbm_library()
diff --git a/sim/detectors/fsd/CbmFsdDigitize.cxx b/sim/detectors/fsd/CbmFsdDigitize.cxx
new file mode 100644
index 0000000000..b889b53a55
--- /dev/null
+++ b/sim/detectors/fsd/CbmFsdDigitize.cxx
@@ -0,0 +1,286 @@
+/* Copyright (C) 2023 Physikalisches Institut Eberhard Karls Universitaet Tuebingen, Tuebingen
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Alla Maevskaya, Selim Seddiki, Sergey Morozov, Volker Friese, Evgeny Kashirin, Lukas Chlad [committer] */
+
+#include "CbmFsdDigitize.h"
+
+#include "CbmFsdAddress.h"
+#include "CbmFsdDigi.h"
+#include "CbmFsdDigiPar.h"
+#include "CbmFsdPoint.h"
+#include "CbmLink.h"
+#include "CbmMatch.h"
+
+#include <FairRootManager.h>
+#include <FairRun.h>
+#include <FairRuntimeDb.h>
+#include <Logger.h>
+
+#include <TArrayD.h>
+#include <TClonesArray.h>
+#include <TMath.h>
+#include <TRandom3.h>
+#include <TStopwatch.h>
+
+#include <cassert>
+#include <iomanip>
+#include <iostream>
+#include <map>
+
+using std::cout;
+using std::endl;
+using std::fixed;
+using std::right;
+using std::setprecision;
+using std::setw;
+
+using std::map;
+using std::pair;
+
+// -----   Public method Init   --------------------------------------------
+InitStatus CbmFsdDigitize::Init()
+{
+  if (!fEventMode) { LOG(info) << GetName() << " uses TimeBased mode."; }
+  else {
+    LOG(info) << GetName() << " uses Events mode.";
+  }
+
+  // Get RootManager
+  FairRootManager* ioman = FairRootManager::Instance();
+
+  // Get input array
+  fPointArray = dynamic_cast<TClonesArray*>(ioman->GetObject("FsdPoint"));
+  if (nullptr == fPointArray) {
+    LOG(error) << GetName() << ": Error in reading from file! No FsdPoint array.";
+    return kERROR;
+  }
+  TString objectName = fPointArray->GetClass()->GetName();
+  if (0 != objectName.CompareTo("CbmFsdPoint")) {
+    LOG(fatal) << GetName() << ": TClonesArray does not contain data of the expected class CbmFsdPoint";
+  }
+
+  // Initialise parameters
+  InitParams();
+
+  // Create and register output array
+  RegisterOutput();
+
+  // Statistics
+  fNumEvents = 0;
+  fNumPoints = 0.;
+  fNumDigis  = 0.;
+  fTimeTot   = 0.;
+
+  LOG(info) << GetName() << ": Initialisation successful " << kSUCCESS;
+  return kSUCCESS;
+}
+// -------------------------------------------------------------------------
+
+void CbmFsdDigitize::SetParContainers()
+{
+  LOG(info) << GetName() << ": Get the digi parameters for FSD";
+
+  // Get Base Container
+  FairRun* ana        = FairRun::Instance();
+  FairRuntimeDb* rtdb = ana->GetRuntimeDb();
+
+  fDigiPar = dynamic_cast<CbmFsdDigiPar*>(rtdb->getContainer("CbmFsdDigiPar"));
+  if (fDigiPar == nullptr) {
+    LOG(fatal) << GetName() << ": parameter container CbmFsdDigiPar not available in current RuntimeDb!!";
+  }
+}
+
+void CbmFsdDigitize::InitParams()
+{
+  if (!fDigiPar) { LOG(fatal) << GetName() << ": parameter container CbmFsdDigiPar not found!!"; }
+
+  // Get values from loaded parameter container
+  fNumPhotoDets = fDigiPar->GetNumPhotoDets();
+  fNumUnits     = fDigiPar->GetNumUnits();
+
+  if (fNumPhotoDets < 1 || fNumUnits < 1) {
+    LOG(fatal) << GetName() << ": parameter values for FSD digitization does not meet a sanity check!!";
+  }
+
+  fTimeResolution.Set(fNumUnits);
+  fEnergyResolution.Set(fNumUnits);
+  fDeadTime.Set(fNumUnits);
+
+  for (Int_t iu = 0; iu < fNumUnits; iu++) {
+    fTimeResolution[iu]   = fDigiPar->GetTimeResolution(iu);
+    fEnergyResolution[iu] = fDigiPar->GetEnergyResolution(iu);
+    fDeadTime[iu]         = fDigiPar->GetDeadTime(iu);
+
+    if (fDeadTime[iu] < 0. || fTimeResolution[iu] < 0. || fEnergyResolution[iu] < 0.) {
+      LOG(fatal) << GetName() << ": parameter values for FSD digitization does not meet a sanity check!!";
+    }
+  }
+}
+
+// -----   Public method Exec   --------------------------------------------
+void CbmFsdDigitize::Exec(Option_t*)
+{
+  // Event info (for event time)
+  GetEventInfo();
+
+  TStopwatch timer;
+  timer.Start();
+
+  Int_t nDigis = 0;
+
+  // digis that are distant in time to this event (and thus can not be modified anymore) should be send to DAQ
+  if (!fEventMode) ReleaseBuffer(kFALSE);
+
+
+  Int_t nPoints = fPointArray->GetEntriesFast();
+  LOG(debug) << fName << ": processing event " << fCurrentEvent << " at t = " << fCurrentEventTime << " ns";
+
+  // Declare some variables
+  CbmFsdPoint* point = nullptr;
+
+  Int_t modId      = -1;
+  Int_t unitId     = -1;
+  Int_t photodetId = -1;
+
+  if (fNumPhotoDets > 1) {
+    // random uniform split into photo detectors?!
+    photodetId = gRandom->Integer(static_cast<UInt_t>(fNumPhotoDets));
+  }
+  else {
+    photodetId = 0;
+  }
+
+  // Loop over FsdPoints
+  for (Int_t iPoint = 0; iPoint < nPoints; iPoint++) {
+    point = static_cast<CbmFsdPoint*>(fPointArray->At(iPoint));
+    if (!point) continue;
+
+    int32_t pointAddress = static_cast<int32_t>(point->GetDetectorID());
+    modId =
+      static_cast<Int_t>(CbmFsdAddress::GetElementId(pointAddress, static_cast<int32_t>(CbmFsdAddress::Level::Module)));
+    unitId =
+      static_cast<Int_t>(CbmFsdAddress::GetElementId(pointAddress, static_cast<int32_t>(CbmFsdAddress::Level::Unit)));
+    Double_t eloss   = point->GetEnergyLoss() + gRandom->Gaus(0., fEnergyResolution[unitId]);
+    Double_t time    = fCurrentEventTime + point->GetTime() + gRandom->Gaus(0., fTimeResolution[unitId]);
+    uint32_t address = static_cast<uint32_t>(CbmFsdAddress::GetAddress(
+      static_cast<uint32_t>(unitId), static_cast<uint32_t>(modId), static_cast<uint32_t>(photodetId)));
+
+    CbmLink link(eloss, iPoint, fCurrentMCEntry, fCurrentInput);  // weight of link is the energy loss
+
+    auto it = fDigiBuffer.find(pointAddress);
+    if (it != fDigiBuffer.end()) {
+      // there is already in buffer a digi with this key
+      Double_t timeDiff = std::fabs(time - it->second.first->GetTime());
+      if (timeDiff < fDeadTime[unitId]) {
+        // modify found digi : add energy deposition, modify time if earlier
+        it->second.first->SetEdep(it->second.first->GetEdep() + eloss);
+        if (time < it->second.first->GetTime()) it->second.first->SetTime(time);
+        // add link to digimatch
+        it->second.second->AddLink(link);
+      }
+      else {
+        // not within time cut -> send the digi from buffer and replace by the new one
+        CbmFsdDigi* oldDigi =
+          new CbmFsdDigi(it->second.first->GetAddress(), it->second.first->GetTime(), it->second.first->GetEdep());
+        CbmMatch* oldDigiMatch = new CbmMatch(*it->second.second);
+        if (fCreateMatches) SendData(oldDigi->GetTime(), oldDigi, oldDigiMatch);
+        else
+          SendData(oldDigi->GetTime(), oldDigi);
+        fDigiBuffer.erase(it);
+
+        CbmFsdDigi* digi = new CbmFsdDigi(address, time, eloss);
+        CbmMatch* match  = new CbmMatch();
+        match->AddLink(link);
+        fDigiBuffer.insert(std::make_pair(pointAddress, std::make_pair(digi, match)));
+        nDigis++;
+      }
+    }
+    else {
+      // digi with this key is not yet in buffer -> insert it
+      CbmFsdDigi* digi = new CbmFsdDigi(address, time, eloss);
+      CbmMatch* match  = new CbmMatch();
+      match->AddLink(link);
+      fDigiBuffer.insert(std::make_pair(pointAddress, std::make_pair(digi, match)));
+      nDigis++;
+    }
+  }  // Loop over MCPoints
+
+  // in EventMode send and clear the whole buffer after MCEvent is processed
+  if (fEventMode) ReleaseBuffer(kTRUE);
+
+  // --- Event log
+  timer.Stop();
+  LOG(info) << "+ " << setw(15) << GetName() << ": Event " << setw(6) << right << fCurrentEvent << " at " << fixed
+            << setprecision(3) << fCurrentEventTime << " ns, points: " << nPoints << ", digis: " << nDigis
+            << ". Exec time " << setprecision(6) << timer.RealTime() << " s.";
+
+  // --- Run statistics
+  fNumEvents++;
+  fNumPoints += nPoints;
+  fNumDigis += nDigis;
+  fTimeTot += timer.RealTime();
+}
+// -------------------------------------------------------------------------
+
+
+// -----   End-of-run   ----------------------------------------------------
+void CbmFsdDigitize::Finish()
+{
+  std::cout << std::endl;
+  LOG(info) << "=====================================";
+  LOG(info) << GetName() << ": Finish run";
+  if (!fEventMode) ReleaseBuffer(kTRUE);  // in time-based mode, send all what is left in buffer to DAQ
+  LOG(info) << GetName() << ": Run summary";
+  LOG(info) << "Events processed    : " << fNumEvents;
+  LOG(info) << "FsdPoint / event    : " << setprecision(1) << fNumPoints / Double_t(fNumEvents);
+  LOG(info) << "FsdDigi / event     : " << fNumDigis / Double_t(fNumEvents);
+  LOG(info) << "Digis per point     : " << setprecision(6) << fNumDigis / fNumPoints;
+  LOG(info) << "Real time per event : " << fTimeTot / Double_t(fNumEvents) << " s";
+  LOG(info) << "=====================================";
+}
+// -------------------------------------------------------------------------
+
+
+void CbmFsdDigitize::ReleaseBuffer(Bool_t sendEverything)
+{
+  if (sendEverything) {
+    // send everything
+    for (const auto& dp : fDigiBuffer) {
+      CbmFsdDigi* digi =
+        new CbmFsdDigi(dp.second.first->GetAddress(), dp.second.first->GetTime(), dp.second.first->GetEdep());
+      CbmMatch* digiMatch = new CbmMatch(*dp.second.second);
+      if (fCreateMatches) SendData(digi->GetTime(), digi, digiMatch);
+      else
+        SendData(digi->GetTime(), digi);
+    }  // # digi buffer
+    fDigiBuffer.clear();
+  }
+  else {
+    // send only if time difference to the new start of event is larger than deadtime
+    std::vector<int32_t> keysSent;
+    for (const auto& dp : fDigiBuffer) {
+      Int_t unitId = static_cast<Int_t>(CbmFsdAddress::GetElementId(static_cast<int32_t>(dp.second.first->GetAddress()),
+                                                                    static_cast<int32_t>(CbmFsdAddress::Level::Unit)));
+      Double_t timeDiff = std::fabs(dp.second.first->GetTime() - fCurrentEventTime);
+      if (timeDiff > fDeadTime[unitId]) {
+        // send this digi
+        CbmFsdDigi* digi =
+          new CbmFsdDigi(dp.second.first->GetAddress(), dp.second.first->GetTime(), dp.second.first->GetEdep());
+        CbmMatch* digiMatch = new CbmMatch(*dp.second.second);
+        if (fCreateMatches) SendData(digi->GetTime(), digi, digiMatch);
+        else
+          SendData(digi->GetTime(), digi);
+
+        // save which keys were sent and should be removed
+        keysSent.push_back(dp.first);
+      }  // ? time
+    }    // # digi buffer
+
+    // remove the sent elements from buffer
+    for (auto& key : keysSent) {
+      fDigiBuffer.erase(key);
+    }
+  }  // ? sendEverything
+}
+
+ClassImp(CbmFsdDigitize)
diff --git a/sim/detectors/fsd/CbmFsdDigitize.h b/sim/detectors/fsd/CbmFsdDigitize.h
new file mode 100644
index 0000000000..f8c3791a21
--- /dev/null
+++ b/sim/detectors/fsd/CbmFsdDigitize.h
@@ -0,0 +1,99 @@
+/* Copyright (C) 2023 Physikalisches Institut Eberhard Karls Universitaet Tuebingen, Tuebingen
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Sergey Morozov, Volker Friese, Lukas Chlad [committer] */
+
+/** @class CbmFsdDigitize
+ ** @date  14.07.2023
+ ** @author Lukas Chlad <l.chlad@gsi.de>
+ ** @brief Class for the digitization of the CBM-FSD
+ **
+ ** The digitizer produces digits of type CbmFsdDigi as sum of Edep of Points and adds smearing in energy and time according to parameters
+ **/
+
+
+#ifndef CBMFSDDIGITIZE_H
+#define CBMFSDDIGITIZE_H 1
+
+
+#include "CbmDefs.h"
+#include "CbmDigitize.h"
+#include "CbmFsdDigi.h"
+
+#include <TArrayD.h>
+
+class TClonesArray;
+class CbmFsdDigiPar;
+
+class CbmFsdDigitize : public CbmDigitize<CbmFsdDigi> {
+
+public:
+  /** Default constructor **/
+  CbmFsdDigitize() : CbmDigitize<CbmFsdDigi>("FsdDigitize") {};
+
+  /** Destructor **/
+  virtual ~CbmFsdDigitize() = default;
+
+  CbmFsdDigitize(const CbmFsdDigitize&) = delete;
+  CbmFsdDigitize operator=(const CbmFsdDigitize&) = delete;
+
+  ECbmModuleId GetSystemId() const { return ECbmModuleId::kFsd; }
+
+
+  /**
+   ** @brief Inherited from FairTask.
+   **/
+  virtual InitStatus Init();
+
+  /**
+   ** @brief Inherited from FairTask.
+   **/
+  virtual void SetParContainers();
+
+  /** Virtual method Exec **/
+  virtual void Exec(Option_t* opt);
+
+
+  /** @brief End-of-run action **/
+  virtual void Finish();
+
+
+private:
+  CbmFsdDigiPar* fDigiPar = nullptr;
+
+  Int_t fNumPhotoDets = -1;
+  Int_t fNumUnits     = -1;
+  TArrayD fTimeResolution {};
+  TArrayD fEnergyResolution {};
+  TArrayD fDeadTime {};
+
+  Int_t fNumEvents    = 0;
+  Double_t fNumPoints = 0.;
+  Double_t fNumDigis  = 0.;
+  Double_t fTimeTot   = 0.;
+
+  /** Input array of CbmFsdPoints **/
+  TClonesArray* fPointArray = nullptr;
+
+  // Temporary storage for digis, key is DetectorID from FsdPoint
+  std::map<int32_t, std::pair<CbmFsdDigi*, CbmMatch*>> fDigiBuffer;
+
+  /** @brief Initialise the parameters **/
+  void InitParams();
+
+  /** @brief release digi from local buffer to CbmDaq
+   ** TIME BASED 
+   **  - at the beginning of MCEvent loop over digi buffer 
+   **    and send those digis that are too far from start of current event to be possibly edited
+   **    free the location of sent digis
+   **  - at the end of whole run do the same only send everything, clear buffer
+   **
+   ** EVENT BASED
+   **  - at the beginning of MCEvent loop send everything, clear buffer
+   **/
+  void ReleaseBuffer(Bool_t sendEverything);
+
+
+  ClassDef(CbmFsdDigitize, 1);
+};
+
+#endif
diff --git a/sim/detectors/fsd/CbmFsdMC.cxx b/sim/detectors/fsd/CbmFsdMC.cxx
new file mode 100644
index 0000000000..21880a7520
--- /dev/null
+++ b/sim/detectors/fsd/CbmFsdMC.cxx
@@ -0,0 +1,135 @@
+/* Copyright (C) 2023 Physikalisches Institut Eberhard Karls Universitaet Tuebingen, Tuebingen
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Alla Maevskaya, Florian Uhlig, Lukas Chlad [committer] */
+
+/** @file CbmFsdMC.cxx
+ ** @since 14.07.2023
+ ** @author Lukas Chlad <l.chlad@gsi.de>
+ **
+ **/
+
+
+#include "CbmFsdMC.h"
+
+#include "CbmFsdGeoHandler.h"
+#include "CbmFsdPoint.h"
+#include "CbmGeometryUtils.h"
+#include "CbmModuleList.h"
+#include "CbmStack.h"
+
+#include <FairVolume.h>
+
+#include <TGeoNode.h>
+#include <TGeoVolume.h>
+#include <TVirtualMC.h>
+
+#include <cassert>
+#include <string>
+
+// -----   Destructor   ----------------------------------------------------
+CbmFsdMC::~CbmFsdMC()
+{
+  if (fFsdPoints) {
+    fFsdPoints->Delete();
+    delete fFsdPoints;
+  }
+}
+// -------------------------------------------------------------------------
+
+
+// -----   Construct the geometry from file   ------------------------------
+void CbmFsdMC::ConstructGeometry()
+{
+  LOG(info) << "Importing FSD geometry from ROOT file " << fgeoName.Data();
+  Cbm::GeometryUtils::ImportRootGeometry(fgeoName, this);
+}
+// -------------------------------------------------------------------------
+
+
+// -----   End of event action   -------------------------------------------
+void CbmFsdMC::EndOfEvent()
+{
+  Print();  // Status output
+  fFsdPoints->Delete();
+}
+// -------------------------------------------------------------------------
+
+
+// -----   Print   ---------------------------------------------------------
+void CbmFsdMC::Print(Option_t*) const
+{
+  LOG(info) << fName << ": " << fFsdPoints->GetEntriesFast() << " points registered in this event.";
+}
+// -------------------------------------------------------------------------
+
+// -----   Initialise   ----------------------------------------------------
+void CbmFsdMC::Initialize()
+{
+  // --- Instantiate the output array
+  fFsdPoints = new TClonesArray("CbmFsdPoint");
+
+  // --- Call the Initialise method of the mother class
+  FairDetector::Initialize();
+}
+// -------------------------------------------------------------------------
+
+
+// -----   Public method ProcessHits  --------------------------------------
+Bool_t CbmFsdMC::ProcessHits(FairVolume*)
+{
+
+  // No action for neutral particles
+  if (TMath::Abs(gMC->TrackCharge()) <= 0) return kFALSE;
+
+  // --- If this is the first step for the track in the volume:
+  //     Reset energy loss and store track parameters
+  if (gMC->IsTrackEntering()) {
+    fTrackID = gMC->GetStack()->GetCurrentTrackNumber();
+
+    fAddress = CbmFsdGeoHandler::GetInstance().GetCurrentAddress(gMC);
+    gMC->TrackPosition(fPos);
+    gMC->TrackMomentum(fMom);
+    fTime   = gMC->TrackTime() * 1.0e09;
+    fLength = gMC->TrackLength();
+    fEloss  = 0.;
+  }  //? track entering
+
+  // --- For all steps within active volume: sum up differential energy loss
+  fEloss += gMC->Edep();
+
+  // --- If track is leaving: get track parameters and create CbmstsPoint
+  if (gMC->IsTrackExiting() || gMC->IsTrackStop() || gMC->IsTrackDisappeared()) {
+
+    // Create CbmFsdPoint
+    Int_t size = fFsdPoints->GetEntriesFast();
+    new ((*fFsdPoints)[size]) CbmFsdPoint(fTrackID, fAddress, fPos.Vect(), fMom.Vect(), fTime, fLength, fEloss);
+
+    // --- Increment number of FsdPoints for this track in the stack
+    CbmStack* stack = dynamic_cast<CbmStack*>(gMC->GetStack());
+    assert(stack);
+    stack->AddPoint(ECbmModuleId::kFsd);
+
+  }  //? track is exiting or stopped
+
+  return kTRUE;
+}
+// -------------------------------------------------------------------------
+
+
+// -----   Register the sensitive volumes   --------------------------------
+void CbmFsdMC::RegisterSensitiveVolumes(TGeoNode* node)
+{
+
+  TObjArray* daughters = node->GetVolume()->GetNodes();
+  for (Int_t iDaughter = 0; iDaughter < daughters->GetEntriesFast(); iDaughter++) {
+    TGeoNode* daughter = dynamic_cast<TGeoNode*>(daughters->At(iDaughter));
+    assert(daughter);
+    if (daughter->GetNdaughters() > 0) RegisterSensitiveVolumes(daughter);
+    TGeoVolume* daughterVolume = daughter->GetVolume();
+    if (CheckIfSensitive(daughterVolume->GetName())) { AddSensitiveVolume(daughterVolume); }  //? Sensitive volume
+  }                                                                                           //# Daughter nodes
+}
+// -------------------------------------------------------------------------
+
+
+ClassImp(CbmFsdMC)
diff --git a/sim/detectors/fsd/CbmFsdMC.h b/sim/detectors/fsd/CbmFsdMC.h
new file mode 100644
index 0000000000..ea9dbd96a0
--- /dev/null
+++ b/sim/detectors/fsd/CbmFsdMC.h
@@ -0,0 +1,165 @@
+/* Copyright (C) 2023 Physikalisches Institut Eberhard Karls Universitaet Tuebingen, Tuebingen
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Yuri Kharlov, Volker Friese, Lukas Chlad [committer] */
+
+/** @file CbmFsdMC.h
+ ** @class CbmFsdMC
+ ** @brief Class for the MC transport of the CBM-FSD
+ ** @since 14.07.2023
+ ** @author Lukas Chlad <l.chlad@gsi.de>
+ ** @version 1.0
+ **
+ ** The CbmFsdMC defines the behaviour of the FSD system during
+ ** transport simulation. It constructs the FSD transport geometry
+ ** and creates objects of type CbmFsdPoints.
+ **/
+
+
+#ifndef CBMFSDMC_H
+#define CBMFSDMC_H 1
+
+#include "CbmDefs.h"  // for ECbmModuleId
+
+#include <FairDetector.h>
+#include <FairRootManager.h>
+
+#include <TClonesArray.h>
+#include <TLorentzVector.h>
+#include <TString.h>
+
+class FairVolume;
+
+class CbmFsdMC : public FairDetector {
+
+public:
+  /** @brief Constructor
+   ** @param active   If set true, ProcessHits will be called
+   ** @param name     Name of detector object
+   **/
+  CbmFsdMC(Bool_t active = kTRUE, const char* name = "FSDMC")
+    : FairDetector(name, active, ToIntegralType(ECbmModuleId::kFsd)) {};
+
+
+  /** Destructor **/
+  virtual ~CbmFsdMC();
+
+  /** Prevent copy constructor and assignment operator **/
+  CbmFsdMC(const CbmFsdMC&) = delete;
+  CbmFsdMC operator=(const CbmFsdMC&) = delete;
+
+
+  /** @brief Check whether a volume is sensitive.
+   ** @param(name)  Volume name
+   ** @value        kTRUE if volume is sensitive, else kFALSE
+   **
+   ** The decision is based on the volume name (has to contain "scint").
+   ** Virtual from FairModule.
+   **/
+  virtual Bool_t IsSensitive(const std::string& name)
+  {
+    return (TString(name).Contains("scint", TString::kIgnoreCase) ? kTRUE : kFALSE);
+  }
+
+  /** @brief Check whether a volume is sensitive.
+   ** @param(name)  Volume name
+   ** @value        kTRUE if volume is sensitive, else kFALSE
+   **
+   ** The decision is based on the volume name (has to contain "scint").
+   ** Virtual from FairModule.
+   **/
+  virtual Bool_t CheckIfSensitive(std::string name) { return IsSensitive(name); }
+
+
+  /** @brief Construct the FSD geometry in the TGeoManager.
+   **
+   ** Only ROOT geometries are supported. The file must contain a top
+   ** volume the name of which starts with "fsd" and a TGeoMatrix for
+   ** the placement of the top fsd volume in the cave.
+   ** Virtual from FairModule.
+   **/
+  virtual void ConstructGeometry();
+
+
+  /** @brief Action at end of event
+   **
+   ** Short status log and Reset().
+   ** Virtual from FairDetector.
+   **/
+  virtual void EndOfEvent();
+
+
+  /**  @brief Initialisation
+	 **
+	 ** The output array is created. Then, the base
+	 ** class method FairDetector::Initialize() is called.
+	 ** Virtual from FairDetector.
+	 **/
+  virtual void Initialize();
+
+
+  /** Accessor to the hit collection **/
+  /** @brief Get output array of CbmFsdPoints
+   ** @param iColl Number of collection. Must be zero, since there is only one.
+   ** @value Pointer to TClonesArray with CbmFsdPoints
+   **/
+  virtual TClonesArray* GetCollection(Int_t iColl) const { return (iColl ? nullptr : fFsdPoints); }
+
+
+  /** @brief Screen log
+   ** Prints current number of StsPoints in array.
+   ** Virtual from TObject.
+   **/
+  virtual void Print(Option_t* opt = "") const;
+
+
+  /** @brief Stepping action
+   ** @param volume  Pointer to the current volume
+   ** @value Always kTRUE
+   **
+   ** Defines the action to be taken when a step is inside the
+   ** active volume. Creates CbmFsdPoints and adds them to the
+   ** collection.
+   ** Abstract from FairDetector.
+   **/
+  virtual Bool_t ProcessHits(FairVolume* volume = 0);
+
+
+  /** @brief Register the output array
+   **
+   ** Abstract from FairDetector.
+   **/
+  virtual void Register() { FairRootManager::Instance()->Register("FsdPoint", GetName(), fFsdPoints, kTRUE); }
+
+
+  /** @brief Clear output array
+   **
+   ** Abstract from FairDetector.
+   **/
+  virtual void Reset() { fFsdPoints->Delete(); };
+
+private:
+  TClonesArray* fFsdPoints = nullptr;  //! Output array
+
+  /** Track information to be temporarily stored **/
+  Int_t fTrackID = -1;     //!  track index
+  Int_t fAddress = -1;     //!  address (module and layer)
+  TLorentzVector fPos {};  //!  position
+  TLorentzVector fMom {};  //!  momentum
+  Double_t fTime   = -1.;  //!  time
+  Double_t fLength = -1.;  //!  length
+  Double_t fEloss  = -1.;  //!  energy loss
+
+  /** @brief Register all sensitive volumes
+   ** @param node Pointer to start node
+   **
+   ** Starting from the specified node, the entire node tree is expanded
+   ** and all volumes which satisfy the CheckIfSensitive() criterion
+   ** are added to the list of sensitive volumes.
+   */
+  void RegisterSensitiveVolumes(TGeoNode* node);
+
+  ClassDef(CbmFsdMC, 1)
+};
+
+
+#endif  //? CBMFSDMC_H
diff --git a/sim/detectors/fsd/CbmFsdSimLinkDef.h b/sim/detectors/fsd/CbmFsdSimLinkDef.h
new file mode 100644
index 0000000000..50dd400024
--- /dev/null
+++ b/sim/detectors/fsd/CbmFsdSimLinkDef.h
@@ -0,0 +1,15 @@
+/* Copyright (C) 2023 Physikalisches Institut Eberhard Karls Universitaet Tuebingen, Tuebingen
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Florian Uhlig, Lukas Chlad [committer] */
+
+#ifdef __CINT__
+
+#pragma link off all globals;
+#pragma link off all classes;
+#pragma link off all functions;
+
+#pragma link C++ class CbmFsdMC;
+#pragma link C++ class CbmDigitize < CbmFsdDigi> + ;
+#pragma link C++ class CbmFsdDigitize + ;
+
+#endif
diff --git a/sim/response/CMakeLists.txt b/sim/response/CMakeLists.txt
index 88992d439d..3594a5d75b 100644
--- a/sim/response/CMakeLists.txt
+++ b/sim/response/CMakeLists.txt
@@ -24,6 +24,7 @@ set(PRIVATE_DEPENDENCIES
   CbmMuchSim
   CbmMvdSim
   CbmPsdSim
+  CbmFsdSim
   CbmRichSim
   CbmSimSteer
   CbmStsSim
diff --git a/sim/response/base/CbmDigitization.cxx b/sim/response/base/CbmDigitization.cxx
index d59f860a08..5cbc82caca 100644
--- a/sim/response/base/CbmDigitization.cxx
+++ b/sim/response/base/CbmDigitization.cxx
@@ -11,6 +11,7 @@
 
 #include "CbmBmonDigitize.h"
 #include "CbmDigitizationSource.h"
+#include "CbmFsdDigitize.h"
 #include "CbmMuchDigitizeGem.h"
 #include "CbmMvdDigitizer.h"
 #include "CbmPsdSimpleDigitizer.h"
@@ -183,6 +184,11 @@ Int_t CbmDigitization::CreateDefaultDigitizers()
         ss << "TOF ";
         nDigis++;
         break;
+      case ECbmModuleId::kFsd:
+        fDigitizers[system]->SetDigitizer(new CbmFsdDigitize());
+        ss << "FSD ";
+        nDigis++;
+        break;
       case ECbmModuleId::kPsd:
         fDigitizers[system]->SetDigitizer(new CbmPsdSimpleDigitizer());
         ss << "PSD ";
@@ -291,6 +297,7 @@ void CbmDigitization::DefaultInit()
   // --- Add default parameter files for TRD and TOF
   TString tofGeo = GetGeoTag(ECbmModuleId::kTof, gGeoManager);
   TString trdGeo = GetGeoTag(ECbmModuleId::kTrd, gGeoManager);
+  TString fsdGeo = GetGeoTag(ECbmModuleId::kFsd, gGeoManager);
   TString srcDir = gSystem->Getenv("VMCWORKDIR");  // top source directory
   TString parFile;
   if (trdGeo.Length() > 0) {
@@ -307,6 +314,10 @@ void CbmDigitization::DefaultInit()
     parFile = srcDir + "/parameters/tof/tof_" + tofGeo + ".digibdf.par";
     AddParameterAsciiFile(parFile);
   }
+  if (fsdGeo.Length() > 0) {
+    parFile = srcDir + "/parameters/fsd/fsd_" + fsdGeo + ".digi.par";
+    AddParameterAsciiFile(parFile);
+  }
 
   delete rtdb;
   delete parIoRoot;
@@ -484,6 +495,7 @@ void CbmDigitization::SetDefaultBranches()
   fDigitizers[ECbmModuleId::kMuch] = new CbmDigitizeInfo(ECbmModuleId::kMuch, "MuchPoint");
   fDigitizers[ECbmModuleId::kTrd]  = new CbmDigitizeInfo(ECbmModuleId::kTrd, "TrdPoint");
   fDigitizers[ECbmModuleId::kTof]  = new CbmDigitizeInfo(ECbmModuleId::kTof, "TofPoint");
+  fDigitizers[ECbmModuleId::kFsd]  = new CbmDigitizeInfo(ECbmModuleId::kFsd, "FsdPoint");
   fDigitizers[ECbmModuleId::kPsd]  = new CbmDigitizeInfo(ECbmModuleId::kPsd, "PsdPoint");
   fDigitizers[ECbmModuleId::kT0]   = new CbmDigitizeInfo(ECbmModuleId::kT0, "");
 }
diff --git a/sim/transport/base/CbmMCEventFilter.cxx b/sim/transport/base/CbmMCEventFilter.cxx
index 13eac68091..963788b7fe 100644
--- a/sim/transport/base/CbmMCEventFilter.cxx
+++ b/sim/transport/base/CbmMCEventFilter.cxx
@@ -82,6 +82,7 @@ InitStatus CbmMCEventFilter::Init()
   GetBranch(ECbmDataType::kMuchPoint);
   GetBranch(ECbmDataType::kTrdPoint);
   GetBranch(ECbmDataType::kTofPoint);
+  GetBranch(ECbmDataType::kFsdPoint);
   GetBranch(ECbmDataType::kPsdPoint);
 
   return kSUCCESS;
@@ -118,6 +119,7 @@ TString CbmMCEventFilter::GetBranchName(ECbmDataType type) const
     case ECbmDataType::kMuchPoint: name = "MuchPoint"; break;
     case ECbmDataType::kTrdPoint: name = "TrdPoint"; break;
     case ECbmDataType::kTofPoint: name = "TofPoint"; break;
+    case ECbmDataType::kFsdPoint: name = "FsdPoint"; break;
     case ECbmDataType::kPsdPoint: name = "PsdPoint"; break;
     default: name = ""; break;
   }
@@ -159,6 +161,7 @@ std::string CbmMCEventFilter::Statistics() const
   if (fData.at(ECbmDataType::kMuchPoint)) ss << "MUCH " << GetNofData(ECbmDataType::kMuchPoint) << "  ";
   if (fData.at(ECbmDataType::kTrdPoint)) ss << "TRD " << GetNofData(ECbmDataType::kTrdPoint) << "  ";
   if (fData.at(ECbmDataType::kTofPoint)) ss << "TOF " << GetNofData(ECbmDataType::kTofPoint) << "  ";
+  if (fData.at(ECbmDataType::kFsdPoint)) ss << "FSD " << GetNofData(ECbmDataType::kFsdPoint) << "  ";
   if (fData.at(ECbmDataType::kPsdPoint)) ss << "PSD " << GetNofData(ECbmDataType::kPsdPoint) << "  ";
 
   return ss.str();
diff --git a/sim/transport/base/CbmStack.cxx b/sim/transport/base/CbmStack.cxx
index 331496716d..c08d1a33f9 100644
--- a/sim/transport/base/CbmStack.cxx
+++ b/sim/transport/base/CbmStack.cxx
@@ -221,7 +221,7 @@ void CbmStack::FillTrackArray()
       fIndexMap[indexP] = indexT;
 
       // Set the number of points in the detectors for this track
-      for (ECbmModuleId detector = ECbmModuleId::kRef; detector <= ECbmModuleId::kPsd; ++detector) {
+      for (ECbmModuleId detector = ECbmModuleId::kRef; detector <= ECbmModuleId::kNofSystems; ++detector) {
         auto it = fPointsMap.find(make_pair(indexP, detector));
         if (it != fPointsMap.end()) track->SetNPoints(detector, it->second);
       }  //# detectors
diff --git a/sim/transport/geosetup/CMakeLists.txt b/sim/transport/geosetup/CMakeLists.txt
index b850211673..bf2b2155a8 100644
--- a/sim/transport/geosetup/CMakeLists.txt
+++ b/sim/transport/geosetup/CMakeLists.txt
@@ -34,6 +34,7 @@ set(PRIVATE_DEPENDENCIES
   CbmMvdSim
   CbmPassive
   CbmPsdSim
+  CbmFsdSim
   CbmRichSim
   CbmSimBase
   CbmStsSim
diff --git a/sim/transport/geosetup/CbmGeoSetupDbProvider.cxx b/sim/transport/geosetup/CbmGeoSetupDbProvider.cxx
index ca4943f4fd..0900029093 100644
--- a/sim/transport/geosetup/CbmGeoSetupDbProvider.cxx
+++ b/sim/transport/geosetup/CbmGeoSetupDbProvider.cxx
@@ -27,9 +27,9 @@ namespace
   std::map<Int_t, ECbmModuleId> dbToCbmModuleIdMap = {
     {0, ECbmModuleId::kCave},       {1, ECbmModuleId::kMagnet},    {2, ECbmModuleId::kPipe},
     {4, ECbmModuleId::kMvd},        {8, ECbmModuleId::kSts},       {16, ECbmModuleId::kRich},
-    {32, ECbmModuleId::kTrd},       {64, ECbmModuleId::kTof},      {128, ECbmModuleId::kPsd},
+    {32, ECbmModuleId::kTrd},       {64, ECbmModuleId::kTof},      {128, ECbmModuleId::kFsd},
     {256, ECbmModuleId::kPlatform}, {512, ECbmModuleId::kMuch},    {1024, ECbmModuleId::kHodo},
-    {2048, ECbmModuleId::kTarget},  {4096, ECbmModuleId::kShield},
+    {2048, ECbmModuleId::kTarget},  {4096, ECbmModuleId::kShield}, {8192, ECbmModuleId::kPsd},
   };
 
   /// Default path to field directory
diff --git a/sim/transport/geosetup/CbmGeoSetupProvider.cxx b/sim/transport/geosetup/CbmGeoSetupProvider.cxx
index c7070dab7d..8a76d708a5 100644
--- a/sim/transport/geosetup/CbmGeoSetupProvider.cxx
+++ b/sim/transport/geosetup/CbmGeoSetupProvider.cxx
@@ -25,6 +25,7 @@
 #include "FairRunSim.h"
 #include <Logger.h>
 //#include "CbmEcal.h"
+#include "CbmFsdMC.h"
 #include "CbmPsdMC.h"
 //#include "CbmShield.h"
 #include "CbmPlatform.h"
@@ -44,7 +45,7 @@ namespace
       ECbmModuleId::kCave, ECbmModuleId::kMagnet, ECbmModuleId::kPipe, ECbmModuleId::kTarget,
       //      ECbmModuleId::kMvd, ECbmModuleId::kSts, ECbmModuleId::kRich, ECbmModuleId::kMuch, ECbmModuleId::kTrd, ECbmModuleId::kTof, ECbmModuleId::kEcal, ECbmModuleId::kPsd,
       ECbmModuleId::kMvd, ECbmModuleId::kSts, ECbmModuleId::kRich, ECbmModuleId::kMuch, ECbmModuleId::kTrd,
-      ECbmModuleId::kTof, ECbmModuleId::kPsd,
+      ECbmModuleId::kTof, ECbmModuleId::kPsd, ECbmModuleId::kFsd,
       //      ECbmModuleId::kHodo, ECbmModuleId::kShield, ECbmModuleId::kPlatform };
       ECbmModuleId::kHodo, ECbmModuleId::kPlatform};
   }
@@ -106,7 +107,7 @@ void CbmGeoSetupProvider::RegisterSetup()
     for (auto& string : _geom) {
 
       LOG(info) << "-I- RegisterSetup: Registering " << modulName << " " << _tag[counter]
-                << ((moduleId >= ECbmModuleId::kMvd && moduleId <= ECbmModuleId::kPsd)
+                << ((moduleId >= ECbmModuleId::kMvd && moduleId <= ECbmModuleId::kNofSystems)
                       ? (isActive ? " -ACTIVE- " : " - INACTIVE- ")
                       : "")
                 << " using " << string;
@@ -131,8 +132,9 @@ void CbmGeoSetupProvider::RegisterSetup()
           fairModule = new CbmTof("TOF", isActive);
           break;
           //        case ECbmModuleId::kEcal:     fairModule = new CbmEcal("Ecal", isActive); break;
-        case ECbmModuleId::kPsd:
-          fairModule = new CbmPsdMC(isActive);
+        case ECbmModuleId::kPsd: fairModule = new CbmPsdMC(isActive); break;
+        case ECbmModuleId::kFsd:
+          fairModule = new CbmFsdMC(isActive);
           break;
           //        case ECbmModuleId::kShield:   fairModule = new CbmShield("SHIELD"); break;
         case ECbmModuleId::kPlatform: fairModule = new CbmPlatform("PLATFORM"); break;
diff --git a/sim/transport/geosetup/CbmGeoSetupRepoProvider.cxx b/sim/transport/geosetup/CbmGeoSetupRepoProvider.cxx
index b4c5af593e..cde422eedc 100644
--- a/sim/transport/geosetup/CbmGeoSetupRepoProvider.cxx
+++ b/sim/transport/geosetup/CbmGeoSetupRepoProvider.cxx
@@ -68,6 +68,7 @@ namespace
     {ECbmModuleId::kTof, {"tofGeoTag", "tof", "tof", "TOF"}},
     //    { ECbmModuleId::kEcal, {"ecalGeoTag", "ecal", "ecal", "ECAL"} },
     {ECbmModuleId::kPsd, {"psdGeoTag", "psd", "psd", "PSD"}},
+    {ECbmModuleId::kFsd, {"fsdGeoTag", "fsd", "fsd", "FSD"}},
     {ECbmModuleId::kHodo, {"hodoGeoTag", "sts", "sts", "HODO"}},
 
     {ECbmModuleId::kShield, {"shieldGeoTag", "much", "shield", "SHIELD"}},
diff --git a/sim/transport/steer/CbmTransportConfig.cxx b/sim/transport/steer/CbmTransportConfig.cxx
index bcf54bcbc1..85384e8cdd 100644
--- a/sim/transport/steer/CbmTransportConfig.cxx
+++ b/sim/transport/steer/CbmTransportConfig.cxx
@@ -67,6 +67,7 @@ CbmTransportConfig::TagSet_t CbmTransportConfig::GetValidationTags()
           "geometry.subsystems.trd",
           "geometry.subsystems.tof",
           "geometry.subsystems.psd",
+          "geometry.subsystems.fsd",
           "geometry.subsystems.hodo",
           "geometry.subsystems.shield",
           "geometry.subsystems.platform",
-- 
GitLab