From 89dece604a2a697eb333355fd3220605e727075e Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Tue, 20 May 2025 00:29:55 +0800
Subject: [PATCH 01/74] feat(DetSimAna): add ChronoAnaElemTool for simulation
 time analysis

- Add new ChronoAnaElemTool class to track simulation time per subdetector and particle type
- Implement ntuple recording for time metrics
- Include initialization of subdetector names and logging
- Update CMakeLists.txt to include new source file
---
 Simulation/DetSimAna/CMakeLists.txt           |   1 +
 .../DetSimAna/src/ChronoAnaElemTool.cpp       | 112 ++++++++++++++++++
 Simulation/DetSimAna/src/ChronoAnaElemTool.h  |  72 +++++++++++
 3 files changed, 185 insertions(+)
 create mode 100644 Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
 create mode 100644 Simulation/DetSimAna/src/ChronoAnaElemTool.h

diff --git a/Simulation/DetSimAna/CMakeLists.txt b/Simulation/DetSimAna/CMakeLists.txt
index f3daa7f6..c41361dd 100644
--- a/Simulation/DetSimAna/CMakeLists.txt
+++ b/Simulation/DetSimAna/CMakeLists.txt
@@ -2,6 +2,7 @@
 gaudi_add_module(DetSimAna
                  SOURCES src/Edm4hepWriterAnaElemTool.cpp
                          src/ExampleAnaDoseElemTool.cpp
+                         src/ChronoAnaElemTool.cpp
                  LINK DetSimInterface
                       DetSimSDLib
                       ${DD4hep_COMPONENT_LIBRARIES} 
diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
new file mode 100644
index 00000000..1eb99371
--- /dev/null
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -0,0 +1,112 @@
+#include "ChronoAnaElemTool.h"
+
+#include "G4Event.hh"
+#include "G4Step.hh"
+#include "G4String.hh"
+#include "GaudiKernel/IChronoSvc.h"
+#include "GaudiKernel/INTupleSvc.h"
+
+#include "DDG4/Geant4Data.h"
+#include "G4TransportationManager.hh"
+#include "GaudiKernel/ChronoEntity.h"
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/NTuple.h"
+#include <array>
+#include <cassert>
+#include <cerrno>
+#include <cstddef>
+#include <string>
+
+#include "GaudiAlg/GaudiAlgorithm.h"
+#include "GaudiKernel/NTuple.h"
+
+DECLARE_COMPONENT(ChronoAnaElemTool)
+
+StatusCode ChronoAnaElemTool::initNtuple() {
+  bool success = true;
+  { // Subdet array
+    NTuplePtr nt(m_ntupleSvc, "ChronoMetrics/SimulationTime/SubDet");
+    if (!nt) {
+      nt = m_ntupleSvc->book("ChronoMetrics/SimulationTime/SubDet",
+                             CLID_ColumnWiseTuple, "SimTime per Subdetector");
+      if (!nt) {
+        return StatusCode::FAILURE;
+      }
+      success &= nt->addItem("EventNumber", m_eventNumber);
+      success &= nt->addItem("TimePerSubdet", m_subdetNames.size(),
+                             m_timePerSubdet, 0.0, 1e6);
+    }
+    m_subdetTuple = nt;
+  }
+
+  { // Particle array
+    NTuplePtr nt(m_ntupleSvc, "ChronoMetrics/SimulationTime/PdgIdTypes");
+    if (!nt) {
+      nt = m_ntupleSvc->book("ChronoMetrics/SimulationTime/PdgIdTypes",
+                             CLID_ColumnWiseTuple, "SimTime per Particle ID");
+      if (!nt) {
+        return StatusCode::FAILURE;
+      }
+      success &= nt->addItem("EventNumber", m_eventNumber);
+      // record the number of different PDG ID
+      success &= nt->addItem("NumPdgIdTypes", m_nPdgTypes, 0U, MAX_PDG_TYPES);
+      // record the PDG ID and time spend on each type of particle
+      success &= nt->addIndexedItem("PdgIdTypes", m_nPdgTypes, m_pdgIds);
+      success &=
+          nt->addIndexedItem("TimePerPdgIdType", m_nPdgTypes, m_timePerPdgType);
+    }
+    m_pidTuple = nt;
+  }
+  if (!m_subdetTuple || !m_pidTuple || !success) {
+    return StatusCode::FAILURE;
+  } else {
+    return StatusCode::SUCCESS;
+  }
+}
+
+StatusCode ChronoAnaElemTool::initSubdetNames() {
+
+  // prepare to write out subdet names
+  m_logFileStream.open(m_writeLogFileName.value(),
+                       std::ios::out | std::ios::trunc);
+  if (m_logFileStream.fail()) {
+    error() << "Failed to open output file: " << m_writeLogFileName.value()
+            << endmsg;
+    return StatusCode::FAILURE;
+  }
+
+  const auto &children = m_geomSvc->getDD4HepGeo().children();
+  size_t index = 0;
+  for (const auto &[name, subdet] : children) {
+    if (subdet.id() < 0) {
+      continue;
+    }
+    m_subdetNames.push_back(name);
+    m_logFileStream << "# " << index << "  " << name << '\n';
+    ++index;
+  }
+
+  m_logFileStream.close();
+  return StatusCode::SUCCESS;
+}
+
+StatusCode ChronoAnaElemTool::initialize() {
+  StatusCode sc;
+
+  // Get services
+  m_chronoStatSvc = service<IChronoStatSvc>("ChronoStatSvc");
+  m_ntupleSvc = service<INTupleSvc>("NTupleSvc");
+  m_geomSvc = service<IGeomSvc>("GeomSvc");
+  if (!m_chronoStatSvc || !m_ntupleSvc || !m_geomSvc) {
+    return StatusCode::FAILURE;
+  }
+
+  // init subdet names and number
+  sc = initSubdetNames();
+  if (sc.isFailure()) {
+    return sc;
+  }
+  // init ntuple for record sim time over subdet and pid
+  sc = initNtuple();
+  return sc;
+}
diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.h b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
new file mode 100644
index 00000000..dce1928b
--- /dev/null
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
@@ -0,0 +1,72 @@
+#ifndef ChronoAnaElemTool_h
+#define ChronoAnaElemTool_h
+
+#include <array>
+#include <chrono>
+#include <cstddef>
+#include <ratio>
+#include <string>
+#include <vector>
+
+#include "DetInterface/IGeomSvc.h"
+#include "DetSimInterface/IAnaElemTool.h"
+#include "G4VTouchable.hh"
+#include "GaudiKernel/AlgTool.h"
+#include "GaudiKernel/IChronoStatSvc.h"
+#include "GaudiKernel/INTupleSvc.h"
+#include "GaudiKernel/NTuple.h"
+
+class ChronoAnaElemTool : public extends<AlgTool, IAnaElemTool> {
+
+public:
+  using extends::extends;
+
+  // Run
+  virtual void BeginOfRunAction(const G4Run *) override;
+
+  // Event
+  virtual void BeginOfEventAction(const G4Event *) override;
+  virtual void EndOfEventAction(const G4Event *) override;
+
+  // Stepping
+  virtual void UserSteppingAction(const G4Step *) override;
+
+  /// Overriding initialize and finalize
+  StatusCode initialize() override;
+
+private:
+  // methods
+  StatusCode initNtuple();
+  StatusCode initSubdetNames();
+
+  // variables
+  SmartIF<IChronoStatSvc> m_chronoStatSvc;
+  SmartIF<INTupleSvc> m_ntupleSvc;
+  SmartIF<IGeomSvc> m_geomSvc;
+
+  Gaudi::Property<std::string> m_writeNtupleFileName{
+      this, "WriteNtupleFileName", "chrono_stat.root"};
+  Gaudi::Property<std::string> m_writeLogFileName{this, "WriteLogFileName",
+                                                  "chrono_stat.log"};
+  std::ofstream    m_logFileStream;
+
+  NTuple::Tuple *m_subdetTuple{nullptr};
+  NTuple::Tuple *m_pidTuple{nullptr};
+
+  std::chrono::duration<double, std::micro> m_overhead_time_counter =
+      std::chrono::duration<double, std::micro>::zero();
+
+  static const unsigned MAX_PDG_TYPES = 256;
+  std::vector<std::string> m_subdetNames;
+
+  NTuple::Item<long> m_eventNumber;
+  NTuple::Array<double> m_timePerSubdet;
+  // type number of different PDG ID
+  NTuple::Item<unsigned> m_nPdgTypes;
+  // array of pdg ids
+  NTuple::Array<int> m_pdgIds;
+  // time spend on each type of particle
+  NTuple::Array<double> m_timePerPdgType;
+};
+
+#endif
-- 
GitLab


From 464e98d663d7bfc01b046b99b0dccd5280ba5110 Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Tue, 20 May 2025 16:40:59 +0800
Subject: [PATCH 02/74] feat(ChronoAnaElemTool): add event timing tracking and
 overhead measurement

- Add tracking of event simulation time and overhead time in ntuples
- Implement per-PDGID and per-subdetector timing measurements
- Include overhead time measurement for subdetector name lookup
- Remove unused includes and clean up header file
---
 .../DetSimAna/src/ChronoAnaElemTool.cpp       | 143 ++++++++++++++++++
 Simulation/DetSimAna/src/ChronoAnaElemTool.h  |  17 ++-
 2 files changed, 154 insertions(+), 6 deletions(-)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
index 1eb99371..6ff34ef8 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -11,6 +11,7 @@
 #include "GaudiKernel/ChronoEntity.h"
 #include "GaudiKernel/MsgStream.h"
 #include "GaudiKernel/NTuple.h"
+#include <G4Track.hh>
 #include <array>
 #include <cassert>
 #include <cerrno>
@@ -33,6 +34,9 @@ StatusCode ChronoAnaElemTool::initNtuple() {
         return StatusCode::FAILURE;
       }
       success &= nt->addItem("EventNumber", m_eventNumber);
+      success &= nt->addItem("EventSimTime", m_eventSimTime); // microseconds
+      success &=
+          nt->addItem("EventOverheadTime", m_eventOverheadTime); // microseconds
       success &= nt->addItem("TimePerSubdet", m_subdetNames.size(),
                              m_timePerSubdet, 0.0, 1e6);
     }
@@ -48,6 +52,8 @@ StatusCode ChronoAnaElemTool::initNtuple() {
         return StatusCode::FAILURE;
       }
       success &= nt->addItem("EventNumber", m_eventNumber);
+      success &= nt->addItem("EventSimTime", m_eventSimTime);
+      success &= nt->addItem("EventOverheadTime", m_eventOverheadTime);
       // record the number of different PDG ID
       success &= nt->addItem("NumPdgIdTypes", m_nPdgTypes, 0U, MAX_PDG_TYPES);
       // record the PDG ID and time spend on each type of particle
@@ -110,3 +116,140 @@ StatusCode ChronoAnaElemTool::initialize() {
   sc = initNtuple();
   return sc;
 }
+
+void ChronoAnaElemTool::BeginOfEventAction(const G4Event *) {
+  // start total simulation timing
+  m_subdetTuple->reset();
+  m_pidTuple->reset();
+  m_overhead_time_counter = std::chrono::duration<double, std::micro>::zero();
+  m_chronoStatSvc->chronoStart("Simulation");
+}
+
+void ChronoAnaElemTool::EndOfEventAction(const G4Event *event) {
+  m_chronoStatSvc->chronoStop("SubDet/" +
+                              m_subdetNames[m_currentSubdetNameIdx]);
+  m_chronoStatSvc->chronoStop("PDGID/" + std::to_string(m_currentPdgId));
+  m_chronoStatSvc->chronoStop("Simulation");
+
+  // save ntuple for record sim time over subdet and pid
+  m_eventNumber = event->GetEventID();
+  m_eventSimTime = m_chronoStatSvc->chronoDelta("Simulation", IChronoSvc::USER);
+  m_eventOverheadTime = m_overhead_time_counter.count();
+
+  // fill ntuple for record sim time over subdet
+  for (unsigned i = 0; i < m_subdetNames.size(); ++i) {
+    auto s = m_chronoStatSvc->chrono("SubDet/" + m_subdetNames[i]);
+    m_timePerSubdet[i] = s->uTotalTime();
+  }
+
+  // fill ntuple for record sim time over pid
+  for (unsigned i = 0; i < m_nPdgTypes; ++i) {
+    auto s = m_chronoStatSvc->chrono("PDGID/" + std::to_string(m_pdgIds[i]));
+    m_timePerPdgType[i] = s->uTotalTime();
+  }
+
+  // write ntuple to file
+  StatusCode sc = m_subdetTuple->writeRecord();
+  if (sc.isFailure()) {
+    error() << "Failed to write m_subdetTuple record for event "
+            << m_eventNumber << endmsg;
+  }
+  sc = m_pidTuple->writeRecord();
+  if (sc.isFailure()) {
+    error() << "Failed to write m_pidTuple record for event " << m_eventNumber
+            << endmsg;
+  }
+}
+
+inline void ChronoAnaElemTool::recordTimePerPdgIdType(const G4Step *aStep) {
+  auto track = aStep->GetTrack();
+  if (track->GetCurrentStepNumber() != 1) {
+    // not the first step, skip
+    return;
+  }
+  // Process Particle ID Timer
+  int pdg = track->GetParticleDefinition()->GetPDGEncoding();
+  if (pdg == m_currentPdgId) {
+    // pdgid not changed, skip
+    return;
+  }
+
+  // If there was a previous PDG timer, stop it
+  if (m_currentPdgId != 0) {
+    debug() << "Stopping PDG[" << m_currentPdgId << "] timer -> Starting PDG["
+            << pdg << "]" << endmsg;
+    m_chronoStatSvc->chronoStop("PDGID/" + std::to_string(m_currentPdgId));
+  }
+
+  // Find if this PDG is already recorded
+  if (std::find(m_pdgIds.begin(), m_pdgIds.end(), pdg) == m_pdgIds.end()) {
+    // new PDG, record it and start timer
+    m_pdgIds[m_nPdgTypes] = pdg;
+    ++m_nPdgTypes;
+  }
+
+  // Update current PDG and start new PDG timer
+  m_currentPdgId = pdg;
+  m_chronoStatSvc->chronoStart("PDGID/" + std::to_string(m_currentPdgId));
+}
+
+inline void ChronoAnaElemTool::recordTimePerSubdet(const G4Step *aStep) {
+  if (!aStep->IsFirstStepInVolume()) {
+    return;
+  }
+  // Enter the first step in a new volume
+  // calculate the time spent in get subdet name
+  auto start = std::chrono::high_resolution_clock::now();
+  const auto newNameIdx = getSubdetNameIdx(aStep->GetTrack()->GetTouchable());
+  auto end = std::chrono::high_resolution_clock::now();
+  m_overhead_time_counter +=
+      std::chrono::duration<double, std::micro>(end - start);
+
+  if (newNameIdx == m_currentSubdetNameIdx) {
+    // same subdet, skip this step
+    return;
+  }
+  // If we had a previous subdet, stop its timer
+  if (m_currentSubdetNameIdx != -1) {
+    debug() << "Exiting " << m_subdetNames[m_currentSubdetNameIdx]
+            << " -> Entering " << m_subdetNames[newNameIdx] << endmsg;
+    m_chronoStatSvc->chronoStop("SubDet/" +
+                                m_subdetNames[m_currentSubdetNameIdx]);
+  }
+
+  // Update state and start the new timer
+  m_currentSubdetNameIdx = newNameIdx;
+  m_chronoStatSvc->chronoStart("SubDet/" +
+                               m_subdetNames[m_currentSubdetNameIdx]);
+}
+
+void ChronoAnaElemTool::UserSteppingAction(const G4Step *aStep) {
+  recordTimePerPdgIdType(aStep);
+  recordTimePerSubdet(aStep);
+}
+
+inline int ChronoAnaElemTool::getSubdetNameIdx(const G4VTouchable *touchable) {
+  auto historyDepthNum = touchable->GetHistoryDepth();
+  for (int depth = 0; depth <= historyDepthNum; depth++) {
+    auto vol = touchable->GetVolume(depth);
+    if (!vol) {
+      error() << "Volume is nullptr during finding " << depth << " in "
+              << historyDepthNum << endmsg;
+      return -1;
+    }
+    auto volName = vol->GetName();
+
+    // Check if the volume name contains the subdetector name
+    auto it = std::find_if(m_subdetNames.begin(), m_subdetNames.end(),
+                           [&volName](const std::string &s) {
+                             return volName.find(s) != std::string::npos;
+                           });
+    // If the volume name contains the subdetector name, return its index
+    if (it != m_subdetNames.end()) {
+      return static_cast<int>(std::distance(m_subdetNames.begin(), it));
+    }
+  }
+  error() << "Volume is nullptr after find history " << historyDepthNum
+          << endmsg;
+  return -1;
+}
\ No newline at end of file
diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.h b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
index dce1928b..5d337f48 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.h
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
@@ -1,9 +1,7 @@
 #ifndef ChronoAnaElemTool_h
 #define ChronoAnaElemTool_h
 
-#include <array>
 #include <chrono>
-#include <cstddef>
 #include <ratio>
 #include <string>
 #include <vector>
@@ -21,9 +19,6 @@ class ChronoAnaElemTool : public extends<AlgTool, IAnaElemTool> {
 public:
   using extends::extends;
 
-  // Run
-  virtual void BeginOfRunAction(const G4Run *) override;
-
   // Event
   virtual void BeginOfEventAction(const G4Event *) override;
   virtual void EndOfEventAction(const G4Event *) override;
@@ -38,6 +33,9 @@ private:
   // methods
   StatusCode initNtuple();
   StatusCode initSubdetNames();
+  inline void recordTimePerPdgIdType(const G4Step *aStep);
+  inline void recordTimePerSubdet(const G4Step *aStep);
+  inline int getSubdetNameIdx(const G4VTouchable *touchable);
 
   // variables
   SmartIF<IChronoStatSvc> m_chronoStatSvc;
@@ -48,7 +46,7 @@ private:
       this, "WriteNtupleFileName", "chrono_stat.root"};
   Gaudi::Property<std::string> m_writeLogFileName{this, "WriteLogFileName",
                                                   "chrono_stat.log"};
-  std::ofstream    m_logFileStream;
+  std::ofstream m_logFileStream;
 
   NTuple::Tuple *m_subdetTuple{nullptr};
   NTuple::Tuple *m_pidTuple{nullptr};
@@ -58,9 +56,16 @@ private:
 
   static const unsigned MAX_PDG_TYPES = 256;
   std::vector<std::string> m_subdetNames;
+  int m_currentSubdetNameIdx = -1;
+  int m_currentPdgId = 0;
 
   NTuple::Item<long> m_eventNumber;
+  // User space sim time
+  NTuple::Item<double> m_eventOverheadTime;
+  NTuple::Item<double> m_eventSimTime;
+
   NTuple::Array<double> m_timePerSubdet;
+
   // type number of different PDG ID
   NTuple::Item<unsigned> m_nPdgTypes;
   // array of pdg ids
-- 
GitLab


From 2c0cdad6751098f7d97d27e75bc1afd65b18f514 Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Tue, 20 May 2025 17:15:19 +0800
Subject: [PATCH 03/74] chore: sort import

---
 .../DetSimAna/src/ChronoAnaElemTool.cpp       | 24 +++++++++----------
 Simulation/DetSimAna/src/ChronoAnaElemTool.h  |  3 +--
 2 files changed, 12 insertions(+), 15 deletions(-)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
index 6ff34ef8..038f4d02 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -1,25 +1,23 @@
+#include <array>
+#include <cassert>
+#include <cerrno>
+#include <cstddef>
+#include <string>
+
 #include "ChronoAnaElemTool.h"
 
+#include "DDG4/Geant4Data.h"
 #include "G4Event.hh"
 #include "G4Step.hh"
 #include "G4String.hh"
-#include "GaudiKernel/IChronoSvc.h"
-#include "GaudiKernel/INTupleSvc.h"
-
-#include "DDG4/Geant4Data.h"
+#include "G4Track.hh"
 #include "G4TransportationManager.hh"
+#include "GaudiAlg/GaudiAlgorithm.h"
 #include "GaudiKernel/ChronoEntity.h"
+#include "GaudiKernel/IChronoSvc.h"
+#include "GaudiKernel/INTupleSvc.h"
 #include "GaudiKernel/MsgStream.h"
 #include "GaudiKernel/NTuple.h"
-#include <G4Track.hh>
-#include <array>
-#include <cassert>
-#include <cerrno>
-#include <cstddef>
-#include <string>
-
-#include "GaudiAlg/GaudiAlgorithm.h"
-#include "GaudiKernel/NTuple.h"
 
 DECLARE_COMPONENT(ChronoAnaElemTool)
 
diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.h b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
index 5d337f48..c2f24be5 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.h
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
@@ -8,6 +8,7 @@
 
 #include "DetInterface/IGeomSvc.h"
 #include "DetSimInterface/IAnaElemTool.h"
+
 #include "G4VTouchable.hh"
 #include "GaudiKernel/AlgTool.h"
 #include "GaudiKernel/IChronoStatSvc.h"
@@ -42,8 +43,6 @@ private:
   SmartIF<INTupleSvc> m_ntupleSvc;
   SmartIF<IGeomSvc> m_geomSvc;
 
-  Gaudi::Property<std::string> m_writeNtupleFileName{
-      this, "WriteNtupleFileName", "chrono_stat.root"};
   Gaudi::Property<std::string> m_writeLogFileName{this, "WriteLogFileName",
                                                   "chrono_stat.log"};
   std::ofstream m_logFileStream;
-- 
GitLab


From 64f8f8873a6cf6063ff5809c35a2bd0ce9b884fa Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Tue, 20 May 2025 17:33:13 +0800
Subject: [PATCH 04/74] fix: add skip first n event

---
 Simulation/DetSimAna/src/ChronoAnaElemTool.cpp | 17 ++++++++++++++---
 Simulation/DetSimAna/src/ChronoAnaElemTool.h   |  2 ++
 2 files changed, 16 insertions(+), 3 deletions(-)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
index 038f4d02..6e770441 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -115,22 +115,30 @@ StatusCode ChronoAnaElemTool::initialize() {
   return sc;
 }
 
-void ChronoAnaElemTool::BeginOfEventAction(const G4Event *) {
+void ChronoAnaElemTool::BeginOfEventAction(const G4Event *event) {
+  m_eventNumber = event->GetEventID();
+  if (m_eventNumber < m_skipFirstNEvents) {
+    return;
+  }
   // start total simulation timing
   m_subdetTuple->reset();
   m_pidTuple->reset();
+  // set eventNumber so we can use in userstepping
+  m_eventNumber = event->GetEventID();
   m_overhead_time_counter = std::chrono::duration<double, std::micro>::zero();
   m_chronoStatSvc->chronoStart("Simulation");
 }
 
-void ChronoAnaElemTool::EndOfEventAction(const G4Event *event) {
+void ChronoAnaElemTool::EndOfEventAction(const G4Event *) {
+  if (m_eventNumber < m_skipFirstNEvents) {
+    return;
+  }
   m_chronoStatSvc->chronoStop("SubDet/" +
                               m_subdetNames[m_currentSubdetNameIdx]);
   m_chronoStatSvc->chronoStop("PDGID/" + std::to_string(m_currentPdgId));
   m_chronoStatSvc->chronoStop("Simulation");
 
   // save ntuple for record sim time over subdet and pid
-  m_eventNumber = event->GetEventID();
   m_eventSimTime = m_chronoStatSvc->chronoDelta("Simulation", IChronoSvc::USER);
   m_eventOverheadTime = m_overhead_time_counter.count();
 
@@ -222,6 +230,9 @@ inline void ChronoAnaElemTool::recordTimePerSubdet(const G4Step *aStep) {
 }
 
 void ChronoAnaElemTool::UserSteppingAction(const G4Step *aStep) {
+  if (m_eventNumber < m_skipFirstNEvents) {
+    return;
+  }
   recordTimePerPdgIdType(aStep);
   recordTimePerSubdet(aStep);
 }
diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.h b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
index c2f24be5..41f93681 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.h
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
@@ -45,6 +45,8 @@ private:
 
   Gaudi::Property<std::string> m_writeLogFileName{this, "WriteLogFileName",
                                                   "chrono_stat.log"};
+  Gaudi::Property<int> m_skipFirstNEvents{this, "SkipFirstNEvents",
+                                                  5};
   std::ofstream m_logFileStream;
 
   NTuple::Tuple *m_subdetTuple{nullptr};
-- 
GitLab


From c6b92b8207da54ea1f52808380997f6003ef22f0 Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Tue, 20 May 2025 17:39:16 +0800
Subject: [PATCH 05/74] fix: add pWorld

---
 Simulation/DetSimAna/src/ChronoAnaElemTool.cpp | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
index 6e770441..026b9d98 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -79,8 +79,13 @@ StatusCode ChronoAnaElemTool::initSubdetNames() {
     return StatusCode::FAILURE;
   }
 
+  // add pWorld
+  m_subdetNames.push_back("pWorld");
+  size_t index = 1;
+  m_logFileStream << "# " << index << "  " << "pWorld" << '\n';
+
+  // add subdets
   const auto &children = m_geomSvc->getDD4HepGeo().children();
-  size_t index = 0;
   for (const auto &[name, subdet] : children) {
     if (subdet.id() < 0) {
       continue;
-- 
GitLab


From 7da0aec0bf68a4c9c46413617332a9ed6ea7779c Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Tue, 20 May 2025 19:55:01 +0800
Subject: [PATCH 06/74] fix: index start from 0; avoid crash when not triggered

---
 Simulation/DetSimAna/src/ChronoAnaElemTool.cpp | 15 ++++++++++++---
 1 file changed, 12 insertions(+), 3 deletions(-)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
index 026b9d98..6dce41c9 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -81,8 +81,9 @@ StatusCode ChronoAnaElemTool::initSubdetNames() {
 
   // add pWorld
   m_subdetNames.push_back("pWorld");
-  size_t index = 1;
+  size_t index = 0;
   m_logFileStream << "# " << index << "  " << "pWorld" << '\n';
+  ++index;
 
   // add subdets
   const auto &children = m_geomSvc->getDD4HepGeo().children();
@@ -150,13 +151,21 @@ void ChronoAnaElemTool::EndOfEventAction(const G4Event *) {
   // fill ntuple for record sim time over subdet
   for (unsigned i = 0; i < m_subdetNames.size(); ++i) {
     auto s = m_chronoStatSvc->chrono("SubDet/" + m_subdetNames[i]);
-    m_timePerSubdet[i] = s->uTotalTime();
+    if (s != nullptr) {
+      m_timePerSubdet[i] = s->uTotalTime();
+    } else {
+      info() << "SubDet/" << m_subdetNames[i] << " not triggered" << endmsg;
+    }
   }
 
   // fill ntuple for record sim time over pid
   for (unsigned i = 0; i < m_nPdgTypes; ++i) {
     auto s = m_chronoStatSvc->chrono("PDGID/" + std::to_string(m_pdgIds[i]));
-    m_timePerPdgType[i] = s->uTotalTime();
+    if (s != nullptr) {
+      m_timePerPdgType[i] = s->uTotalTime();
+    } else {
+      info() << "PDGID/" << std::to_string(m_pdgIds[i]) << " not triggered" << endmsg;
+    }
   }
 
   // write ntuple to file
-- 
GitLab


From 17d436a58df3dde782894e4c7ccda4d2f01964a5 Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Wed, 21 May 2025 18:02:23 +0800
Subject: [PATCH 07/74] fix: remove ntuple write out

---
 .../DetSimAna/src/ChronoAnaElemTool.cpp       | 126 ++----------------
 Simulation/DetSimAna/src/ChronoAnaElemTool.h  |  25 +---
 2 files changed, 10 insertions(+), 141 deletions(-)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
index 6dce41c9..fe3b25c3 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -1,7 +1,7 @@
+#include <GaudiKernel/StatusCode.h>
 #include <array>
 #include <cassert>
 #include <cerrno>
-#include <cstddef>
 #include <string>
 
 #include "ChronoAnaElemTool.h"
@@ -13,77 +13,16 @@
 #include "G4Track.hh"
 #include "G4TransportationManager.hh"
 #include "GaudiAlg/GaudiAlgorithm.h"
-#include "GaudiKernel/ChronoEntity.h"
 #include "GaudiKernel/IChronoSvc.h"
 #include "GaudiKernel/INTupleSvc.h"
 #include "GaudiKernel/MsgStream.h"
-#include "GaudiKernel/NTuple.h"
 
 DECLARE_COMPONENT(ChronoAnaElemTool)
 
-StatusCode ChronoAnaElemTool::initNtuple() {
-  bool success = true;
-  { // Subdet array
-    NTuplePtr nt(m_ntupleSvc, "ChronoMetrics/SimulationTime/SubDet");
-    if (!nt) {
-      nt = m_ntupleSvc->book("ChronoMetrics/SimulationTime/SubDet",
-                             CLID_ColumnWiseTuple, "SimTime per Subdetector");
-      if (!nt) {
-        return StatusCode::FAILURE;
-      }
-      success &= nt->addItem("EventNumber", m_eventNumber);
-      success &= nt->addItem("EventSimTime", m_eventSimTime); // microseconds
-      success &=
-          nt->addItem("EventOverheadTime", m_eventOverheadTime); // microseconds
-      success &= nt->addItem("TimePerSubdet", m_subdetNames.size(),
-                             m_timePerSubdet, 0.0, 1e6);
-    }
-    m_subdetTuple = nt;
-  }
-
-  { // Particle array
-    NTuplePtr nt(m_ntupleSvc, "ChronoMetrics/SimulationTime/PdgIdTypes");
-    if (!nt) {
-      nt = m_ntupleSvc->book("ChronoMetrics/SimulationTime/PdgIdTypes",
-                             CLID_ColumnWiseTuple, "SimTime per Particle ID");
-      if (!nt) {
-        return StatusCode::FAILURE;
-      }
-      success &= nt->addItem("EventNumber", m_eventNumber);
-      success &= nt->addItem("EventSimTime", m_eventSimTime);
-      success &= nt->addItem("EventOverheadTime", m_eventOverheadTime);
-      // record the number of different PDG ID
-      success &= nt->addItem("NumPdgIdTypes", m_nPdgTypes, 0U, MAX_PDG_TYPES);
-      // record the PDG ID and time spend on each type of particle
-      success &= nt->addIndexedItem("PdgIdTypes", m_nPdgTypes, m_pdgIds);
-      success &=
-          nt->addIndexedItem("TimePerPdgIdType", m_nPdgTypes, m_timePerPdgType);
-    }
-    m_pidTuple = nt;
-  }
-  if (!m_subdetTuple || !m_pidTuple || !success) {
-    return StatusCode::FAILURE;
-  } else {
-    return StatusCode::SUCCESS;
-  }
-}
-
 StatusCode ChronoAnaElemTool::initSubdetNames() {
 
-  // prepare to write out subdet names
-  m_logFileStream.open(m_writeLogFileName.value(),
-                       std::ios::out | std::ios::trunc);
-  if (m_logFileStream.fail()) {
-    error() << "Failed to open output file: " << m_writeLogFileName.value()
-            << endmsg;
-    return StatusCode::FAILURE;
-  }
-
   // add pWorld
   m_subdetNames.push_back("pWorld");
-  size_t index = 0;
-  m_logFileStream << "# " << index << "  " << "pWorld" << '\n';
-  ++index;
 
   // add subdets
   const auto &children = m_geomSvc->getDD4HepGeo().children();
@@ -92,11 +31,8 @@ StatusCode ChronoAnaElemTool::initSubdetNames() {
       continue;
     }
     m_subdetNames.push_back(name);
-    m_logFileStream << "# " << index << "  " << name << '\n';
-    ++index;
   }
 
-  m_logFileStream.close();
   return StatusCode::SUCCESS;
 }
 
@@ -113,11 +49,6 @@ StatusCode ChronoAnaElemTool::initialize() {
 
   // init subdet names and number
   sc = initSubdetNames();
-  if (sc.isFailure()) {
-    return sc;
-  }
-  // init ntuple for record sim time over subdet and pid
-  sc = initNtuple();
   return sc;
 }
 
@@ -126,12 +57,7 @@ void ChronoAnaElemTool::BeginOfEventAction(const G4Event *event) {
   if (m_eventNumber < m_skipFirstNEvents) {
     return;
   }
-  // start total simulation timing
-  m_subdetTuple->reset();
-  m_pidTuple->reset();
   // set eventNumber so we can use in userstepping
-  m_eventNumber = event->GetEventID();
-  m_overhead_time_counter = std::chrono::duration<double, std::micro>::zero();
   m_chronoStatSvc->chronoStart("Simulation");
 }
 
@@ -143,42 +69,6 @@ void ChronoAnaElemTool::EndOfEventAction(const G4Event *) {
                               m_subdetNames[m_currentSubdetNameIdx]);
   m_chronoStatSvc->chronoStop("PDGID/" + std::to_string(m_currentPdgId));
   m_chronoStatSvc->chronoStop("Simulation");
-
-  // save ntuple for record sim time over subdet and pid
-  m_eventSimTime = m_chronoStatSvc->chronoDelta("Simulation", IChronoSvc::USER);
-  m_eventOverheadTime = m_overhead_time_counter.count();
-
-  // fill ntuple for record sim time over subdet
-  for (unsigned i = 0; i < m_subdetNames.size(); ++i) {
-    auto s = m_chronoStatSvc->chrono("SubDet/" + m_subdetNames[i]);
-    if (s != nullptr) {
-      m_timePerSubdet[i] = s->uTotalTime();
-    } else {
-      info() << "SubDet/" << m_subdetNames[i] << " not triggered" << endmsg;
-    }
-  }
-
-  // fill ntuple for record sim time over pid
-  for (unsigned i = 0; i < m_nPdgTypes; ++i) {
-    auto s = m_chronoStatSvc->chrono("PDGID/" + std::to_string(m_pdgIds[i]));
-    if (s != nullptr) {
-      m_timePerPdgType[i] = s->uTotalTime();
-    } else {
-      info() << "PDGID/" << std::to_string(m_pdgIds[i]) << " not triggered" << endmsg;
-    }
-  }
-
-  // write ntuple to file
-  StatusCode sc = m_subdetTuple->writeRecord();
-  if (sc.isFailure()) {
-    error() << "Failed to write m_subdetTuple record for event "
-            << m_eventNumber << endmsg;
-  }
-  sc = m_pidTuple->writeRecord();
-  if (sc.isFailure()) {
-    error() << "Failed to write m_pidTuple record for event " << m_eventNumber
-            << endmsg;
-  }
 }
 
 inline void ChronoAnaElemTool::recordTimePerPdgIdType(const G4Step *aStep) {
@@ -201,13 +91,6 @@ inline void ChronoAnaElemTool::recordTimePerPdgIdType(const G4Step *aStep) {
     m_chronoStatSvc->chronoStop("PDGID/" + std::to_string(m_currentPdgId));
   }
 
-  // Find if this PDG is already recorded
-  if (std::find(m_pdgIds.begin(), m_pdgIds.end(), pdg) == m_pdgIds.end()) {
-    // new PDG, record it and start timer
-    m_pdgIds[m_nPdgTypes] = pdg;
-    ++m_nPdgTypes;
-  }
-
   // Update current PDG and start new PDG timer
   m_currentPdgId = pdg;
   m_chronoStatSvc->chronoStart("PDGID/" + std::to_string(m_currentPdgId));
@@ -275,4 +158,11 @@ inline int ChronoAnaElemTool::getSubdetNameIdx(const G4VTouchable *touchable) {
   error() << "Volume is nullptr after find history " << historyDepthNum
           << endmsg;
   return -1;
+}
+
+StatusCode ChronoAnaElemTool::finalize() {
+  info() << "Finalizing ChronoAnaElemTool" << endmsg;
+  info() << "Overhead Time in total: " << m_overhead_time_counter.count()
+         << " ms" << endmsg;
+  return StatusCode::SUCCESS;
 }
\ No newline at end of file
diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.h b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
index 41f93681..2873d5e3 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.h
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
@@ -13,7 +13,6 @@
 #include "GaudiKernel/AlgTool.h"
 #include "GaudiKernel/IChronoStatSvc.h"
 #include "GaudiKernel/INTupleSvc.h"
-#include "GaudiKernel/NTuple.h"
 
 class ChronoAnaElemTool : public extends<AlgTool, IAnaElemTool> {
 
@@ -29,10 +28,10 @@ public:
 
   /// Overriding initialize and finalize
   StatusCode initialize() override;
+  StatusCode finalize() override;
 
 private:
   // methods
-  StatusCode initNtuple();
   StatusCode initSubdetNames();
   inline void recordTimePerPdgIdType(const G4Step *aStep);
   inline void recordTimePerSubdet(const G4Step *aStep);
@@ -43,36 +42,16 @@ private:
   SmartIF<INTupleSvc> m_ntupleSvc;
   SmartIF<IGeomSvc> m_geomSvc;
 
-  Gaudi::Property<std::string> m_writeLogFileName{this, "WriteLogFileName",
-                                                  "chrono_stat.log"};
   Gaudi::Property<int> m_skipFirstNEvents{this, "SkipFirstNEvents",
                                                   5};
-  std::ofstream m_logFileStream;
-
-  NTuple::Tuple *m_subdetTuple{nullptr};
-  NTuple::Tuple *m_pidTuple{nullptr};
 
   std::chrono::duration<double, std::micro> m_overhead_time_counter =
       std::chrono::duration<double, std::micro>::zero();
 
-  static const unsigned MAX_PDG_TYPES = 256;
   std::vector<std::string> m_subdetNames;
   int m_currentSubdetNameIdx = -1;
   int m_currentPdgId = 0;
-
-  NTuple::Item<long> m_eventNumber;
-  // User space sim time
-  NTuple::Item<double> m_eventOverheadTime;
-  NTuple::Item<double> m_eventSimTime;
-
-  NTuple::Array<double> m_timePerSubdet;
-
-  // type number of different PDG ID
-  NTuple::Item<unsigned> m_nPdgTypes;
-  // array of pdg ids
-  NTuple::Array<int> m_pdgIds;
-  // time spend on each type of particle
-  NTuple::Array<double> m_timePerPdgType;
+  int m_eventNumber = 0;
 };
 
 #endif
-- 
GitLab


From a83b5d8fcab479c8c57f0b149c3c4929e77baaeb Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Wed, 21 May 2025 22:58:37 +0800
Subject: [PATCH 08/74] fix: calculate more overhead

---
 Simulation/DetSimAna/src/ChronoAnaElemTool.cpp | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
index fe3b25c3..e1c56745 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -102,11 +102,7 @@ inline void ChronoAnaElemTool::recordTimePerSubdet(const G4Step *aStep) {
   }
   // Enter the first step in a new volume
   // calculate the time spent in get subdet name
-  auto start = std::chrono::high_resolution_clock::now();
   const auto newNameIdx = getSubdetNameIdx(aStep->GetTrack()->GetTouchable());
-  auto end = std::chrono::high_resolution_clock::now();
-  m_overhead_time_counter +=
-      std::chrono::duration<double, std::micro>(end - start);
 
   if (newNameIdx == m_currentSubdetNameIdx) {
     // same subdet, skip this step
@@ -130,8 +126,12 @@ void ChronoAnaElemTool::UserSteppingAction(const G4Step *aStep) {
   if (m_eventNumber < m_skipFirstNEvents) {
     return;
   }
+  auto start = std::chrono::high_resolution_clock::now();
   recordTimePerPdgIdType(aStep);
   recordTimePerSubdet(aStep);
+  auto end = std::chrono::high_resolution_clock::now();
+  m_overhead_time_counter +=
+      std::chrono::duration<double, std::micro>(end - start);
 }
 
 inline int ChronoAnaElemTool::getSubdetNameIdx(const G4VTouchable *touchable) {
@@ -163,6 +163,6 @@ inline int ChronoAnaElemTool::getSubdetNameIdx(const G4VTouchable *touchable) {
 StatusCode ChronoAnaElemTool::finalize() {
   info() << "Finalizing ChronoAnaElemTool" << endmsg;
   info() << "Overhead Time in total: " << m_overhead_time_counter.count()
-         << " ms" << endmsg;
+         << " microseconds" << endmsg;
   return StatusCode::SUCCESS;
 }
\ No newline at end of file
-- 
GitLab


From 7b182768b73edc0273676412857b82baedf42094 Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Sat, 24 May 2025 08:57:29 +0800
Subject: [PATCH 09/74] fix: refactor header; reformat initSubdetNames

---
 .../DetSimAna/src/ChronoAnaElemTool.cpp       | 28 +++++++---
 Simulation/DetSimAna/src/ChronoAnaElemTool.h  | 56 +++++++++++++------
 2 files changed, 60 insertions(+), 24 deletions(-)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
index e1c56745..c488a1d5 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -13,24 +13,32 @@
 #include "G4Track.hh"
 #include "G4TransportationManager.hh"
 #include "GaudiAlg/GaudiAlgorithm.h"
-#include "GaudiKernel/IChronoSvc.h"
 #include "GaudiKernel/INTupleSvc.h"
 #include "GaudiKernel/MsgStream.h"
 
 DECLARE_COMPONENT(ChronoAnaElemTool)
 
 StatusCode ChronoAnaElemTool::initSubdetNames() {
+  const auto &children = m_geomSvc->getDD4HepGeo().children();
+  m_subdetNames.clear();
+  m_subdetNames.reserve(children.size() + 1);
 
   // add pWorld
-  m_subdetNames.push_back("pWorld");
+  m_subdetNames.emplace_back("pWorld");
 
   // add subdets
-  const auto &children = m_geomSvc->getDD4HepGeo().children();
   for (const auto &[name, subdet] : children) {
-    if (subdet.id() < 0) {
+    if (subdet.id() < 0)
       continue;
-    }
-    m_subdetNames.push_back(name);
+    m_subdetNames.emplace_back(name);
+  }
+
+  // print subdets to log file
+
+  info() << "=== Subdetector Names List (" << m_subdetNames.size()
+         << ") ===" << endmsg;
+  for (size_t i = 0; i < m_subdetNames.size(); ++i) {
+    info() << "[" << std::setw(2) << i << "]  " << m_subdetNames[i] << endmsg;
   }
 
   return StatusCode::SUCCESS;
@@ -40,13 +48,15 @@ StatusCode ChronoAnaElemTool::initialize() {
   StatusCode sc;
 
   // Get services
-  m_chronoStatSvc = service<IChronoStatSvc>("ChronoStatSvc");
   m_ntupleSvc = service<INTupleSvc>("NTupleSvc");
   m_geomSvc = service<IGeomSvc>("GeomSvc");
-  if (!m_chronoStatSvc || !m_ntupleSvc || !m_geomSvc) {
+  if (!m_ntupleSvc || !m_geomSvc) {
     return StatusCode::FAILURE;
   }
 
+  // calibrate the clock before starting the simulation
+  NanoClock::calibrate();
+
   // init subdet names and number
   sc = initSubdetNames();
   return sc;
@@ -69,6 +79,8 @@ void ChronoAnaElemTool::EndOfEventAction(const G4Event *) {
                               m_subdetNames[m_currentSubdetNameIdx]);
   m_chronoStatSvc->chronoStop("PDGID/" + std::to_string(m_currentPdgId));
   m_chronoStatSvc->chronoStop("Simulation");
+
+  // record time to NTuple
 }
 
 inline void ChronoAnaElemTool::recordTimePerPdgIdType(const G4Step *aStep) {
diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.h b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
index 2873d5e3..10f23655 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.h
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
@@ -2,27 +2,31 @@
 #define ChronoAnaElemTool_h
 
 #include <chrono>
-#include <ratio>
 #include <string>
 #include <vector>
 
 #include "DetInterface/IGeomSvc.h"
 #include "DetSimInterface/IAnaElemTool.h"
 
-#include "G4VTouchable.hh"
 #include "GaudiKernel/AlgTool.h"
-#include "GaudiKernel/IChronoStatSvc.h"
 #include "GaudiKernel/INTupleSvc.h"
+#include "GaudiKernel/NTuple.h"
+#include <Gaudi/Timers/RdtscClock.h>
 
 class ChronoAnaElemTool : public extends<AlgTool, IAnaElemTool> {
 
 public:
   using extends::extends;
+  using NanoClock = Gaudi::Timers::RdtscClock<std::chrono::nanoseconds>;
 
   // Event
   virtual void BeginOfEventAction(const G4Event *) override;
   virtual void EndOfEventAction(const G4Event *) override;
 
+  // Tracking
+  virtual void PreUserTrackingAction(const G4Track *) override;
+  virtual void PostUserTrackingAction(const G4Track *) override;
+
   // Stepping
   virtual void UserSteppingAction(const G4Step *) override;
 
@@ -31,27 +35,47 @@ public:
   StatusCode finalize() override;
 
 private:
-  // methods
-  StatusCode initSubdetNames();
-  inline void recordTimePerPdgIdType(const G4Step *aStep);
-  inline void recordTimePerSubdet(const G4Step *aStep);
-  inline int getSubdetNameIdx(const G4VTouchable *touchable);
-
-  // variables
-  SmartIF<IChronoStatSvc> m_chronoStatSvc;
+  // common
   SmartIF<INTupleSvc> m_ntupleSvc;
   SmartIF<IGeomSvc> m_geomSvc;
 
-  Gaudi::Property<int> m_skipFirstNEvents{this, "SkipFirstNEvents",
-                                                  5};
+  Gaudi::Property<int> m_skipFirstNEvents{this, "SkipFirstNEvents", 5};
+
+  NTuple::Item<long> m_eventNumber;
+  NTuple::Item<double> m_eventTotSimTime;
+  StatusCode initNtuple();
 
-  std::chrono::duration<double, std::micro> m_overhead_time_counter =
-      std::chrono::duration<double, std::micro>::zero();
+  // simtime over subdet
+  inline void recordTimePerSubdet(const G4Step *aStep);
+  inline int getSubdetNameIdx(const G4Step *aStep);
 
+  StatusCode initSubdetNames();
   std::vector<std::string> m_subdetNames;
   int m_currentSubdetNameIdx = -1;
+
+  NTuple::Tuple *m_subdetTuple{nullptr};
+  NTuple::Array<double> m_timePerSubdet;
+
+  // simtime over PDGID
+  inline void recordTimePerPdgIdType(const G4Step *aStep);
   int m_currentPdgId = 0;
-  int m_eventNumber = 0;
+  static const unsigned MAX_PDG_TYPES = 1024;
+
+  NTuple::Tuple *m_pidTuple{nullptr};
+  // type number of different PDG ID
+  NTuple::Item<unsigned> m_nPdgTypes;
+  // array of pdg ids
+  NTuple::Array<int> m_pdgIds;
+  // time spend on each type of particle
+  NTuple::Array<double> m_timePerPdgType;
+
+  // simtime over Track
+  Gaudi::Property<bool> m_doTrackSimTime{this, "DoTrackSimTime", false};
+  static const unsigned MAX_TRACK_TYPES = 1024;
+  NTuple::Item<int> m_trackTypes;
+  NTuple::Array<double> m_trackLengths;
+  NTuple::Array<double> m_trackTimes;
+  NTuple::Array<double> m_trackLengthsOverTime; // track length over time
 };
 
 #endif
-- 
GitLab


From 003db483fddb7ab15c1194ddfcff0992adb82fb0 Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Sat, 24 May 2025 10:02:14 +0800
Subject: [PATCH 10/74] feat(ChronoAnaElemTool): add N-tuple tracking for track
 lengths and times

- Implement `bookNtuple` helper method for NTuple booking
- Add `initNtuple` to initialize track-related NTuple items
- Update service initialization order and dependencies
- Remove unused MAX_PDG_TYPES and MAX_TRACK_TYPES constants
- Switch to NTuplePtr for tuple management
---
 .../DetSimAna/src/ChronoAnaElemTool.cpp       | 178 +++++-------------
 Simulation/DetSimAna/src/ChronoAnaElemTool.h  |  14 +-
 2 files changed, 56 insertions(+), 136 deletions(-)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
index c488a1d5..5d96b849 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -2,19 +2,16 @@
 #include <array>
 #include <cassert>
 #include <cerrno>
-#include <string>
 
 #include "ChronoAnaElemTool.h"
 
-#include "DDG4/Geant4Data.h"
-#include "G4Event.hh"
-#include "G4Step.hh"
-#include "G4String.hh"
-#include "G4Track.hh"
-#include "G4TransportationManager.hh"
 #include "GaudiAlg/GaudiAlgorithm.h"
-#include "GaudiKernel/INTupleSvc.h"
+#include "GaudiKernel/ClassID.h"
 #include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/NTuple.h"
+#include "GaudiKernel/SmartIF.h"
+
+#include "DetInterface/IGeomSvc.h"
 
 DECLARE_COMPONENT(ChronoAnaElemTool)
 
@@ -44,13 +41,50 @@ StatusCode ChronoAnaElemTool::initSubdetNames() {
   return StatusCode::SUCCESS;
 }
 
+StatusCode ChronoAnaElemTool::bookNtuple(NTuplePtr &t, std::string_view path,
+                                         std::string_view title) {
+  if (NTuplePtr tmp = NTuplePtr(m_ntupleSvc, std::string(path)); !tmp) {
+    tmp = m_ntupleSvc->book(std::string(path), CLID_ColumnWiseTuple,
+                            std::string(title));
+    if (!tmp) {
+      error() << "Cannot book N-tuple: " << path << endmsg;
+      return StatusCode::FAILURE;
+    }
+  } else {
+    t = tmp;
+  }
+  return StatusCode::SUCCESS;
+}
+
+StatusCode ChronoAnaElemTool::initNtuple() {
+  bool suc = true;
+
+  // Book the N-tuple for track length
+  suc &= bookNtuple(m_trackTuple, "ChronoMetrics/TrackLength",
+                    "SimTime over Track length")
+             .isSuccess();
+  suc &=
+      m_trackTuple->addItem("trackNum", m_trackNum, 0U, UINT_MAX).isSuccess();
+  suc &=
+      m_trackTuple->addIndexedItem("trackLengths", m_trackNum, m_trackLengths)
+          .isSuccess();
+  suc &= m_trackTuple->addIndexedItem("trackTimes", m_trackNum, m_trackTimes)
+             .isSuccess();
+  suc &= m_trackTuple
+             ->addIndexedItem("trackLengthsOverTime", m_trackNum,
+                              m_trackLengthsOverTime)
+             .isSuccess();
+
+  return suc ? StatusCode::SUCCESS : StatusCode::FAILURE;
+}
+
 StatusCode ChronoAnaElemTool::initialize() {
-  StatusCode sc;
+  bool suc = true;
 
   // Get services
-  m_ntupleSvc = service<INTupleSvc>("NTupleSvc");
   m_geomSvc = service<IGeomSvc>("GeomSvc");
-  if (!m_ntupleSvc || !m_geomSvc) {
+  m_ntupleSvc = service<INTupleSvc>("NTupleSvc");
+  if (!m_geomSvc || !m_ntupleSvc) {
     return StatusCode::FAILURE;
   }
 
@@ -58,123 +92,7 @@ StatusCode ChronoAnaElemTool::initialize() {
   NanoClock::calibrate();
 
   // init subdet names and number
-  sc = initSubdetNames();
-  return sc;
+  suc &= initSubdetNames().isSuccess();
+  suc &= initNtuple().isSuccess();
+  return suc ? StatusCode::SUCCESS : StatusCode::FAILURE;
 }
-
-void ChronoAnaElemTool::BeginOfEventAction(const G4Event *event) {
-  m_eventNumber = event->GetEventID();
-  if (m_eventNumber < m_skipFirstNEvents) {
-    return;
-  }
-  // set eventNumber so we can use in userstepping
-  m_chronoStatSvc->chronoStart("Simulation");
-}
-
-void ChronoAnaElemTool::EndOfEventAction(const G4Event *) {
-  if (m_eventNumber < m_skipFirstNEvents) {
-    return;
-  }
-  m_chronoStatSvc->chronoStop("SubDet/" +
-                              m_subdetNames[m_currentSubdetNameIdx]);
-  m_chronoStatSvc->chronoStop("PDGID/" + std::to_string(m_currentPdgId));
-  m_chronoStatSvc->chronoStop("Simulation");
-
-  // record time to NTuple
-}
-
-inline void ChronoAnaElemTool::recordTimePerPdgIdType(const G4Step *aStep) {
-  auto track = aStep->GetTrack();
-  if (track->GetCurrentStepNumber() != 1) {
-    // not the first step, skip
-    return;
-  }
-  // Process Particle ID Timer
-  int pdg = track->GetParticleDefinition()->GetPDGEncoding();
-  if (pdg == m_currentPdgId) {
-    // pdgid not changed, skip
-    return;
-  }
-
-  // If there was a previous PDG timer, stop it
-  if (m_currentPdgId != 0) {
-    debug() << "Stopping PDG[" << m_currentPdgId << "] timer -> Starting PDG["
-            << pdg << "]" << endmsg;
-    m_chronoStatSvc->chronoStop("PDGID/" + std::to_string(m_currentPdgId));
-  }
-
-  // Update current PDG and start new PDG timer
-  m_currentPdgId = pdg;
-  m_chronoStatSvc->chronoStart("PDGID/" + std::to_string(m_currentPdgId));
-}
-
-inline void ChronoAnaElemTool::recordTimePerSubdet(const G4Step *aStep) {
-  if (!aStep->IsFirstStepInVolume()) {
-    return;
-  }
-  // Enter the first step in a new volume
-  // calculate the time spent in get subdet name
-  const auto newNameIdx = getSubdetNameIdx(aStep->GetTrack()->GetTouchable());
-
-  if (newNameIdx == m_currentSubdetNameIdx) {
-    // same subdet, skip this step
-    return;
-  }
-  // If we had a previous subdet, stop its timer
-  if (m_currentSubdetNameIdx != -1) {
-    debug() << "Exiting " << m_subdetNames[m_currentSubdetNameIdx]
-            << " -> Entering " << m_subdetNames[newNameIdx] << endmsg;
-    m_chronoStatSvc->chronoStop("SubDet/" +
-                                m_subdetNames[m_currentSubdetNameIdx]);
-  }
-
-  // Update state and start the new timer
-  m_currentSubdetNameIdx = newNameIdx;
-  m_chronoStatSvc->chronoStart("SubDet/" +
-                               m_subdetNames[m_currentSubdetNameIdx]);
-}
-
-void ChronoAnaElemTool::UserSteppingAction(const G4Step *aStep) {
-  if (m_eventNumber < m_skipFirstNEvents) {
-    return;
-  }
-  auto start = std::chrono::high_resolution_clock::now();
-  recordTimePerPdgIdType(aStep);
-  recordTimePerSubdet(aStep);
-  auto end = std::chrono::high_resolution_clock::now();
-  m_overhead_time_counter +=
-      std::chrono::duration<double, std::micro>(end - start);
-}
-
-inline int ChronoAnaElemTool::getSubdetNameIdx(const G4VTouchable *touchable) {
-  auto historyDepthNum = touchable->GetHistoryDepth();
-  for (int depth = 0; depth <= historyDepthNum; depth++) {
-    auto vol = touchable->GetVolume(depth);
-    if (!vol) {
-      error() << "Volume is nullptr during finding " << depth << " in "
-              << historyDepthNum << endmsg;
-      return -1;
-    }
-    auto volName = vol->GetName();
-
-    // Check if the volume name contains the subdetector name
-    auto it = std::find_if(m_subdetNames.begin(), m_subdetNames.end(),
-                           [&volName](const std::string &s) {
-                             return volName.find(s) != std::string::npos;
-                           });
-    // If the volume name contains the subdetector name, return its index
-    if (it != m_subdetNames.end()) {
-      return static_cast<int>(std::distance(m_subdetNames.begin(), it));
-    }
-  }
-  error() << "Volume is nullptr after find history " << historyDepthNum
-          << endmsg;
-  return -1;
-}
-
-StatusCode ChronoAnaElemTool::finalize() {
-  info() << "Finalizing ChronoAnaElemTool" << endmsg;
-  info() << "Overhead Time in total: " << m_overhead_time_counter.count()
-         << " microseconds" << endmsg;
-  return StatusCode::SUCCESS;
-}
\ No newline at end of file
diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.h b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
index 10f23655..4ee608d2 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.h
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
@@ -11,6 +11,7 @@
 #include "GaudiKernel/AlgTool.h"
 #include "GaudiKernel/INTupleSvc.h"
 #include "GaudiKernel/NTuple.h"
+#include "GaudiKernel/SmartIF.h"
 #include <Gaudi/Timers/RdtscClock.h>
 
 class ChronoAnaElemTool : public extends<AlgTool, IAnaElemTool> {
@@ -36,14 +37,16 @@ public:
 
 private:
   // common
-  SmartIF<INTupleSvc> m_ntupleSvc;
   SmartIF<IGeomSvc> m_geomSvc;
+  SmartIF<INTupleSvc> m_ntupleSvc;
 
   Gaudi::Property<int> m_skipFirstNEvents{this, "SkipFirstNEvents", 5};
 
   NTuple::Item<long> m_eventNumber;
   NTuple::Item<double> m_eventTotSimTime;
   StatusCode initNtuple();
+  StatusCode bookNtuple(NTuplePtr &t, std::string_view path,
+                        std::string_view title);
 
   // simtime over subdet
   inline void recordTimePerSubdet(const G4Step *aStep);
@@ -53,15 +56,14 @@ private:
   std::vector<std::string> m_subdetNames;
   int m_currentSubdetNameIdx = -1;
 
-  NTuple::Tuple *m_subdetTuple{nullptr};
+  NTuplePtr m_subdetTuple;
   NTuple::Array<double> m_timePerSubdet;
 
   // simtime over PDGID
   inline void recordTimePerPdgIdType(const G4Step *aStep);
   int m_currentPdgId = 0;
-  static const unsigned MAX_PDG_TYPES = 1024;
 
-  NTuple::Tuple *m_pidTuple{nullptr};
+  NTuplePtr m_pidTuple;
   // type number of different PDG ID
   NTuple::Item<unsigned> m_nPdgTypes;
   // array of pdg ids
@@ -71,8 +73,8 @@ private:
 
   // simtime over Track
   Gaudi::Property<bool> m_doTrackSimTime{this, "DoTrackSimTime", false};
-  static const unsigned MAX_TRACK_TYPES = 1024;
-  NTuple::Item<int> m_trackTypes;
+  NTuplePtr m_trackTuple;
+  NTuple::Item<unsigned> m_trackNum;
   NTuple::Array<double> m_trackLengths;
   NTuple::Array<double> m_trackTimes;
   NTuple::Array<double> m_trackLengthsOverTime; // track length over time
-- 
GitLab


From 0af89ffa6d8dd213601161e72e83e7be962a0f05 Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Sat, 24 May 2025 12:34:04 +0800
Subject: [PATCH 11/74] feat: simtime metrics on track done;

---
 .../DetSimAna/src/ChronoAnaElemTool.cpp       | 51 +++++++++++++++++--
 Simulation/DetSimAna/src/ChronoAnaElemTool.h  | 12 +++--
 2 files changed, 56 insertions(+), 7 deletions(-)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
index 5d96b849..27d05f5c 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -5,6 +5,9 @@
 
 #include "ChronoAnaElemTool.h"
 
+#include "G4Event.hh"
+#include "G4Step.hh"
+#include "G4Track.hh"
 #include "GaudiAlg/GaudiAlgorithm.h"
 #include "GaudiKernel/ClassID.h"
 #include "GaudiKernel/MsgStream.h"
@@ -56,13 +59,15 @@ StatusCode ChronoAnaElemTool::bookNtuple(NTuplePtr &t, std::string_view path,
   return StatusCode::SUCCESS;
 }
 
-StatusCode ChronoAnaElemTool::initNtuple() {
+StatusCode ChronoAnaElemTool::initTrackNtuple() {
   bool suc = true;
 
   // Book the N-tuple for track length
   suc &= bookNtuple(m_trackTuple, "ChronoMetrics/TrackLength",
                     "SimTime over Track length")
              .isSuccess();
+  suc &= m_trackTuple->addItem("eventNumber", m_eventNumber, 0U, UINT_MAX)
+             .isSuccess();
   suc &=
       m_trackTuple->addItem("trackNum", m_trackNum, 0U, UINT_MAX).isSuccess();
   suc &=
@@ -92,7 +97,47 @@ StatusCode ChronoAnaElemTool::initialize() {
   NanoClock::calibrate();
 
   // init subdet names and number
-  suc &= initSubdetNames().isSuccess();
-  suc &= initNtuple().isSuccess();
+  if (m_enableSubdetSimTimeRecording.value()) {
+    suc &= initSubdetNames().isSuccess();
+  }
+
+  if (m_enableTrackSimTimeRecording.value()) {
+    suc &= initTrackNtuple().isSuccess();
+  }
+
   return suc ? StatusCode::SUCCESS : StatusCode::FAILURE;
 }
+
+void ChronoAnaElemTool::PreUserTrackingAction(const G4Track *) {
+  if (!m_enableTrackSimTimeRecording.value()) {
+    return;
+  }
+  m_track_start = NanoClock::now();
+}
+
+void ChronoAnaElemTool::PostUserTrackingAction(const G4Track *track) {
+  if (!m_enableTrackSimTimeRecording.value()) {
+    return;
+  }
+  NanoClock::time_point track_stop = NanoClock::now();
+  m_trackLengths[m_trackNum] = track->GetTrackLength();
+  m_trackTimes[m_trackNum] =
+      NanoClock::duration(track_stop - m_track_start).count();
+  m_trackLengthsOverTime[m_trackNum] =
+      m_trackLengths[m_trackNum] / m_trackTimes[m_trackNum];
+  ++m_trackNum;
+}
+
+void ChronoAnaElemTool::EndOfEventAction(const G4Event *) {
+  if (m_enableTrackSimTimeRecording.value()) {
+    if (m_trackTuple->write().isFailure()) {
+      error() << "Error write track tuple at event " << m_eventNumber << endmsg;
+    } else {
+      m_trackTuple->reset();
+    }
+  }
+}
+
+void ChronoAnaElemTool::BeginOfEventAction(const G4Event *event) {
+  m_eventNumber = event->GetEventID();
+}
\ No newline at end of file
diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.h b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
index 4ee608d2..792bd98e 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.h
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
@@ -40,15 +40,14 @@ private:
   SmartIF<IGeomSvc> m_geomSvc;
   SmartIF<INTupleSvc> m_ntupleSvc;
 
-  Gaudi::Property<int> m_skipFirstNEvents{this, "SkipFirstNEvents", 5};
-
   NTuple::Item<long> m_eventNumber;
   NTuple::Item<double> m_eventTotSimTime;
-  StatusCode initNtuple();
   StatusCode bookNtuple(NTuplePtr &t, std::string_view path,
                         std::string_view title);
 
   // simtime over subdet
+  Gaudi::Property<bool> m_enableSubdetSimTimeRecording{
+      this, "EnableSubdetSimTimeRecording", false};
   inline void recordTimePerSubdet(const G4Step *aStep);
   inline int getSubdetNameIdx(const G4Step *aStep);
 
@@ -60,6 +59,8 @@ private:
   NTuple::Array<double> m_timePerSubdet;
 
   // simtime over PDGID
+  Gaudi::Property<bool> m_enablePDGIDSimTimeRecording{
+      this, "EnablePDGIDSimTimeRecording", false};
   inline void recordTimePerPdgIdType(const G4Step *aStep);
   int m_currentPdgId = 0;
 
@@ -72,7 +73,10 @@ private:
   NTuple::Array<double> m_timePerPdgType;
 
   // simtime over Track
-  Gaudi::Property<bool> m_doTrackSimTime{this, "DoTrackSimTime", false};
+  NanoClock::time_point m_track_start;
+  StatusCode initTrackNtuple();
+  Gaudi::Property<bool> m_enableTrackSimTimeRecording{
+      this, "EnableTrackSimTimeRecording", false};
   NTuplePtr m_trackTuple;
   NTuple::Item<unsigned> m_trackNum;
   NTuple::Array<double> m_trackLengths;
-- 
GitLab


From e3a1e90dfbb60ebba11efc9b87e038b98c229110 Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Sat, 24 May 2025 14:09:48 +0800
Subject: [PATCH 12/74] feat(ChronoAnaElemTool): refactor ntuple handling and
 add tracking metrics

---
 .../DetSimAna/src/ChronoAnaElemTool.cpp       | 115 +++++++++++++-----
 Simulation/DetSimAna/src/ChronoAnaElemTool.h  |  40 +++---
 2 files changed, 97 insertions(+), 58 deletions(-)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
index 27d05f5c..3f194411 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -44,41 +44,39 @@ StatusCode ChronoAnaElemTool::initSubdetNames() {
   return StatusCode::SUCCESS;
 }
 
-StatusCode ChronoAnaElemTool::bookNtuple(NTuplePtr &t, std::string_view path,
-                                         std::string_view title) {
-  if (NTuplePtr tmp = NTuplePtr(m_ntupleSvc, std::string(path)); !tmp) {
-    tmp = m_ntupleSvc->book(std::string(path), CLID_ColumnWiseTuple,
-                            std::string(title));
-    if (!tmp) {
-      error() << "Cannot book N-tuple: " << path << endmsg;
+StatusCode ChronoAnaElemTool::initNtuple() {
+  bool suc = true;
+
+  // Book the N-tuple
+  if (NTuplePtr nt = NTuplePtr(m_ntupleSvc, std::string("ChronoMetrics"));
+      !nt) {
+    nt = m_ntupleSvc->book(std::string("ChronoMetrics/events"),
+                           CLID_ColumnWiseTuple,
+                           std::string("ChronoMetrics/events"));
+    if (!nt) {
+      error() << "Cannot book N-tuple: ChronoMetrics" << endmsg;
       return StatusCode::FAILURE;
     }
   } else {
-    t = tmp;
+    m_ntuple = nt;
   }
-  return StatusCode::SUCCESS;
-}
 
-StatusCode ChronoAnaElemTool::initTrackNtuple() {
-  bool suc = true;
-
-  // Book the N-tuple for track length
-  suc &= bookNtuple(m_trackTuple, "ChronoMetrics/TrackLength",
-                    "SimTime over Track length")
-             .isSuccess();
-  suc &= m_trackTuple->addItem("eventNumber", m_eventNumber, 0U, UINT_MAX)
-             .isSuccess();
   suc &=
-      m_trackTuple->addItem("trackNum", m_trackNum, 0U, UINT_MAX).isSuccess();
+      m_ntuple->addItem("EventNumber", m_eventNumber, 0U, UINT_MAX).isSuccess();
+  suc &= m_ntuple->addItem("TrackNum", m_trackNum, 0U, UINT_MAX).isSuccess();
   suc &=
-      m_trackTuple->addIndexedItem("trackLengths", m_trackNum, m_trackLengths)
+      m_ntuple->addItem("TimePerSubdet", m_subdetNames.size(), m_timePerSubdet)
           .isSuccess();
-  suc &= m_trackTuple->addIndexedItem("trackTimes", m_trackNum, m_trackTimes)
+  suc &= m_ntuple->addIndexedItem("TrackLengths", m_trackNum, m_trackLengths)
              .isSuccess();
-  suc &= m_trackTuple
-             ->addIndexedItem("trackLengthsOverTime", m_trackNum,
+  suc &= m_ntuple->addIndexedItem("TrackTimes", m_trackNum, m_trackTimes)
+             .isSuccess();
+  suc &= m_ntuple
+             ->addIndexedItem("TrackLengthsOverTime", m_trackNum,
                               m_trackLengthsOverTime)
              .isSuccess();
+  suc &= m_ntuple->addIndexedItem("TrackPDGID", m_trackNum, m_trackPDGIds)
+             .isSuccess();
 
   return suc ? StatusCode::SUCCESS : StatusCode::FAILURE;
 }
@@ -102,16 +100,18 @@ StatusCode ChronoAnaElemTool::initialize() {
   }
 
   if (m_enableTrackSimTimeRecording.value()) {
-    suc &= initTrackNtuple().isSuccess();
+    suc &= initNtuple().isSuccess();
   }
 
   return suc ? StatusCode::SUCCESS : StatusCode::FAILURE;
 }
 
-void ChronoAnaElemTool::PreUserTrackingAction(const G4Track *) {
+void ChronoAnaElemTool::PreUserTrackingAction(const G4Track *track) {
   if (!m_enableTrackSimTimeRecording.value()) {
     return;
   }
+
+  m_trackStartSubdet[m_trackNum] = getSubdetNameIdx(track->GetTouchable());
   m_track_start = NanoClock::now();
 }
 
@@ -125,19 +125,70 @@ void ChronoAnaElemTool::PostUserTrackingAction(const G4Track *track) {
       NanoClock::duration(track_stop - m_track_start).count();
   m_trackLengthsOverTime[m_trackNum] =
       m_trackLengths[m_trackNum] / m_trackTimes[m_trackNum];
+  m_trackPDGIds[m_trackNum] = track->GetParticleDefinition()->GetPDGEncoding();
+  m_trackEndSubdet[m_trackNum] = getSubdetNameIdx(track->GetTouchable());
   ++m_trackNum;
 }
 
 void ChronoAnaElemTool::EndOfEventAction(const G4Event *) {
-  if (m_enableTrackSimTimeRecording.value()) {
-    if (m_trackTuple->write().isFailure()) {
-      error() << "Error write track tuple at event " << m_eventNumber << endmsg;
-    } else {
-      m_trackTuple->reset();
-    }
+  NanoClock::time_point event_stop = NanoClock::now();
+  m_eventTotSimTime = NanoClock::duration(event_stop - m_event_start).count();
+  if (m_ntuple->write().isFailure()) {
+    error() << "Error write track tuple at event " << m_eventNumber << endmsg;
   }
 }
 
 void ChronoAnaElemTool::BeginOfEventAction(const G4Event *event) {
+  m_ntuple->reset();
   m_eventNumber = event->GetEventID();
+
+  m_event_start = NanoClock::now();
+}
+
+void ChronoAnaElemTool::UserSteppingAction(const G4Step *step) {
+  if (!m_enableSubdetSimTimeRecording.value())
+    return;
+  if (step->IsLastStepInVolume()) {
+    auto subdet_stop = NanoClock::now();
+    m_timePerSubdet[getSubdetNameIdx(step->GetTrack()->GetTouchable())] +=
+        NanoClock::duration(subdet_stop - m_subdet_enter).count();
+  } else if (step->IsFirstStepInVolume()) {
+    m_subdet_enter = NanoClock::now();
+  }
+}
+
+inline int ChronoAnaElemTool::getSubdetNameIdx(const G4VTouchable *touch) {
+  auto historyDepthNum = touch->GetHistoryDepth();
+  for (int depth = 0; depth <= historyDepthNum; depth++) {
+    auto vol = touch->GetVolume(depth);
+    if (!vol) {
+      error() << "Volume is nullptr during finding " << depth << " in "
+              << historyDepthNum << endmsg;
+      return -1;
+    }
+    auto volName = vol->GetName();
+
+    // Check how many subdetector names are contained in the volume name
+    int foundCount = 0;
+    int foundIndex = -1;
+
+    for (size_t i = 0; i < m_subdetNames.size(); ++i) {
+      if (volName.find(m_subdetNames[i]) != std::string::npos) {
+        foundCount++;
+        foundIndex = static_cast<int>(i);
+      }
+    }
+
+    // Return index if exactly one match found, otherwise -1
+    return (foundCount == 1) ? foundIndex : -1;
+  }
+  error() << "Volume is nullptr after find history " << historyDepthNum
+          << endmsg;
+  return -1;
+}
+
+StatusCode ChronoAnaElemTool::finalize() {
+  StatusCode sc;
+
+  return sc;
 }
\ No newline at end of file
diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.h b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
index 792bd98e..ddebd7ca 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.h
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
@@ -1,6 +1,7 @@
 #ifndef ChronoAnaElemTool_h
 #define ChronoAnaElemTool_h
 
+#include <G4VTouchable.hh>
 #include <chrono>
 #include <string>
 #include <vector>
@@ -39,49 +40,36 @@ private:
   // common
   SmartIF<IGeomSvc> m_geomSvc;
   SmartIF<INTupleSvc> m_ntupleSvc;
+  NTuple::Tuple *m_ntuple;
+  StatusCode initNtuple();
 
   NTuple::Item<long> m_eventNumber;
   NTuple::Item<double> m_eventTotSimTime;
-  StatusCode bookNtuple(NTuplePtr &t, std::string_view path,
-                        std::string_view title);
+  NanoClock::time_point m_event_start;
 
   // simtime over subdet
-  Gaudi::Property<bool> m_enableSubdetSimTimeRecording{
-      this, "EnableSubdetSimTimeRecording", false};
-  inline void recordTimePerSubdet(const G4Step *aStep);
-  inline int getSubdetNameIdx(const G4Step *aStep);
-
   StatusCode initSubdetNames();
   std::vector<std::string> m_subdetNames;
-  int m_currentSubdetNameIdx = -1;
-
-  NTuplePtr m_subdetTuple;
-  NTuple::Array<double> m_timePerSubdet;
+  inline int getSubdetNameIdx(const G4VTouchable *);
 
-  // simtime over PDGID
-  Gaudi::Property<bool> m_enablePDGIDSimTimeRecording{
-      this, "EnablePDGIDSimTimeRecording", false};
-  inline void recordTimePerPdgIdType(const G4Step *aStep);
-  int m_currentPdgId = 0;
+  NanoClock::time_point m_subdet_enter;
+  Gaudi::Property<bool> m_enableSubdetSimTimeRecording{
+      this, "EnableSubdetSimTimeRecording", false};
 
-  NTuplePtr m_pidTuple;
-  // type number of different PDG ID
-  NTuple::Item<unsigned> m_nPdgTypes;
-  // array of pdg ids
-  NTuple::Array<int> m_pdgIds;
-  // time spend on each type of particle
-  NTuple::Array<double> m_timePerPdgType;
+  NTuple::Array<double> m_timePerSubdet;
 
   // simtime over Track
   NanoClock::time_point m_track_start;
-  StatusCode initTrackNtuple();
   Gaudi::Property<bool> m_enableTrackSimTimeRecording{
       this, "EnableTrackSimTimeRecording", false};
-  NTuplePtr m_trackTuple;
+
   NTuple::Item<unsigned> m_trackNum;
   NTuple::Array<double> m_trackLengths;
   NTuple::Array<double> m_trackTimes;
-  NTuple::Array<double> m_trackLengthsOverTime; // track length over time
+  NTuple::Array<double> m_trackLengthsOverTime;
+  NTuple::Array<int> m_trackPDGIds;
+  NTuple::Array<int> m_trackStartSubdet;
+  NTuple::Array<int> m_trackEndSubdet;
 };
 
 #endif
-- 
GitLab


From 45d02fcf5ad372805ad93217b0838d39479e3df6 Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Sat, 24 May 2025 14:42:03 +0800
Subject: [PATCH 13/74] refactor: remove redundant ntuple item additions

---
 .../DetSimAna/src/ChronoAnaElemTool.cpp       | 56 ++++++++++---------
 1 file changed, 30 insertions(+), 26 deletions(-)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
index 3f194411..01469b24 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -48,36 +48,40 @@ StatusCode ChronoAnaElemTool::initNtuple() {
   bool suc = true;
 
   // Book the N-tuple
-  if (NTuplePtr nt = NTuplePtr(m_ntupleSvc, std::string("ChronoMetrics"));
-      !nt) {
-    nt = m_ntupleSvc->book(std::string("ChronoMetrics/events"),
-                           CLID_ColumnWiseTuple,
-                           std::string("ChronoMetrics/events"));
-    if (!nt) {
-      error() << "Cannot book N-tuple: ChronoMetrics" << endmsg;
+  NTuplePtr nt(m_ntupleSvc, "ChronoMetrics/Sim");
+  if (nt) {
+    info() << "Get Ntuple Success" << endmsg;
+    m_ntuple = nt;
+  } else {
+    m_ntuple = m_ntupleSvc->book("ChronoMetrics/Sim", CLID_ColumnWiseTuple,
+                                 "Simulation ChronoMetrics");
+    if (m_ntuple) {
+      info() << "Create Ntuple Success" << endmsg;
+      suc &= m_ntuple->addItem("EventNumber", m_eventNumber, 0U, UINT_MAX)
+                 .isSuccess();
+      suc &=
+          m_ntuple->addItem("TrackNum", m_trackNum, 0U, UINT_MAX).isSuccess();
+      suc &=
+          m_ntuple
+              ->addItem("TimePerSubdet", m_subdetNames.size(), m_timePerSubdet)
+              .isSuccess();
+      suc &=
+          m_ntuple->addIndexedItem("TrackLengths", m_trackNum, m_trackLengths)
+              .isSuccess();
+      suc &= m_ntuple->addIndexedItem("TrackTimes", m_trackNum, m_trackTimes)
+                 .isSuccess();
+      suc &= m_ntuple
+                 ->addIndexedItem("TrackLengthsOverTime", m_trackNum,
+                                  m_trackLengthsOverTime)
+                 .isSuccess();
+      suc &= m_ntuple->addIndexedItem("TrackPDGID", m_trackNum, m_trackPDGIds)
+                 .isSuccess();
+    } else {
+      error() << "Cannot book N-tuple:" << long(m_ntuple) << endmsg;
       return StatusCode::FAILURE;
     }
-  } else {
-    m_ntuple = nt;
   }
 
-  suc &=
-      m_ntuple->addItem("EventNumber", m_eventNumber, 0U, UINT_MAX).isSuccess();
-  suc &= m_ntuple->addItem("TrackNum", m_trackNum, 0U, UINT_MAX).isSuccess();
-  suc &=
-      m_ntuple->addItem("TimePerSubdet", m_subdetNames.size(), m_timePerSubdet)
-          .isSuccess();
-  suc &= m_ntuple->addIndexedItem("TrackLengths", m_trackNum, m_trackLengths)
-             .isSuccess();
-  suc &= m_ntuple->addIndexedItem("TrackTimes", m_trackNum, m_trackTimes)
-             .isSuccess();
-  suc &= m_ntuple
-             ->addIndexedItem("TrackLengthsOverTime", m_trackNum,
-                              m_trackLengthsOverTime)
-             .isSuccess();
-  suc &= m_ntuple->addIndexedItem("TrackPDGID", m_trackNum, m_trackPDGIds)
-             .isSuccess();
-
   return suc ? StatusCode::SUCCESS : StatusCode::FAILURE;
 }
 
-- 
GitLab


From ae2128643d1c3d0ece69531832c3e15e8cd46275 Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Sat, 24 May 2025 14:44:05 +0800
Subject: [PATCH 14/74] fix: init ntuple anyway

---
 Simulation/DetSimAna/src/ChronoAnaElemTool.cpp | 7 ++-----
 1 file changed, 2 insertions(+), 5 deletions(-)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
index 01469b24..b16c2757 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -102,11 +102,7 @@ StatusCode ChronoAnaElemTool::initialize() {
   if (m_enableSubdetSimTimeRecording.value()) {
     suc &= initSubdetNames().isSuccess();
   }
-
-  if (m_enableTrackSimTimeRecording.value()) {
-    suc &= initNtuple().isSuccess();
-  }
-
+  suc &= initNtuple().isSuccess();
   return suc ? StatusCode::SUCCESS : StatusCode::FAILURE;
 }
 
@@ -143,6 +139,7 @@ void ChronoAnaElemTool::EndOfEventAction(const G4Event *) {
 }
 
 void ChronoAnaElemTool::BeginOfEventAction(const G4Event *event) {
+  info() << m_ntuple << endmsg;
   m_ntuple->reset();
   m_eventNumber = event->GetEventID();
 
-- 
GitLab


From 649176483bf2c8ac0382d7466122c0e34fc982de Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Sat, 24 May 2025 15:22:58 +0800
Subject: [PATCH 15/74] feat(ChronoAnaElemTool): add skip events option and
 track time improvements

---
 .../DetSimAna/src/ChronoAnaElemTool.cpp       | 22 ++++++++++++++-----
 Simulation/DetSimAna/src/ChronoAnaElemTool.h  |  3 +++
 2 files changed, 20 insertions(+), 5 deletions(-)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
index b16c2757..7558854c 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -59,8 +59,9 @@ StatusCode ChronoAnaElemTool::initNtuple() {
       info() << "Create Ntuple Success" << endmsg;
       suc &= m_ntuple->addItem("EventNumber", m_eventNumber, 0U, UINT_MAX)
                  .isSuccess();
-      suc &=
-          m_ntuple->addItem("TrackNum", m_trackNum, 0U, UINT_MAX).isSuccess();
+      suc &= m_ntuple->addItem("TotSimTime", m_eventTotSimTime).isSuccess();
+      suc &= m_ntuple->addItem("TrackNum", m_trackNum, 0U, MAX_TRACK_NUM)
+                 .isSuccess();
       suc &=
           m_ntuple
               ->addItem("TimePerSubdet", m_subdetNames.size(), m_timePerSubdet)
@@ -110,13 +111,17 @@ void ChronoAnaElemTool::PreUserTrackingAction(const G4Track *track) {
   if (!m_enableTrackSimTimeRecording.value()) {
     return;
   }
+  if (m_eventNumber < m_skipFirstNEvents) {
+    return;
+  }
 
   m_trackStartSubdet[m_trackNum] = getSubdetNameIdx(track->GetTouchable());
   m_track_start = NanoClock::now();
 }
 
 void ChronoAnaElemTool::PostUserTrackingAction(const G4Track *track) {
-  if (!m_enableTrackSimTimeRecording.value()) {
+  if (!m_enableTrackSimTimeRecording.value() ||
+      m_eventNumber < m_skipFirstNEvents) {
     return;
   }
   NanoClock::time_point track_stop = NanoClock::now();
@@ -131,6 +136,9 @@ void ChronoAnaElemTool::PostUserTrackingAction(const G4Track *track) {
 }
 
 void ChronoAnaElemTool::EndOfEventAction(const G4Event *) {
+  if (m_eventNumber < m_skipFirstNEvents) {
+    return;
+  }
   NanoClock::time_point event_stop = NanoClock::now();
   m_eventTotSimTime = NanoClock::duration(event_stop - m_event_start).count();
   if (m_ntuple->write().isFailure()) {
@@ -139,16 +147,20 @@ void ChronoAnaElemTool::EndOfEventAction(const G4Event *) {
 }
 
 void ChronoAnaElemTool::BeginOfEventAction(const G4Event *event) {
-  info() << m_ntuple << endmsg;
   m_ntuple->reset();
   m_eventNumber = event->GetEventID();
+  if (m_eventNumber < m_skipFirstNEvents) {
+    return;
+  }
 
   m_event_start = NanoClock::now();
 }
 
 void ChronoAnaElemTool::UserSteppingAction(const G4Step *step) {
-  if (!m_enableSubdetSimTimeRecording.value())
+  if (!m_enableSubdetSimTimeRecording.value() ||
+      m_eventNumber < m_skipFirstNEvents) {
     return;
+  }
   if (step->IsLastStepInVolume()) {
     auto subdet_stop = NanoClock::now();
     m_timePerSubdet[getSubdetNameIdx(step->GetTrack()->GetTouchable())] +=
diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.h b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
index ddebd7ca..07407219 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.h
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
@@ -47,6 +47,8 @@ private:
   NTuple::Item<double> m_eventTotSimTime;
   NanoClock::time_point m_event_start;
 
+  Gaudi::Property<int> m_skipFirstNEvents{this, "SkipFirstNEvents", 5};
+
   // simtime over subdet
   StatusCode initSubdetNames();
   std::vector<std::string> m_subdetNames;
@@ -59,6 +61,7 @@ private:
   NTuple::Array<double> m_timePerSubdet;
 
   // simtime over Track
+  static const unsigned MAX_TRACK_NUM = 25565;
   NanoClock::time_point m_track_start;
   Gaudi::Property<bool> m_enableTrackSimTimeRecording{
       this, "EnableTrackSimTimeRecording", false};
-- 
GitLab


From fb6b9613e1bace1e0d7d4970789a9d118a7167b2 Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Sun, 25 May 2025 11:37:48 +0800
Subject: [PATCH 16/74] fix: data type; getSubdetNameIdx

---
 .../DetSimAna/src/ChronoAnaElemTool.cpp       | 98 ++++++++++---------
 Simulation/DetSimAna/src/ChronoAnaElemTool.h  | 11 +--
 2 files changed, 58 insertions(+), 51 deletions(-)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
index 7558854c..73c8cd13 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -1,7 +1,7 @@
 #include <GaudiKernel/StatusCode.h>
-#include <array>
 #include <cassert>
 #include <cerrno>
+#include <climits>
 
 #include "ChronoAnaElemTool.h"
 
@@ -28,8 +28,6 @@ StatusCode ChronoAnaElemTool::initSubdetNames() {
 
   // add subdets
   for (const auto &[name, subdet] : children) {
-    if (subdet.id() < 0)
-      continue;
     m_subdetNames.emplace_back(name);
   }
 
@@ -57,26 +55,34 @@ StatusCode ChronoAnaElemTool::initNtuple() {
                                  "Simulation ChronoMetrics");
     if (m_ntuple) {
       info() << "Create Ntuple Success" << endmsg;
-      suc &= m_ntuple->addItem("EventNumber", m_eventNumber, 0U, UINT_MAX)
+      suc &= m_ntuple->addItem("EventNumber", m_eventNumber, 0L, LONG_MAX)
                  .isSuccess();
       suc &= m_ntuple->addItem("TotSimTime", m_eventTotSimTime).isSuccess();
-      suc &= m_ntuple->addItem("TrackNum", m_trackNum, 0U, MAX_TRACK_NUM)
-                 .isSuccess();
-      suc &=
-          m_ntuple
-              ->addItem("TimePerSubdet", m_subdetNames.size(), m_timePerSubdet)
-              .isSuccess();
-      suc &=
-          m_ntuple->addIndexedItem("TrackLengths", m_trackNum, m_trackLengths)
-              .isSuccess();
-      suc &= m_ntuple->addIndexedItem("TrackTimes", m_trackNum, m_trackTimes)
-                 .isSuccess();
-      suc &= m_ntuple
-                 ->addIndexedItem("TrackLengthsOverTime", m_trackNum,
-                                  m_trackLengthsOverTime)
-                 .isSuccess();
-      suc &= m_ntuple->addIndexedItem("TrackPDGID", m_trackNum, m_trackPDGIds)
-                 .isSuccess();
+      if (m_enableTrackSimTimeRecording.value()) {
+        suc &= m_ntuple->addItem("TrackNum", m_trackNum, 0L, MAX_TRACK_NUM)
+                   .isSuccess();
+        suc &=
+            m_ntuple->addIndexedItem("TrackLengths", m_trackNum, m_trackLengths)
+                .isSuccess();
+        suc &= m_ntuple->addIndexedItem("TrackTimes", m_trackNum, m_trackTimes)
+                   .isSuccess();
+        suc &= m_ntuple->addIndexedItem("TrackPDGID", m_trackNum, m_trackPDGIds)
+                   .isSuccess();
+        suc &= m_ntuple
+                   ->addIndexedItem("TrackStartSubdet", m_trackNum,
+                                    m_trackStartSubdet)
+                   .isSuccess();
+        suc &=
+            m_ntuple
+                ->addIndexedItem("TrackEndSubdet", m_trackNum, m_trackEndSubdet)
+                .isSuccess();
+      }
+      if (m_enableSubdetSimTimeRecording.value()) {
+        suc &= m_ntuple
+                   ->addItem("TimePerSubdet", m_subdetNames.size(),
+                             m_timePerSubdet)
+                   .isSuccess();
+      }
     } else {
       error() << "Cannot book N-tuple:" << long(m_ntuple) << endmsg;
       return StatusCode::FAILURE;
@@ -87,7 +93,7 @@ StatusCode ChronoAnaElemTool::initNtuple() {
 }
 
 StatusCode ChronoAnaElemTool::initialize() {
-  bool suc = true;
+  bool suc = AlgTool::initialize().isSuccess();
 
   // Get services
   m_geomSvc = service<IGeomSvc>("GeomSvc");
@@ -108,13 +114,11 @@ StatusCode ChronoAnaElemTool::initialize() {
 }
 
 void ChronoAnaElemTool::PreUserTrackingAction(const G4Track *track) {
-  if (!m_enableTrackSimTimeRecording.value()) {
-    return;
-  }
-  if (m_eventNumber < m_skipFirstNEvents) {
+  if (!m_enableTrackSimTimeRecording.value() ||
+      m_eventNumber < m_skipFirstNEvents) {
     return;
   }
-
+  // info() << "Start Recording Track Num " << m_trackNum << endmsg;
   m_trackStartSubdet[m_trackNum] = getSubdetNameIdx(track->GetTouchable());
   m_track_start = NanoClock::now();
 }
@@ -128,8 +132,6 @@ void ChronoAnaElemTool::PostUserTrackingAction(const G4Track *track) {
   m_trackLengths[m_trackNum] = track->GetTrackLength();
   m_trackTimes[m_trackNum] =
       NanoClock::duration(track_stop - m_track_start).count();
-  m_trackLengthsOverTime[m_trackNum] =
-      m_trackLengths[m_trackNum] / m_trackTimes[m_trackNum];
   m_trackPDGIds[m_trackNum] = track->GetParticleDefinition()->GetPDGEncoding();
   m_trackEndSubdet[m_trackNum] = getSubdetNameIdx(track->GetTouchable());
   ++m_trackNum;
@@ -163,7 +165,8 @@ void ChronoAnaElemTool::UserSteppingAction(const G4Step *step) {
   }
   if (step->IsLastStepInVolume()) {
     auto subdet_stop = NanoClock::now();
-    m_timePerSubdet[getSubdetNameIdx(step->GetTrack()->GetTouchable())] +=
+    auto subDetNameIdx = getSubdetNameIdx(step->GetTrack()->GetTouchable());
+    m_timePerSubdet[subDetNameIdx] +=
         NanoClock::duration(subdet_stop - m_subdet_enter).count();
   } else if (step->IsFirstStepInVolume()) {
     m_subdet_enter = NanoClock::now();
@@ -172,36 +175,41 @@ void ChronoAnaElemTool::UserSteppingAction(const G4Step *step) {
 
 inline int ChronoAnaElemTool::getSubdetNameIdx(const G4VTouchable *touch) {
   auto historyDepthNum = touch->GetHistoryDepth();
-  for (int depth = 0; depth <= historyDepthNum; depth++) {
+  for (int depth = 0; depth <= historyDepthNum; ++depth) {
     auto vol = touch->GetVolume(depth);
     if (!vol) {
-      error() << "Volume is nullptr during finding " << depth << " in "
-              << historyDepthNum << endmsg;
-      return -1;
+      error() << "Volume is nullptr at depth " << depth << endmsg;
+      continue;
     }
-    auto volName = vol->GetName();
+    std::string_view volName{vol->GetName()};
 
-    // Check how many subdetector names are contained in the volume name
-    int foundCount = 0;
     int foundIndex = -1;
-
     for (size_t i = 0; i < m_subdetNames.size(); ++i) {
       if (volName.find(m_subdetNames[i]) != std::string::npos) {
-        foundCount++;
+        if (foundIndex != -1) {
+          // Found more than one match, report an error and return -1
+          error() << "Multiple matching subdetector names found in volume: "
+                  << volName << " depth: " << depth << endmsg;
+          return -1;
+        }
         foundIndex = static_cast<int>(i);
       }
     }
 
-    // Return index if exactly one match found, otherwise -1
-    return (foundCount == 1) ? foundIndex : -1;
+    // Found exactly one match, return the index
+    if (foundIndex != -1) {
+      // info() << "Found exactly one matching subdetector name in volume: "
+      //        << volName << " depth: " << depth << endmsg;
+      return foundIndex;
+    }
   }
-  error() << "Volume is nullptr after find history " << historyDepthNum
-          << endmsg;
+
+  error() << "No matching subdetector name found" << endmsg;
   return -1;
 }
 
 StatusCode ChronoAnaElemTool::finalize() {
-  StatusCode sc;
+  m_ntuple->reset();
 
-  return sc;
+  return AlgTool::finalize();
 }
\ No newline at end of file
diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.h b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
index 07407219..85e8ddf3 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.h
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
@@ -44,7 +44,7 @@ private:
   StatusCode initNtuple();
 
   NTuple::Item<long> m_eventNumber;
-  NTuple::Item<double> m_eventTotSimTime;
+  NTuple::Item<long> m_eventTotSimTime;
   NanoClock::time_point m_event_start;
 
   Gaudi::Property<int> m_skipFirstNEvents{this, "SkipFirstNEvents", 5};
@@ -58,18 +58,17 @@ private:
   Gaudi::Property<bool> m_enableSubdetSimTimeRecording{
       this, "EnableSubdetSimTimeRecording", false};
 
-  NTuple::Array<double> m_timePerSubdet;
+  NTuple::Array<long> m_timePerSubdet;
 
   // simtime over Track
-  static const unsigned MAX_TRACK_NUM = 25565;
+  static const long MAX_TRACK_NUM = 100'000'000; // 0.1B
   NanoClock::time_point m_track_start;
   Gaudi::Property<bool> m_enableTrackSimTimeRecording{
       this, "EnableTrackSimTimeRecording", false};
 
-  NTuple::Item<unsigned> m_trackNum;
+  NTuple::Item<long> m_trackNum;
   NTuple::Array<double> m_trackLengths;
-  NTuple::Array<double> m_trackTimes;
-  NTuple::Array<double> m_trackLengthsOverTime;
+  NTuple::Array<long> m_trackTimes;
   NTuple::Array<int> m_trackPDGIds;
   NTuple::Array<int> m_trackStartSubdet;
   NTuple::Array<int> m_trackEndSubdet;
-- 
GitLab


From 7c37a0e403f0d30fd4abd889a885f2135d028287 Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Sun, 25 May 2025 12:06:24 +0800
Subject: [PATCH 17/74] fix: allow multiple matching subdetector names

---
 Simulation/DetSimAna/src/ChronoAnaElemTool.cpp | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
index 73c8cd13..a4db8d2a 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -188,9 +188,10 @@ inline int ChronoAnaElemTool::getSubdetNameIdx(const G4VTouchable *touch) {
       if (volName.find(m_subdetNames[i]) != std::string::npos) {
         if (foundIndex != -1) {
           // Found more than one match, report an error and return -1
-          error() << "Multiple matching subdetector names found in volume: "
-                  << volName << " depth: " << depth << endmsg;
-          return -1;
+          info() << "Multiple matching subdetector names found in volume: "
+                  << volName << " depth: " << depth
+                  << " ,return first found index: " << foundIndex << endmsg;
+          return foundIndex;
         }
         foundIndex = static_cast<int>(i);
       }
-- 
GitLab


From cfe8aff182a8566b38f8b5c85b3d0a692799d815 Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Sun, 25 May 2025 12:08:03 +0800
Subject: [PATCH 18/74] fix: disallow multiple match

---
 Simulation/DetSimAna/src/ChronoAnaElemTool.cpp | 18 ++----------------
 1 file changed, 2 insertions(+), 16 deletions(-)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
index a4db8d2a..21f2ca43 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -183,26 +183,12 @@ inline int ChronoAnaElemTool::getSubdetNameIdx(const G4VTouchable *touch) {
     }
     std::string_view volName{vol->GetName()};
 
-    int foundIndex = -1;
     for (size_t i = 0; i < m_subdetNames.size(); ++i) {
       if (volName.find(m_subdetNames[i]) != std::string::npos) {
-        if (foundIndex != -1) {
-          // Found more than one match, report an error and return -1
-          info() << "Multiple matching subdetector names found in volume: "
-                  << volName << " depth: " << depth
-                  << " ,return first found index: " << foundIndex << endmsg;
-          return foundIndex;
-        }
-        foundIndex = static_cast<int>(i);
+        // Found a or more match, return the index
+        return static_cast<int>(i);
       }
     }
-
-    // Found exactly one match, return the index
-    if (foundIndex != -1) {
-      // info() << "Found exactly one matching subdetector name in volume: "
-      //        << volName << " depth: " << depth << endmsg;
-      return foundIndex;
-    }
   }
 
   error() << "No matching subdetector name found" << endmsg;
-- 
GitLab


From cfdd50fe4854cca4ca19c8834fd6ab620d80114f Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Sun, 25 May 2025 12:11:54 +0800
Subject: [PATCH 19/74] fix: use error to print subdet names

---
 .vscode                                        | 1 +
 Simulation/DetSimAna/src/ChronoAnaElemTool.cpp | 7 +++----
 2 files changed, 4 insertions(+), 4 deletions(-)
 create mode 160000 .vscode

diff --git a/.vscode b/.vscode
new file mode 160000
index 00000000..51256046
--- /dev/null
+++ b/.vscode
@@ -0,0 +1 @@
+Subproject commit 51256046f3df0e7c007be8bf892d69c1e9bd523b
diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
index 21f2ca43..a1e30788 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -31,12 +31,11 @@ StatusCode ChronoAnaElemTool::initSubdetNames() {
     m_subdetNames.emplace_back(name);
   }
 
-  // print subdets to log file
-
-  info() << "=== Subdetector Names List (" << m_subdetNames.size()
+  // print subdets to log file, use ERROR level for performance reasons
+  error() << "=== Subdetector Names List (" << m_subdetNames.size()
          << ") ===" << endmsg;
   for (size_t i = 0; i < m_subdetNames.size(); ++i) {
-    info() << "[" << std::setw(2) << i << "]  " << m_subdetNames[i] << endmsg;
+    error() << "[" << std::setw(2) << i << "]  " << m_subdetNames[i] << endmsg;
   }
 
   return StatusCode::SUCCESS;
-- 
GitLab


From ae6496831352fbf63c950d01bc3f987a2b2072c7 Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Sun, 25 May 2025 13:40:29 +0800
Subject: [PATCH 20/74] fix: add unknown subdet

---
 Simulation/DetSimAna/src/ChronoAnaElemTool.cpp | 11 ++++++++---
 Simulation/DetSimAna/src/ChronoAnaElemTool.h   |  1 +
 2 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
index a1e30788..86b33fd6 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -21,9 +21,10 @@ DECLARE_COMPONENT(ChronoAnaElemTool)
 StatusCode ChronoAnaElemTool::initSubdetNames() {
   const auto &children = m_geomSvc->getDD4HepGeo().children();
   m_subdetNames.clear();
-  m_subdetNames.reserve(children.size() + 1);
+  m_subdetNames.reserve(children.size() + 2); // add pWorld and Unknown subdet
 
   // add pWorld
+  m_subdetNames.emplace_back("Unknown");
   m_subdetNames.emplace_back("pWorld");
 
   // add subdets
@@ -33,7 +34,7 @@ StatusCode ChronoAnaElemTool::initSubdetNames() {
 
   // print subdets to log file, use ERROR level for performance reasons
   error() << "=== Subdetector Names List (" << m_subdetNames.size()
-         << ") ===" << endmsg;
+          << ") ===" << endmsg;
   for (size_t i = 0; i < m_subdetNames.size(); ++i) {
     error() << "[" << std::setw(2) << i << "]  " << m_subdetNames[i] << endmsg;
   }
@@ -191,7 +192,11 @@ inline int ChronoAnaElemTool::getSubdetNameIdx(const G4VTouchable *touch) {
   }
 
   error() << "No matching subdetector name found" << endmsg;
-  return -1;
+  for (int depth = 0; depth <= historyDepthNum; ++depth) {
+    auto vol = touch->GetVolume(depth);
+    error() << "Volume at depth " << depth << ": " << vol->GetName() << endmsg;
+  }
+  return UNKNOWN_SUBDET;
 }
 
 StatusCode ChronoAnaElemTool::finalize() {
diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.h b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
index 85e8ddf3..a804011a 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.h
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
@@ -51,6 +51,7 @@ private:
 
   // simtime over subdet
   StatusCode initSubdetNames();
+  static const int UNKNOWN_SUBDET = 0;
   std::vector<std::string> m_subdetNames;
   inline int getSubdetNameIdx(const G4VTouchable *);
 
-- 
GitLab


From ffb0773664104fca50c09623b99b47e3e73252f9 Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Sun, 25 May 2025 13:46:41 +0800
Subject: [PATCH 21/74] fix: search exclude unknown

---
 Simulation/DetSimAna/src/ChronoAnaElemTool.cpp | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
index 86b33fd6..81baa18d 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -183,7 +183,9 @@ inline int ChronoAnaElemTool::getSubdetNameIdx(const G4VTouchable *touch) {
     }
     std::string_view volName{vol->GetName()};
 
-    for (size_t i = 0; i < m_subdetNames.size(); ++i) {
+    // start from the second subdetector name, as the first one is always
+    // Unknown
+    for (size_t i = 1; i < m_subdetNames.size(); ++i) {
       if (volName.find(m_subdetNames[i]) != std::string::npos) {
         // Found a or more match, return the index
         return static_cast<int>(i);
-- 
GitLab


From 3fca1c731ff1b77d91015f787e2447a2e4bcdcbb Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Sun, 25 May 2025 13:56:44 +0800
Subject: [PATCH 22/74] fix: replace int with size_t

---
 .../DetSimAna/src/ChronoAnaElemTool.cpp       | 21 ++++++++++---------
 Simulation/DetSimAna/src/ChronoAnaElemTool.h  | 17 ++++++++-------
 2 files changed, 20 insertions(+), 18 deletions(-)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
index 81baa18d..e921b4ad 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -2,6 +2,7 @@
 #include <cassert>
 #include <cerrno>
 #include <climits>
+#include <cstddef>
 
 #include "ChronoAnaElemTool.h"
 
@@ -55,11 +56,11 @@ StatusCode ChronoAnaElemTool::initNtuple() {
                                  "Simulation ChronoMetrics");
     if (m_ntuple) {
       info() << "Create Ntuple Success" << endmsg;
-      suc &= m_ntuple->addItem("EventNumber", m_eventNumber, 0L, LONG_MAX)
+      suc &= m_ntuple->addItem("EventNumber", m_eventNumber, 0UL, ULONG_MAX)
                  .isSuccess();
       suc &= m_ntuple->addItem("TotSimTime", m_eventTotSimTime).isSuccess();
       if (m_enableTrackSimTimeRecording.value()) {
-        suc &= m_ntuple->addItem("TrackNum", m_trackNum, 0L, MAX_TRACK_NUM)
+        suc &= m_ntuple->addItem("TrackNum", m_trackNum, 0UL, MAX_TRACK_NUM)
                    .isSuccess();
         suc &=
             m_ntuple->addIndexedItem("TrackLengths", m_trackNum, m_trackLengths)
@@ -115,7 +116,7 @@ StatusCode ChronoAnaElemTool::initialize() {
 
 void ChronoAnaElemTool::PreUserTrackingAction(const G4Track *track) {
   if (!m_enableTrackSimTimeRecording.value() ||
-      m_eventNumber < m_skipFirstNEvents) {
+      m_eventNumber < m_skipFirstNEvents.value()) {
     return;
   }
   // info() << "Start Recording Track Num " << m_trackNum << endmsg;
@@ -125,7 +126,7 @@ void ChronoAnaElemTool::PreUserTrackingAction(const G4Track *track) {
 
 void ChronoAnaElemTool::PostUserTrackingAction(const G4Track *track) {
   if (!m_enableTrackSimTimeRecording.value() ||
-      m_eventNumber < m_skipFirstNEvents) {
+      m_eventNumber < m_skipFirstNEvents.value()) {
     return;
   }
   NanoClock::time_point track_stop = NanoClock::now();
@@ -138,7 +139,7 @@ void ChronoAnaElemTool::PostUserTrackingAction(const G4Track *track) {
 }
 
 void ChronoAnaElemTool::EndOfEventAction(const G4Event *) {
-  if (m_eventNumber < m_skipFirstNEvents) {
+  if (m_eventNumber < m_skipFirstNEvents.value()) {
     return;
   }
   NanoClock::time_point event_stop = NanoClock::now();
@@ -150,8 +151,8 @@ void ChronoAnaElemTool::EndOfEventAction(const G4Event *) {
 
 void ChronoAnaElemTool::BeginOfEventAction(const G4Event *event) {
   m_ntuple->reset();
-  m_eventNumber = event->GetEventID();
-  if (m_eventNumber < m_skipFirstNEvents) {
+  m_eventNumber = static_cast<size_t>(event->GetEventID());
+  if (m_eventNumber < m_skipFirstNEvents.value()) {
     return;
   }
 
@@ -160,7 +161,7 @@ void ChronoAnaElemTool::BeginOfEventAction(const G4Event *event) {
 
 void ChronoAnaElemTool::UserSteppingAction(const G4Step *step) {
   if (!m_enableSubdetSimTimeRecording.value() ||
-      m_eventNumber < m_skipFirstNEvents) {
+      m_eventNumber < m_skipFirstNEvents.value()) {
     return;
   }
   if (step->IsLastStepInVolume()) {
@@ -173,7 +174,7 @@ void ChronoAnaElemTool::UserSteppingAction(const G4Step *step) {
   }
 }
 
-inline int ChronoAnaElemTool::getSubdetNameIdx(const G4VTouchable *touch) {
+inline size_t ChronoAnaElemTool::getSubdetNameIdx(const G4VTouchable *touch) {
   auto historyDepthNum = touch->GetHistoryDepth();
   for (int depth = 0; depth <= historyDepthNum; ++depth) {
     auto vol = touch->GetVolume(depth);
@@ -188,7 +189,7 @@ inline int ChronoAnaElemTool::getSubdetNameIdx(const G4VTouchable *touch) {
     for (size_t i = 1; i < m_subdetNames.size(); ++i) {
       if (volName.find(m_subdetNames[i]) != std::string::npos) {
         // Found a or more match, return the index
-        return static_cast<int>(i);
+        return i;
       }
     }
   }
diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.h b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
index a804011a..a29178ea 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.h
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
@@ -3,6 +3,7 @@
 
 #include <G4VTouchable.hh>
 #include <chrono>
+#include <cstddef>
 #include <string>
 #include <vector>
 
@@ -43,17 +44,17 @@ private:
   NTuple::Tuple *m_ntuple;
   StatusCode initNtuple();
 
-  NTuple::Item<long> m_eventNumber;
+  NTuple::Item<size_t> m_eventNumber;
   NTuple::Item<long> m_eventTotSimTime;
   NanoClock::time_point m_event_start;
 
-  Gaudi::Property<int> m_skipFirstNEvents{this, "SkipFirstNEvents", 5};
+  Gaudi::Property<size_t> m_skipFirstNEvents{this, "SkipFirstNEvents", 5};
 
   // simtime over subdet
   StatusCode initSubdetNames();
-  static const int UNKNOWN_SUBDET = 0;
+  static const size_t UNKNOWN_SUBDET = 0;
   std::vector<std::string> m_subdetNames;
-  inline int getSubdetNameIdx(const G4VTouchable *);
+  inline size_t getSubdetNameIdx(const G4VTouchable *);
 
   NanoClock::time_point m_subdet_enter;
   Gaudi::Property<bool> m_enableSubdetSimTimeRecording{
@@ -62,17 +63,17 @@ private:
   NTuple::Array<long> m_timePerSubdet;
 
   // simtime over Track
-  static const long MAX_TRACK_NUM = 100'000'000; // 0.1B
+  static const size_t MAX_TRACK_NUM = 100'000'000; // 0.1B
   NanoClock::time_point m_track_start;
   Gaudi::Property<bool> m_enableTrackSimTimeRecording{
       this, "EnableTrackSimTimeRecording", false};
 
-  NTuple::Item<long> m_trackNum;
+  NTuple::Item<size_t> m_trackNum;
   NTuple::Array<double> m_trackLengths;
   NTuple::Array<long> m_trackTimes;
   NTuple::Array<int> m_trackPDGIds;
-  NTuple::Array<int> m_trackStartSubdet;
-  NTuple::Array<int> m_trackEndSubdet;
+  NTuple::Array<size_t> m_trackStartSubdet;
+  NTuple::Array<size_t> m_trackEndSubdet;
 };
 
 #endif
-- 
GitLab


From 77a8dd7c3647e5adb6554611fe78a202c8801222 Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Sun, 25 May 2025 14:14:58 +0800
Subject: [PATCH 23/74] fix: manage index uniformly

---
 Simulation/DetSimAna/src/ChronoAnaElemTool.cpp | 10 +++++-----
 Simulation/DetSimAna/src/ChronoAnaElemTool.h   |  6 ++++--
 2 files changed, 9 insertions(+), 7 deletions(-)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
index e921b4ad..810dd094 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -22,7 +22,7 @@ DECLARE_COMPONENT(ChronoAnaElemTool)
 StatusCode ChronoAnaElemTool::initSubdetNames() {
   const auto &children = m_geomSvc->getDD4HepGeo().children();
   m_subdetNames.clear();
-  m_subdetNames.reserve(children.size() + 2); // add pWorld and Unknown subdet
+  m_subdetNames.reserve(children.size() + SpecialCount); // add pWorld and Unknown subdet
 
   // add pWorld
   m_subdetNames.emplace_back("Unknown");
@@ -37,7 +37,7 @@ StatusCode ChronoAnaElemTool::initSubdetNames() {
   error() << "=== Subdetector Names List (" << m_subdetNames.size()
           << ") ===" << endmsg;
   for (size_t i = 0; i < m_subdetNames.size(); ++i) {
-    error() << "[" << std::setw(2) << i << "]  " << m_subdetNames[i] << endmsg;
+    error() << "[" << i << "]  " << m_subdetNames[i] << endmsg;
   }
 
   return StatusCode::SUCCESS;
@@ -184,9 +184,9 @@ inline size_t ChronoAnaElemTool::getSubdetNameIdx(const G4VTouchable *touch) {
     }
     std::string_view volName{vol->GetName()};
 
-    // start from the second subdetector name, as the first one is always
+    // start from the PWorldIndex subdetector name, as the first one is always
     // Unknown
-    for (size_t i = 1; i < m_subdetNames.size(); ++i) {
+    for (size_t i = PWorldIndex; i < m_subdetNames.size(); ++i) {
       if (volName.find(m_subdetNames[i]) != std::string::npos) {
         // Found a or more match, return the index
         return i;
@@ -199,7 +199,7 @@ inline size_t ChronoAnaElemTool::getSubdetNameIdx(const G4VTouchable *touch) {
     auto vol = touch->GetVolume(depth);
     error() << "Volume at depth " << depth << ": " << vol->GetName() << endmsg;
   }
-  return UNKNOWN_SUBDET;
+  return UnknownIndex;
 }
 
 StatusCode ChronoAnaElemTool::finalize() {
diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.h b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
index a29178ea..2d58eeae 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.h
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
@@ -52,7 +52,9 @@ private:
 
   // simtime over subdet
   StatusCode initSubdetNames();
-  static const size_t UNKNOWN_SUBDET = 0;
+  static constexpr size_t UnknownIndex = 0;
+  static constexpr size_t PWorldIndex = 1;
+  static constexpr size_t SpecialCount = 2;
   std::vector<std::string> m_subdetNames;
   inline size_t getSubdetNameIdx(const G4VTouchable *);
 
@@ -63,7 +65,7 @@ private:
   NTuple::Array<long> m_timePerSubdet;
 
   // simtime over Track
-  static const size_t MAX_TRACK_NUM = 100'000'000; // 0.1B
+  static constexpr size_t MAX_TRACK_NUM = 100'000'000; // 0.1B
   NanoClock::time_point m_track_start;
   Gaudi::Property<bool> m_enableTrackSimTimeRecording{
       this, "EnableTrackSimTimeRecording", false};
-- 
GitLab


From 0cc5af9608ef2684dc80d5fb3acd6757bdc84b99 Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Sun, 25 May 2025 14:20:20 +0800
Subject: [PATCH 24/74] fix: init subdet names anyway

---
 Simulation/DetSimAna/src/ChronoAnaElemTool.cpp | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
index 810dd094..e9dbc3dd 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -107,9 +107,7 @@ StatusCode ChronoAnaElemTool::initialize() {
   NanoClock::calibrate();
 
   // init subdet names and number
-  if (m_enableSubdetSimTimeRecording.value()) {
-    suc &= initSubdetNames().isSuccess();
-  }
+  suc &= initSubdetNames().isSuccess();
   suc &= initNtuple().isSuccess();
   return suc ? StatusCode::SUCCESS : StatusCode::FAILURE;
 }
-- 
GitLab


From 92a9791608fae8e2b313d5e35c8dacd005295581 Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Sun, 25 May 2025 15:07:10 +0800
Subject: [PATCH 25/74] fix: track num limit

---
 Simulation/DetSimAna/src/ChronoAnaElemTool.cpp | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
index e9dbc3dd..bccf8016 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -22,7 +22,8 @@ DECLARE_COMPONENT(ChronoAnaElemTool)
 StatusCode ChronoAnaElemTool::initSubdetNames() {
   const auto &children = m_geomSvc->getDD4HepGeo().children();
   m_subdetNames.clear();
-  m_subdetNames.reserve(children.size() + SpecialCount); // add pWorld and Unknown subdet
+  m_subdetNames.reserve(children.size() +
+                        SpecialCount); // add pWorld and Unknown subdet
 
   // add pWorld
   m_subdetNames.emplace_back("Unknown");
@@ -117,7 +118,10 @@ void ChronoAnaElemTool::PreUserTrackingAction(const G4Track *track) {
       m_eventNumber < m_skipFirstNEvents.value()) {
     return;
   }
-  // info() << "Start Recording Track Num " << m_trackNum << endmsg;
+  if (m_trackNum >= MAX_TRACK_NUM) {
+    error() << "Exceeded MAX_TRACK_NUM at event " << m_eventNumber << endmsg;
+    return;
+  }
   m_trackStartSubdet[m_trackNum] = getSubdetNameIdx(track->GetTouchable());
   m_track_start = NanoClock::now();
 }
-- 
GitLab


From a109d4f3f5223f15f68b526cff576726045506ce Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Sun, 25 May 2025 15:08:29 +0800
Subject: [PATCH 26/74] fix: remove meaningless output

---
 Simulation/DetSimAna/src/ChronoAnaElemTool.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
index bccf8016..df3f3334 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -86,7 +86,7 @@ StatusCode ChronoAnaElemTool::initNtuple() {
                    .isSuccess();
       }
     } else {
-      error() << "Cannot book N-tuple:" << long(m_ntuple) << endmsg;
+      error() << "Cannot book N-tuple." << endmsg;
       return StatusCode::FAILURE;
     }
   }
-- 
GitLab


From c9616be10bc786047cb58975fc317f3a7f28a816 Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Sun, 25 May 2025 16:49:29 +0800
Subject: [PATCH 27/74] fix: throw error when exceed

---
 Simulation/DetSimAna/src/ChronoAnaElemTool.cpp | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
index df3f3334..25c55d63 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -3,6 +3,8 @@
 #include <cerrno>
 #include <climits>
 #include <cstddef>
+#include <cstdlib>
+#include <string>
 
 #include "ChronoAnaElemTool.h"
 
@@ -11,6 +13,7 @@
 #include "G4Track.hh"
 #include "GaudiAlg/GaudiAlgorithm.h"
 #include "GaudiKernel/ClassID.h"
+#include "GaudiKernel/GaudiException.h"
 #include "GaudiKernel/MsgStream.h"
 #include "GaudiKernel/NTuple.h"
 #include "GaudiKernel/SmartIF.h"
@@ -120,7 +123,9 @@ void ChronoAnaElemTool::PreUserTrackingAction(const G4Track *track) {
   }
   if (m_trackNum >= MAX_TRACK_NUM) {
     error() << "Exceeded MAX_TRACK_NUM at event " << m_eventNumber << endmsg;
-    return;
+    throw GaudiException("Exceeded MAX_TRACK_NUM at event " +
+                             std::to_string(m_eventNumber),
+                         "ChronoAnaElemTool", StatusCode::FAILURE);
   }
   m_trackStartSubdet[m_trackNum] = getSubdetNameIdx(track->GetTouchable());
   m_track_start = NanoClock::now();
-- 
GitLab


From 2a6aa455a28ccdf0e7f16cfeb016d93560c15e30 Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Mon, 26 May 2025 12:13:42 +0800
Subject: [PATCH 28/74] fix: make time to int64

---
 Simulation/DetSimAna/src/ChronoAnaElemTool.h | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.h b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
index 2d58eeae..6b5887c5 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.h
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
@@ -45,7 +45,7 @@ private:
   StatusCode initNtuple();
 
   NTuple::Item<size_t> m_eventNumber;
-  NTuple::Item<long> m_eventTotSimTime;
+  NTuple::Item<long long> m_eventTotSimTime;
   NanoClock::time_point m_event_start;
 
   Gaudi::Property<size_t> m_skipFirstNEvents{this, "SkipFirstNEvents", 5};
@@ -62,7 +62,7 @@ private:
   Gaudi::Property<bool> m_enableSubdetSimTimeRecording{
       this, "EnableSubdetSimTimeRecording", false};
 
-  NTuple::Array<long> m_timePerSubdet;
+  NTuple::Array<long long> m_timePerSubdet;
 
   // simtime over Track
   static constexpr size_t MAX_TRACK_NUM = 100'000'000; // 0.1B
@@ -72,7 +72,7 @@ private:
 
   NTuple::Item<size_t> m_trackNum;
   NTuple::Array<double> m_trackLengths;
-  NTuple::Array<long> m_trackTimes;
+  NTuple::Array<long long> m_trackTimes;
   NTuple::Array<int> m_trackPDGIds;
   NTuple::Array<size_t> m_trackStartSubdet;
   NTuple::Array<size_t> m_trackEndSubdet;
-- 
GitLab


From efbfa48f438a4498dbfb2ab2607a2d986742626b Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Mon, 26 May 2025 12:24:10 +0800
Subject: [PATCH 29/74] fix: make size clear

---
 Simulation/DetSimAna/src/ChronoAnaElemTool.cpp | 8 ++++----
 Simulation/DetSimAna/src/ChronoAnaElemTool.h   | 9 +++++----
 2 files changed, 9 insertions(+), 8 deletions(-)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
index 25c55d63..bb18c34f 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -2,7 +2,6 @@
 #include <cassert>
 #include <cerrno>
 #include <climits>
-#include <cstddef>
 #include <cstdlib>
 #include <string>
 
@@ -60,11 +59,11 @@ StatusCode ChronoAnaElemTool::initNtuple() {
                                  "Simulation ChronoMetrics");
     if (m_ntuple) {
       info() << "Create Ntuple Success" << endmsg;
-      suc &= m_ntuple->addItem("EventNumber", m_eventNumber, 0UL, ULONG_MAX)
+      suc &= m_ntuple->addItem("EventNumber", m_eventNumber, 0U, UINT_MAX)
                  .isSuccess();
       suc &= m_ntuple->addItem("TotSimTime", m_eventTotSimTime).isSuccess();
       if (m_enableTrackSimTimeRecording.value()) {
-        suc &= m_ntuple->addItem("TrackNum", m_trackNum, 0UL, MAX_TRACK_NUM)
+        suc &= m_ntuple->addItem("TrackNum", m_trackNum, 0U, MAX_TRACK_NUM)
                    .isSuccess();
         suc &=
             m_ntuple->addIndexedItem("TrackLengths", m_trackNum, m_trackLengths)
@@ -181,7 +180,8 @@ void ChronoAnaElemTool::UserSteppingAction(const G4Step *step) {
   }
 }
 
-inline size_t ChronoAnaElemTool::getSubdetNameIdx(const G4VTouchable *touch) {
+inline ChronoAnaElemTool::size_t
+ChronoAnaElemTool::getSubdetNameIdx(const G4VTouchable *touch) {
   auto historyDepthNum = touch->GetHistoryDepth();
   for (int depth = 0; depth <= historyDepthNum; ++depth) {
     auto vol = touch->GetVolume(depth);
diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.h b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
index 6b5887c5..974cad3f 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.h
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
@@ -3,7 +3,6 @@
 
 #include <G4VTouchable.hh>
 #include <chrono>
-#include <cstddef>
 #include <string>
 #include <vector>
 
@@ -21,6 +20,8 @@ class ChronoAnaElemTool : public extends<AlgTool, IAnaElemTool> {
 public:
   using extends::extends;
   using NanoClock = Gaudi::Timers::RdtscClock<std::chrono::nanoseconds>;
+  typedef unsigned long long time_t;
+  typedef unsigned int size_t;
 
   // Event
   virtual void BeginOfEventAction(const G4Event *) override;
@@ -45,7 +46,7 @@ private:
   StatusCode initNtuple();
 
   NTuple::Item<size_t> m_eventNumber;
-  NTuple::Item<long long> m_eventTotSimTime;
+  NTuple::Item<time_t> m_eventTotSimTime;
   NanoClock::time_point m_event_start;
 
   Gaudi::Property<size_t> m_skipFirstNEvents{this, "SkipFirstNEvents", 5};
@@ -62,7 +63,7 @@ private:
   Gaudi::Property<bool> m_enableSubdetSimTimeRecording{
       this, "EnableSubdetSimTimeRecording", false};
 
-  NTuple::Array<long long> m_timePerSubdet;
+  NTuple::Array<time_t> m_timePerSubdet;
 
   // simtime over Track
   static constexpr size_t MAX_TRACK_NUM = 100'000'000; // 0.1B
@@ -72,7 +73,7 @@ private:
 
   NTuple::Item<size_t> m_trackNum;
   NTuple::Array<double> m_trackLengths;
-  NTuple::Array<long long> m_trackTimes;
+  NTuple::Array<time_t> m_trackTimes;
   NTuple::Array<int> m_trackPDGIds;
   NTuple::Array<size_t> m_trackStartSubdet;
   NTuple::Array<size_t> m_trackEndSubdet;
-- 
GitLab


From 014b5d84565f79d8e351141d9386995a4aebdd44 Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Tue, 27 May 2025 11:36:03 +0800
Subject: [PATCH 30/74] fix: add debug to log out subdets

---
 Simulation/DetSimAna/src/ChronoAnaElemTool.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
index bb18c34f..bc22fb19 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -196,6 +196,8 @@ ChronoAnaElemTool::getSubdetNameIdx(const G4VTouchable *touch) {
     for (size_t i = PWorldIndex; i < m_subdetNames.size(); ++i) {
       if (volName.find(m_subdetNames[i]) != std::string::npos) {
         // Found a or more match, return the index
+        // info() << "Found subdetector name: " << m_subdetNames[i] << " at depth "
+        //        << depth << " with volName: " << volName << endmsg;
         return i;
       }
     }
-- 
GitLab


From 408004fb82d2094a3e142255873e9934e71accf2 Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Tue, 27 May 2025 14:47:09 +0800
Subject: [PATCH 31/74] fix: subdet alg renewed

---
 Simulation/DetSimAna/src/ChronoAnaElemTool.cpp | 15 +++++++++------
 Simulation/DetSimAna/src/ChronoAnaElemTool.h   |  3 +++
 2 files changed, 12 insertions(+), 6 deletions(-)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
index bc22fb19..7268d2c1 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -170,12 +170,14 @@ void ChronoAnaElemTool::UserSteppingAction(const G4Step *step) {
       m_eventNumber < m_skipFirstNEvents.value()) {
     return;
   }
-  if (step->IsLastStepInVolume()) {
-    auto subdet_stop = NanoClock::now();
+  if (step->IsFirstStepInVolume()) {
+    if (m_currentSubdetIndex != UninitializedIndex) {
+      auto subdet_stop = NanoClock::now();
+      m_timePerSubdet[m_currentSubdetIndex] +=
+          NanoClock::duration(subdet_stop - m_subdet_enter).count();
+    }
     auto subDetNameIdx = getSubdetNameIdx(step->GetTrack()->GetTouchable());
-    m_timePerSubdet[subDetNameIdx] +=
-        NanoClock::duration(subdet_stop - m_subdet_enter).count();
-  } else if (step->IsFirstStepInVolume()) {
+    m_currentSubdetIndex = subDetNameIdx;
     m_subdet_enter = NanoClock::now();
   }
 }
@@ -196,7 +198,8 @@ ChronoAnaElemTool::getSubdetNameIdx(const G4VTouchable *touch) {
     for (size_t i = PWorldIndex; i < m_subdetNames.size(); ++i) {
       if (volName.find(m_subdetNames[i]) != std::string::npos) {
         // Found a or more match, return the index
-        // info() << "Found subdetector name: " << m_subdetNames[i] << " at depth "
+        // info() << "Found subdetector name: " << m_subdetNames[i] << " at
+        // depth "
         //        << depth << " with volName: " << volName << endmsg;
         return i;
       }
diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.h b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
index 974cad3f..3eadcabd 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.h
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
@@ -3,6 +3,7 @@
 
 #include <G4VTouchable.hh>
 #include <chrono>
+#include <climits>
 #include <string>
 #include <vector>
 
@@ -56,8 +57,10 @@ private:
   static constexpr size_t UnknownIndex = 0;
   static constexpr size_t PWorldIndex = 1;
   static constexpr size_t SpecialCount = 2;
+  static constexpr size_t UninitializedIndex = UINT_MAX;
   std::vector<std::string> m_subdetNames;
   inline size_t getSubdetNameIdx(const G4VTouchable *);
+  size_t m_currentSubdetIndex = UninitializedIndex;
 
   NanoClock::time_point m_subdet_enter;
   Gaudi::Property<bool> m_enableSubdetSimTimeRecording{
-- 
GitLab


From a6318b45e53adcbbc880174f84198e7497076458 Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Tue, 27 May 2025 16:23:35 +0800
Subject: [PATCH 32/74] fix: add event number

---
 Simulation/DetSimAna/src/ChronoAnaElemTool.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
index 7268d2c1..1638d002 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -158,6 +158,7 @@ void ChronoAnaElemTool::EndOfEventAction(const G4Event *) {
 void ChronoAnaElemTool::BeginOfEventAction(const G4Event *event) {
   m_ntuple->reset();
   m_eventNumber = static_cast<size_t>(event->GetEventID());
+  always() << "ChronoAnaElemTool: Event number: " << m_eventNumber << endmsg;
   if (m_eventNumber < m_skipFirstNEvents.value()) {
     return;
   }
-- 
GitLab


From a1c6a20968c8c85e85be4711981cbd8733a26efb Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Wed, 28 May 2025 10:34:37 +0800
Subject: [PATCH 33/74] fix: add track step num

---
 Simulation/DetSimAna/src/ChronoAnaElemTool.cpp | 4 ++++
 Simulation/DetSimAna/src/ChronoAnaElemTool.h   | 1 +
 2 files changed, 5 insertions(+)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
index 1638d002..c071dddc 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -80,6 +80,9 @@ StatusCode ChronoAnaElemTool::initNtuple() {
             m_ntuple
                 ->addIndexedItem("TrackEndSubdet", m_trackNum, m_trackEndSubdet)
                 .isSuccess();
+        suc &=
+            m_ntuple->addIndexedItem("TrackStepNum", m_trackNum, m_trackStepNum)
+                .isSuccess();
       }
       if (m_enableSubdetSimTimeRecording.value()) {
         suc &= m_ntuple
@@ -139,6 +142,7 @@ void ChronoAnaElemTool::PostUserTrackingAction(const G4Track *track) {
   m_trackLengths[m_trackNum] = track->GetTrackLength();
   m_trackTimes[m_trackNum] =
       NanoClock::duration(track_stop - m_track_start).count();
+  m_trackStepNum[m_trackNum] = track->GetCurrentStepNumber();
   m_trackPDGIds[m_trackNum] = track->GetParticleDefinition()->GetPDGEncoding();
   m_trackEndSubdet[m_trackNum] = getSubdetNameIdx(track->GetTouchable());
   ++m_trackNum;
diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.h b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
index 3eadcabd..f5d01e9a 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.h
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
@@ -77,6 +77,7 @@ private:
   NTuple::Item<size_t> m_trackNum;
   NTuple::Array<double> m_trackLengths;
   NTuple::Array<time_t> m_trackTimes;
+  NTuple::Array<int> m_trackStepNum;
   NTuple::Array<int> m_trackPDGIds;
   NTuple::Array<size_t> m_trackStartSubdet;
   NTuple::Array<size_t> m_trackEndSubdet;
-- 
GitLab


From 880baa65beed4582c1a7474f4670a7698860be4c Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Wed, 28 May 2025 16:33:00 +0800
Subject: [PATCH 34/74] fix: add per subdet time to track

---
 .../DetSimAna/src/ChronoAnaElemTool.cpp       | 88 ++++++++++---------
 Simulation/DetSimAna/src/ChronoAnaElemTool.h  | 13 ++-
 2 files changed, 52 insertions(+), 49 deletions(-)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
index c071dddc..4abdb318 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -1,3 +1,4 @@
+#include <G4StepStatus.hh>
 #include <GaudiKernel/StatusCode.h>
 #include <cassert>
 #include <cerrno>
@@ -43,6 +44,11 @@ StatusCode ChronoAnaElemTool::initSubdetNames() {
     error() << "[" << i << "]  " << m_subdetNames[i] << endmsg;
   }
 
+  if (m_subdetNames.size() > MAX_SUBDET_NUM) {
+    throw GaudiException("m_subdetNames size exceeded MAX_SUBDET_NUM",
+                         "ChronoAnaElemTool", StatusCode::FAILURE);
+  }
+
   return StatusCode::SUCCESS;
 }
 
@@ -62,34 +68,34 @@ StatusCode ChronoAnaElemTool::initNtuple() {
       suc &= m_ntuple->addItem("EventNumber", m_eventNumber, 0U, UINT_MAX)
                  .isSuccess();
       suc &= m_ntuple->addItem("TotSimTime", m_eventTotSimTime).isSuccess();
-      if (m_enableTrackSimTimeRecording.value()) {
-        suc &= m_ntuple->addItem("TrackNum", m_trackNum, 0U, MAX_TRACK_NUM)
-                   .isSuccess();
-        suc &=
-            m_ntuple->addIndexedItem("TrackLengths", m_trackNum, m_trackLengths)
-                .isSuccess();
-        suc &= m_ntuple->addIndexedItem("TrackTimes", m_trackNum, m_trackTimes)
-                   .isSuccess();
-        suc &= m_ntuple->addIndexedItem("TrackPDGID", m_trackNum, m_trackPDGIds)
-                   .isSuccess();
-        suc &= m_ntuple
-                   ->addIndexedItem("TrackStartSubdet", m_trackNum,
-                                    m_trackStartSubdet)
-                   .isSuccess();
-        suc &=
-            m_ntuple
-                ->addIndexedItem("TrackEndSubdet", m_trackNum, m_trackEndSubdet)
-                .isSuccess();
-        suc &=
-            m_ntuple->addIndexedItem("TrackStepNum", m_trackNum, m_trackStepNum)
-                .isSuccess();
-      }
-      if (m_enableSubdetSimTimeRecording.value()) {
-        suc &= m_ntuple
-                   ->addItem("TimePerSubdet", m_subdetNames.size(),
-                             m_timePerSubdet)
-                   .isSuccess();
-      }
+      suc &= m_ntuple->addItem("TrackNum", m_trackNum, 0U, MAX_TRACK_NUM)
+                 .isSuccess();
+      suc &=
+          m_ntuple->addIndexedItem("TrackLengths", m_trackNum, m_trackLengths)
+              .isSuccess();
+      suc &= m_ntuple->addIndexedItem("TrackTimes", m_trackNum, m_trackTimes)
+                 .isSuccess();
+      suc &= m_ntuple
+                 ->addIndexedItem("TrackTimeOverhead", m_trackNum,
+                                  m_trackTimeOverhead)
+                 .isSuccess();
+      suc &= m_ntuple->addIndexedItem("TrackPDGID", m_trackNum, m_trackPDGIds)
+                 .isSuccess();
+      suc &= m_ntuple
+                 ->addIndexedItem("TrackStartSubdet", m_trackNum,
+                                  m_trackStartSubdet)
+                 .isSuccess();
+      suc &=
+          m_ntuple
+              ->addIndexedItem("TrackEndSubdet", m_trackNum, m_trackEndSubdet)
+              .isSuccess();
+      suc &=
+          m_ntuple->addIndexedItem("TrackStepNum", m_trackNum, m_trackStepNum)
+              .isSuccess();
+      suc &= m_ntuple
+                 ->addIndexedItem("TrackTimePerSubdet", m_trackNum,
+                                  MAX_SUBDET_NUM, m_trackTimePerSubdet)
+                 .isSuccess();
     } else {
       error() << "Cannot book N-tuple." << endmsg;
       return StatusCode::FAILURE;
@@ -119,8 +125,7 @@ StatusCode ChronoAnaElemTool::initialize() {
 }
 
 void ChronoAnaElemTool::PreUserTrackingAction(const G4Track *track) {
-  if (!m_enableTrackSimTimeRecording.value() ||
-      m_eventNumber < m_skipFirstNEvents.value()) {
+  if (m_eventNumber < m_skipFirstNEvents.value()) {
     return;
   }
   if (m_trackNum >= MAX_TRACK_NUM) {
@@ -129,13 +134,14 @@ void ChronoAnaElemTool::PreUserTrackingAction(const G4Track *track) {
                              std::to_string(m_eventNumber),
                          "ChronoAnaElemTool", StatusCode::FAILURE);
   }
-  m_trackStartSubdet[m_trackNum] = getSubdetNameIdx(track->GetTouchable());
-  m_track_start = NanoClock::now();
+  m_currentSubdetIndex = getSubdetNameIdx(track->GetTouchable());
+  m_trackStartSubdet[m_trackNum] = m_currentSubdetIndex;
+  m_step_overhead = m_step_overhead.zero();
+  m_track_start = m_subdet_enter = NanoClock::now();
 }
 
 void ChronoAnaElemTool::PostUserTrackingAction(const G4Track *track) {
-  if (!m_enableTrackSimTimeRecording.value() ||
-      m_eventNumber < m_skipFirstNEvents.value()) {
+  if (m_eventNumber < m_skipFirstNEvents.value()) {
     return;
   }
   NanoClock::time_point track_stop = NanoClock::now();
@@ -145,6 +151,8 @@ void ChronoAnaElemTool::PostUserTrackingAction(const G4Track *track) {
   m_trackStepNum[m_trackNum] = track->GetCurrentStepNumber();
   m_trackPDGIds[m_trackNum] = track->GetParticleDefinition()->GetPDGEncoding();
   m_trackEndSubdet[m_trackNum] = getSubdetNameIdx(track->GetTouchable());
+  m_trackTimeOverhead[m_trackNum] =
+      NanoClock::duration(m_step_overhead).count();
   ++m_trackNum;
 }
 
@@ -171,19 +179,17 @@ void ChronoAnaElemTool::BeginOfEventAction(const G4Event *event) {
 }
 
 void ChronoAnaElemTool::UserSteppingAction(const G4Step *step) {
-  if (!m_enableSubdetSimTimeRecording.value() ||
-      m_eventNumber < m_skipFirstNEvents.value()) {
+  if (m_eventNumber < m_skipFirstNEvents.value()) {
     return;
   }
   if (step->IsFirstStepInVolume()) {
-    if (m_currentSubdetIndex != UninitializedIndex) {
-      auto subdet_stop = NanoClock::now();
-      m_timePerSubdet[m_currentSubdetIndex] +=
-          NanoClock::duration(subdet_stop - m_subdet_enter).count();
-    }
+    auto subdet_stop = NanoClock::now();
+    m_trackTimePerSubdet[m_trackNum][m_currentSubdetIndex] +=
+        NanoClock::duration(subdet_stop - m_subdet_enter).count();
     auto subDetNameIdx = getSubdetNameIdx(step->GetTrack()->GetTouchable());
     m_currentSubdetIndex = subDetNameIdx;
     m_subdet_enter = NanoClock::now();
+    m_step_overhead += (m_subdet_enter - subdet_stop);
   }
 }
 
diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.h b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
index f5d01e9a..968a13f4 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.h
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
@@ -62,21 +62,18 @@ private:
   inline size_t getSubdetNameIdx(const G4VTouchable *);
   size_t m_currentSubdetIndex = UninitializedIndex;
 
-  NanoClock::time_point m_subdet_enter;
-  Gaudi::Property<bool> m_enableSubdetSimTimeRecording{
-      this, "EnableSubdetSimTimeRecording", false};
-
-  NTuple::Array<time_t> m_timePerSubdet;
-
   // simtime over Track
   static constexpr size_t MAX_TRACK_NUM = 100'000'000; // 0.1B
+  static constexpr size_t MAX_SUBDET_NUM = 30;
   NanoClock::time_point m_track_start;
-  Gaudi::Property<bool> m_enableTrackSimTimeRecording{
-      this, "EnableTrackSimTimeRecording", false};
+  NanoClock::time_point m_subdet_enter;
+  NanoClock::duration m_step_overhead{0};
 
   NTuple::Item<size_t> m_trackNum;
   NTuple::Array<double> m_trackLengths;
   NTuple::Array<time_t> m_trackTimes;
+  NTuple::Array<time_t> m_trackTimeOverhead;
+  NTuple::Matrix<time_t> m_trackTimePerSubdet; // tracknum * subdet_size + subdet_idx
   NTuple::Array<int> m_trackStepNum;
   NTuple::Array<int> m_trackPDGIds;
   NTuple::Array<size_t> m_trackStartSubdet;
-- 
GitLab


From e17efeab0f11bae7beb262321bf24ad7e3a5dca6 Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Wed, 28 May 2025 16:54:40 +0800
Subject: [PATCH 35/74] fix: add charge

---
 Simulation/DetSimAna/src/ChronoAnaElemTool.cpp | 3 +++
 Simulation/DetSimAna/src/ChronoAnaElemTool.h   | 1 +
 2 files changed, 4 insertions(+)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
index 4abdb318..67976d0e 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -89,6 +89,8 @@ StatusCode ChronoAnaElemTool::initNtuple() {
           m_ntuple
               ->addIndexedItem("TrackEndSubdet", m_trackNum, m_trackEndSubdet)
               .isSuccess();
+      suc &= m_ntuple->addIndexedItem("TrackCharge", m_trackNum, m_trackCharge)
+                 .isSuccess();
       suc &=
           m_ntuple->addIndexedItem("TrackStepNum", m_trackNum, m_trackStepNum)
               .isSuccess();
@@ -149,6 +151,7 @@ void ChronoAnaElemTool::PostUserTrackingAction(const G4Track *track) {
   m_trackTimes[m_trackNum] =
       NanoClock::duration(track_stop - m_track_start).count();
   m_trackStepNum[m_trackNum] = track->GetCurrentStepNumber();
+  m_trackCharge[m_trackNum] = track->GetParticleDefinition()->GetPDGCharge();
   m_trackPDGIds[m_trackNum] = track->GetParticleDefinition()->GetPDGEncoding();
   m_trackEndSubdet[m_trackNum] = getSubdetNameIdx(track->GetTouchable());
   m_trackTimeOverhead[m_trackNum] =
diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.h b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
index 968a13f4..78061c09 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.h
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
@@ -71,6 +71,7 @@ private:
 
   NTuple::Item<size_t> m_trackNum;
   NTuple::Array<double> m_trackLengths;
+  NTuple::Array<double> m_trackCharge;
   NTuple::Array<time_t> m_trackTimes;
   NTuple::Array<time_t> m_trackTimeOverhead;
   NTuple::Matrix<time_t> m_trackTimePerSubdet; // tracknum * subdet_size + subdet_idx
-- 
GitLab


From 3e2706aa3592200a6a5508a3b64716b35c2bc0a5 Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Wed, 28 May 2025 17:44:53 +0800
Subject: [PATCH 36/74] fix: add track id momentum energy; reduce MAX_TRACK_NUM

---
 .../DetSimAna/src/ChronoAnaElemTool.cpp       | 29 ++++++++++++++++++-
 Simulation/DetSimAna/src/ChronoAnaElemTool.h  | 10 +++++--
 2 files changed, 36 insertions(+), 3 deletions(-)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
index 67976d0e..31c8fac2 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -8,6 +8,7 @@
 
 #include "ChronoAnaElemTool.h"
 
+#include "CLHEP/Units/SystemOfUnits.h"
 #include "G4Event.hh"
 #include "G4Step.hh"
 #include "G4Track.hh"
@@ -70,6 +71,12 @@ StatusCode ChronoAnaElemTool::initNtuple() {
       suc &= m_ntuple->addItem("TotSimTime", m_eventTotSimTime).isSuccess();
       suc &= m_ntuple->addItem("TrackNum", m_trackNum, 0U, MAX_TRACK_NUM)
                  .isSuccess();
+      suc &= m_ntuple->addItem("TrackPx", m_trackNum, m_px);
+      suc &= m_ntuple->addItem("TrackPy", m_trackNum, m_py);
+      suc &= m_ntuple->addItem("TrackPz", m_trackNum, m_pz);
+      suc &= m_ntuple->addItem("TrackX", m_trackNum, m_x);
+      suc &= m_ntuple->addItem("TrackY", m_trackNum, m_y);
+      suc &= m_ntuple->addItem("TrackZ", m_trackNum, m_z);
       suc &=
           m_ntuple->addIndexedItem("TrackLengths", m_trackNum, m_trackLengths)
               .isSuccess();
@@ -94,6 +101,15 @@ StatusCode ChronoAnaElemTool::initNtuple() {
       suc &=
           m_ntuple->addIndexedItem("TrackStepNum", m_trackNum, m_trackStepNum)
               .isSuccess();
+      suc &= m_ntuple->addIndexedItem("TrackID", m_trackNum, m_trackID)
+                 .isSuccess();
+      suc &=
+          m_ntuple->addIndexedItem("TrackParentID", m_trackNum, m_trackParentID)
+              .isSuccess();
+      suc &= m_ntuple
+                 ->addIndexedItem("TrackKineticEnergy", m_trackNum,
+                                  m_trackKineticEnergy)
+                 .isSuccess();
       suc &= m_ntuple
                  ->addIndexedItem("TrackTimePerSubdet", m_trackNum,
                                   MAX_SUBDET_NUM, m_trackTimePerSubdet)
@@ -139,6 +155,17 @@ void ChronoAnaElemTool::PreUserTrackingAction(const G4Track *track) {
   m_currentSubdetIndex = getSubdetNameIdx(track->GetTouchable());
   m_trackStartSubdet[m_trackNum] = m_currentSubdetIndex;
   m_step_overhead = m_step_overhead.zero();
+  m_trackID[m_trackNum] = track->GetTrackID();
+  m_trackParentID[m_trackNum] = track->GetParentID();
+  m_trackKineticEnergy[m_trackNum] = track->GetKineticEnergy() / CLHEP::GeV;
+  auto mom = track->GetMomentum();
+  auto pos = track->GetPosition();
+  m_px[m_trackNum] = mom.x() / CLHEP::GeV;
+  m_py[m_trackNum] = mom.y() / CLHEP::GeV;
+  m_pz[m_trackNum] = mom.z() / CLHEP::GeV;
+  m_x[m_trackNum] = pos.x() / CLHEP::mm;
+  m_y[m_trackNum] = pos.y() / CLHEP::mm;
+  m_z[m_trackNum] = pos.z() / CLHEP::mm;
   m_track_start = m_subdet_enter = NanoClock::now();
 }
 
@@ -147,7 +174,7 @@ void ChronoAnaElemTool::PostUserTrackingAction(const G4Track *track) {
     return;
   }
   NanoClock::time_point track_stop = NanoClock::now();
-  m_trackLengths[m_trackNum] = track->GetTrackLength();
+  m_trackLengths[m_trackNum] = track->GetTrackLength() / CLHEP::mm;
   m_trackTimes[m_trackNum] =
       NanoClock::duration(track_stop - m_track_start).count();
   m_trackStepNum[m_trackNum] = track->GetCurrentStepNumber();
diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.h b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
index 78061c09..241f2ea3 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.h
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
@@ -63,18 +63,24 @@ private:
   size_t m_currentSubdetIndex = UninitializedIndex;
 
   // simtime over Track
-  static constexpr size_t MAX_TRACK_NUM = 100'000'000; // 0.1B
+  static constexpr size_t MAX_TRACK_NUM = 10'000'000; // 10M
   static constexpr size_t MAX_SUBDET_NUM = 30;
   NanoClock::time_point m_track_start;
   NanoClock::time_point m_subdet_enter;
   NanoClock::duration m_step_overhead{0};
 
   NTuple::Item<size_t> m_trackNum;
+  NTuple::Array<int> m_trackID;
+  NTuple::Array<int> m_trackParentID;
+  NTuple::Array<double> m_trackKineticEnergy;
+  NTuple::Array<double> m_px, m_py, m_pz;
+  NTuple::Array<double> m_x, m_y, m_z;
   NTuple::Array<double> m_trackLengths;
   NTuple::Array<double> m_trackCharge;
   NTuple::Array<time_t> m_trackTimes;
   NTuple::Array<time_t> m_trackTimeOverhead;
-  NTuple::Matrix<time_t> m_trackTimePerSubdet; // tracknum * subdet_size + subdet_idx
+  NTuple::Matrix<time_t>
+      m_trackTimePerSubdet; // tracknum * subdet_size + subdet_idx
   NTuple::Array<int> m_trackStepNum;
   NTuple::Array<int> m_trackPDGIds;
   NTuple::Array<size_t> m_trackStartSubdet;
-- 
GitLab


From 2ef282a978251545ceb867bbd90311ba256631ec Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Thu, 29 May 2025 11:30:00 +0800
Subject: [PATCH 37/74] feat: add metric to see if frequently call causeing
 waste

---
 Simulation/DetSimAna/src/ChronoAnaElemTool.cpp | 18 ++++++++++++++++--
 Simulation/DetSimAna/src/ChronoAnaElemTool.h   |  3 +++
 2 files changed, 19 insertions(+), 2 deletions(-)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
index 31c8fac2..f614de10 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -114,6 +114,14 @@ StatusCode ChronoAnaElemTool::initNtuple() {
                  ->addIndexedItem("TrackTimePerSubdet", m_trackNum,
                                   MAX_SUBDET_NUM, m_trackTimePerSubdet)
                  .isSuccess();
+      suc &= m_ntuple
+                 ->addIndexedItem("TrackStepsPerSubdet", m_trackNum,
+                                  MAX_SUBDET_NUM, m_trackStepsPerSubdet)
+                 .isSuccess();
+      suc &= m_ntuple
+                 ->addIndexedItem("TrackStepsPerSubdet", m_trackNum,
+                                  MAX_SUBDET_NUM, m_trackTriggerCountPerSubdet)
+                 .isSuccess();
     } else {
       error() << "Cannot book N-tuple." << endmsg;
       return StatusCode::FAILURE;
@@ -166,6 +174,7 @@ void ChronoAnaElemTool::PreUserTrackingAction(const G4Track *track) {
   m_x[m_trackNum] = pos.x() / CLHEP::mm;
   m_y[m_trackNum] = pos.y() / CLHEP::mm;
   m_z[m_trackNum] = pos.z() / CLHEP::mm;
+  m_currentStepNum = track->GetCurrentStepNumber();
   m_track_start = m_subdet_enter = NanoClock::now();
 }
 
@@ -214,10 +223,15 @@ void ChronoAnaElemTool::UserSteppingAction(const G4Step *step) {
   }
   if (step->IsFirstStepInVolume()) {
     auto subdet_stop = NanoClock::now();
+    auto newStepNum = step->GetTrack()->GetCurrentStepNumber();
+    auto newSubDetNameIdx = getSubdetNameIdx(step->GetTrack()->GetTouchable());
     m_trackTimePerSubdet[m_trackNum][m_currentSubdetIndex] +=
         NanoClock::duration(subdet_stop - m_subdet_enter).count();
-    auto subDetNameIdx = getSubdetNameIdx(step->GetTrack()->GetTouchable());
-    m_currentSubdetIndex = subDetNameIdx;
+    m_trackStepsPerSubdet[m_trackNum][m_currentSubdetIndex] +=
+        (newStepNum - m_currentStepNum);
+    m_trackTriggerCountPerSubdet[m_trackNum][m_currentSubdetIndex] += 1;
+    m_currentSubdetIndex = newSubDetNameIdx;
+    m_currentStepNum = newStepNum;
     m_subdet_enter = NanoClock::now();
     m_step_overhead += (m_subdet_enter - subdet_stop);
   }
diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.h b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
index 241f2ea3..2ec9f870 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.h
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
@@ -68,6 +68,7 @@ private:
   NanoClock::time_point m_track_start;
   NanoClock::time_point m_subdet_enter;
   NanoClock::duration m_step_overhead{0};
+  size_t m_currentStepNum = 0;
 
   NTuple::Item<size_t> m_trackNum;
   NTuple::Array<int> m_trackID;
@@ -81,6 +82,8 @@ private:
   NTuple::Array<time_t> m_trackTimeOverhead;
   NTuple::Matrix<time_t>
       m_trackTimePerSubdet; // tracknum * subdet_size + subdet_idx
+  NTuple::Matrix<time_t> m_trackStepsPerSubdet;
+  NTuple::Matrix<time_t> m_trackTriggerCountPerSubdet;
   NTuple::Array<int> m_trackStepNum;
   NTuple::Array<int> m_trackPDGIds;
   NTuple::Array<size_t> m_trackStartSubdet;
-- 
GitLab


From f6060e64f6ed8a02b139acf460e13e4406650e25 Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Thu, 29 May 2025 11:31:06 +0800
Subject: [PATCH 38/74] fix: name dup

---
 Simulation/DetSimAna/src/ChronoAnaElemTool.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
index f614de10..cccf59c7 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -119,7 +119,7 @@ StatusCode ChronoAnaElemTool::initNtuple() {
                                   MAX_SUBDET_NUM, m_trackStepsPerSubdet)
                  .isSuccess();
       suc &= m_ntuple
-                 ->addIndexedItem("TrackStepsPerSubdet", m_trackNum,
+                 ->addIndexedItem("TrackTriggerCountPerSubdet", m_trackNum,
                                   MAX_SUBDET_NUM, m_trackTriggerCountPerSubdet)
                  .isSuccess();
     } else {
-- 
GitLab


From 3ee9b1fc39e7db6d2e7ee88e1562a50a23f1fdc2 Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Thu, 29 May 2025 14:06:16 +0800
Subject: [PATCH 39/74] fix: rm track and update subdet

---
 .../DetSimAna/src/ChronoAnaElemTool.cpp       | 177 +++---------------
 Simulation/DetSimAna/src/ChronoAnaElemTool.h  |  40 +---
 2 files changed, 35 insertions(+), 182 deletions(-)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
index cccf59c7..a4b5015d 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -8,13 +8,11 @@
 
 #include "ChronoAnaElemTool.h"
 
-#include "CLHEP/Units/SystemOfUnits.h"
 #include "G4Event.hh"
 #include "G4Step.hh"
 #include "G4Track.hh"
 #include "GaudiAlg/GaudiAlgorithm.h"
 #include "GaudiKernel/ClassID.h"
-#include "GaudiKernel/GaudiException.h"
 #include "GaudiKernel/MsgStream.h"
 #include "GaudiKernel/NTuple.h"
 #include "GaudiKernel/SmartIF.h"
@@ -26,8 +24,7 @@ DECLARE_COMPONENT(ChronoAnaElemTool)
 StatusCode ChronoAnaElemTool::initSubdetNames() {
   const auto &children = m_geomSvc->getDD4HepGeo().children();
   m_subdetNames.clear();
-  m_subdetNames.reserve(children.size() +
-                        SpecialCount); // add pWorld and Unknown subdet
+  m_subdetNames.reserve(MAX_SUBDET_NUM); // add pWorld and Unknown subdet
 
   // add pWorld
   m_subdetNames.emplace_back("Unknown");
@@ -45,11 +42,6 @@ StatusCode ChronoAnaElemTool::initSubdetNames() {
     error() << "[" << i << "]  " << m_subdetNames[i] << endmsg;
   }
 
-  if (m_subdetNames.size() > MAX_SUBDET_NUM) {
-    throw GaudiException("m_subdetNames size exceeded MAX_SUBDET_NUM",
-                         "ChronoAnaElemTool", StatusCode::FAILURE);
-  }
-
   return StatusCode::SUCCESS;
 }
 
@@ -69,58 +61,7 @@ StatusCode ChronoAnaElemTool::initNtuple() {
       suc &= m_ntuple->addItem("EventNumber", m_eventNumber, 0U, UINT_MAX)
                  .isSuccess();
       suc &= m_ntuple->addItem("TotSimTime", m_eventTotSimTime).isSuccess();
-      suc &= m_ntuple->addItem("TrackNum", m_trackNum, 0U, MAX_TRACK_NUM)
-                 .isSuccess();
-      suc &= m_ntuple->addItem("TrackPx", m_trackNum, m_px);
-      suc &= m_ntuple->addItem("TrackPy", m_trackNum, m_py);
-      suc &= m_ntuple->addItem("TrackPz", m_trackNum, m_pz);
-      suc &= m_ntuple->addItem("TrackX", m_trackNum, m_x);
-      suc &= m_ntuple->addItem("TrackY", m_trackNum, m_y);
-      suc &= m_ntuple->addItem("TrackZ", m_trackNum, m_z);
-      suc &=
-          m_ntuple->addIndexedItem("TrackLengths", m_trackNum, m_trackLengths)
-              .isSuccess();
-      suc &= m_ntuple->addIndexedItem("TrackTimes", m_trackNum, m_trackTimes)
-                 .isSuccess();
-      suc &= m_ntuple
-                 ->addIndexedItem("TrackTimeOverhead", m_trackNum,
-                                  m_trackTimeOverhead)
-                 .isSuccess();
-      suc &= m_ntuple->addIndexedItem("TrackPDGID", m_trackNum, m_trackPDGIds)
-                 .isSuccess();
-      suc &= m_ntuple
-                 ->addIndexedItem("TrackStartSubdet", m_trackNum,
-                                  m_trackStartSubdet)
-                 .isSuccess();
-      suc &=
-          m_ntuple
-              ->addIndexedItem("TrackEndSubdet", m_trackNum, m_trackEndSubdet)
-              .isSuccess();
-      suc &= m_ntuple->addIndexedItem("TrackCharge", m_trackNum, m_trackCharge)
-                 .isSuccess();
-      suc &=
-          m_ntuple->addIndexedItem("TrackStepNum", m_trackNum, m_trackStepNum)
-              .isSuccess();
-      suc &= m_ntuple->addIndexedItem("TrackID", m_trackNum, m_trackID)
-                 .isSuccess();
-      suc &=
-          m_ntuple->addIndexedItem("TrackParentID", m_trackNum, m_trackParentID)
-              .isSuccess();
-      suc &= m_ntuple
-                 ->addIndexedItem("TrackKineticEnergy", m_trackNum,
-                                  m_trackKineticEnergy)
-                 .isSuccess();
-      suc &= m_ntuple
-                 ->addIndexedItem("TrackTimePerSubdet", m_trackNum,
-                                  MAX_SUBDET_NUM, m_trackTimePerSubdet)
-                 .isSuccess();
-      suc &= m_ntuple
-                 ->addIndexedItem("TrackStepsPerSubdet", m_trackNum,
-                                  MAX_SUBDET_NUM, m_trackStepsPerSubdet)
-                 .isSuccess();
-      suc &= m_ntuple
-                 ->addIndexedItem("TrackTriggerCountPerSubdet", m_trackNum,
-                                  MAX_SUBDET_NUM, m_trackTriggerCountPerSubdet)
+      suc &= m_ntuple->addItem("TimePerSubdet", MAX_SUBDET_NUM, m_timePerSubdet)
                  .isSuccess();
     } else {
       error() << "Cannot book N-tuple." << endmsg;
@@ -150,51 +91,6 @@ StatusCode ChronoAnaElemTool::initialize() {
   return suc ? StatusCode::SUCCESS : StatusCode::FAILURE;
 }
 
-void ChronoAnaElemTool::PreUserTrackingAction(const G4Track *track) {
-  if (m_eventNumber < m_skipFirstNEvents.value()) {
-    return;
-  }
-  if (m_trackNum >= MAX_TRACK_NUM) {
-    error() << "Exceeded MAX_TRACK_NUM at event " << m_eventNumber << endmsg;
-    throw GaudiException("Exceeded MAX_TRACK_NUM at event " +
-                             std::to_string(m_eventNumber),
-                         "ChronoAnaElemTool", StatusCode::FAILURE);
-  }
-  m_currentSubdetIndex = getSubdetNameIdx(track->GetTouchable());
-  m_trackStartSubdet[m_trackNum] = m_currentSubdetIndex;
-  m_step_overhead = m_step_overhead.zero();
-  m_trackID[m_trackNum] = track->GetTrackID();
-  m_trackParentID[m_trackNum] = track->GetParentID();
-  m_trackKineticEnergy[m_trackNum] = track->GetKineticEnergy() / CLHEP::GeV;
-  auto mom = track->GetMomentum();
-  auto pos = track->GetPosition();
-  m_px[m_trackNum] = mom.x() / CLHEP::GeV;
-  m_py[m_trackNum] = mom.y() / CLHEP::GeV;
-  m_pz[m_trackNum] = mom.z() / CLHEP::GeV;
-  m_x[m_trackNum] = pos.x() / CLHEP::mm;
-  m_y[m_trackNum] = pos.y() / CLHEP::mm;
-  m_z[m_trackNum] = pos.z() / CLHEP::mm;
-  m_currentStepNum = track->GetCurrentStepNumber();
-  m_track_start = m_subdet_enter = NanoClock::now();
-}
-
-void ChronoAnaElemTool::PostUserTrackingAction(const G4Track *track) {
-  if (m_eventNumber < m_skipFirstNEvents.value()) {
-    return;
-  }
-  NanoClock::time_point track_stop = NanoClock::now();
-  m_trackLengths[m_trackNum] = track->GetTrackLength() / CLHEP::mm;
-  m_trackTimes[m_trackNum] =
-      NanoClock::duration(track_stop - m_track_start).count();
-  m_trackStepNum[m_trackNum] = track->GetCurrentStepNumber();
-  m_trackCharge[m_trackNum] = track->GetParticleDefinition()->GetPDGCharge();
-  m_trackPDGIds[m_trackNum] = track->GetParticleDefinition()->GetPDGEncoding();
-  m_trackEndSubdet[m_trackNum] = getSubdetNameIdx(track->GetTouchable());
-  m_trackTimeOverhead[m_trackNum] =
-      NanoClock::duration(m_step_overhead).count();
-  ++m_trackNum;
-}
-
 void ChronoAnaElemTool::EndOfEventAction(const G4Event *) {
   if (m_eventNumber < m_skipFirstNEvents.value()) {
     return;
@@ -214,7 +110,24 @@ void ChronoAnaElemTool::BeginOfEventAction(const G4Event *event) {
     return;
   }
 
-  m_event_start = NanoClock::now();
+  m_subdet_enter = m_event_start = NanoClock::now();
+}
+
+inline ChronoAnaElemTool::size_t
+ChronoAnaElemTool::getSubdetNameIdx(const G4Step *step) {
+  const auto &pos = step->GetPreStepPoint()->GetPosition();
+  const double r = pos.r();
+  const double z = std::abs(pos.z());
+
+  if ((r >= 600 && r <= 1800) && (z <= 2900)) {
+    return TPC;
+  } else if ((r >= 1830 && r <= 2130) && (z <= 2900)) {
+    return ECAL_BARREL;
+  } else if ((r >= 350 && r <= 2130) && (2930 <= z && z <= 2930 + 300)) {
+    return ECAL_ENDCAP;
+  } else {
+    return Other;
+  }
 }
 
 void ChronoAnaElemTool::UserSteppingAction(const G4Step *step) {
@@ -222,51 +135,15 @@ void ChronoAnaElemTool::UserSteppingAction(const G4Step *step) {
     return;
   }
   if (step->IsFirstStepInVolume()) {
-    auto subdet_stop = NanoClock::now();
-    auto newStepNum = step->GetTrack()->GetCurrentStepNumber();
-    auto newSubDetNameIdx = getSubdetNameIdx(step->GetTrack()->GetTouchable());
-    m_trackTimePerSubdet[m_trackNum][m_currentSubdetIndex] +=
-        NanoClock::duration(subdet_stop - m_subdet_enter).count();
-    m_trackStepsPerSubdet[m_trackNum][m_currentSubdetIndex] +=
-        (newStepNum - m_currentStepNum);
-    m_trackTriggerCountPerSubdet[m_trackNum][m_currentSubdetIndex] += 1;
-    m_currentSubdetIndex = newSubDetNameIdx;
-    m_currentStepNum = newStepNum;
-    m_subdet_enter = NanoClock::now();
-    m_step_overhead += (m_subdet_enter - subdet_stop);
-  }
-}
-
-inline ChronoAnaElemTool::size_t
-ChronoAnaElemTool::getSubdetNameIdx(const G4VTouchable *touch) {
-  auto historyDepthNum = touch->GetHistoryDepth();
-  for (int depth = 0; depth <= historyDepthNum; ++depth) {
-    auto vol = touch->GetVolume(depth);
-    if (!vol) {
-      error() << "Volume is nullptr at depth " << depth << endmsg;
-      continue;
+    auto newSubdet = getSubdetNameIdx(step);
+    if (newSubdet != m_currentSubdetIndex) {
+      auto stop = NanoClock::now();
+      m_timePerSubdet[m_currentSubdetIndex] +=
+          NanoClock::duration(stop - m_subdet_enter).count();
+      m_subdet_enter = stop;
+      m_currentSubdetIndex = newSubdet;
     }
-    std::string_view volName{vol->GetName()};
-
-    // start from the PWorldIndex subdetector name, as the first one is always
-    // Unknown
-    for (size_t i = PWorldIndex; i < m_subdetNames.size(); ++i) {
-      if (volName.find(m_subdetNames[i]) != std::string::npos) {
-        // Found a or more match, return the index
-        // info() << "Found subdetector name: " << m_subdetNames[i] << " at
-        // depth "
-        //        << depth << " with volName: " << volName << endmsg;
-        return i;
-      }
-    }
-  }
-
-  error() << "No matching subdetector name found" << endmsg;
-  for (int depth = 0; depth <= historyDepthNum; ++depth) {
-    auto vol = touch->GetVolume(depth);
-    error() << "Volume at depth " << depth << ": " << vol->GetName() << endmsg;
   }
-  return UnknownIndex;
 }
 
 StatusCode ChronoAnaElemTool::finalize() {
diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.h b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
index 2ec9f870..ce68ef83 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.h
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
@@ -28,10 +28,6 @@ public:
   virtual void BeginOfEventAction(const G4Event *) override;
   virtual void EndOfEventAction(const G4Event *) override;
 
-  // Tracking
-  virtual void PreUserTrackingAction(const G4Track *) override;
-  virtual void PostUserTrackingAction(const G4Track *) override;
-
   // Stepping
   virtual void UserSteppingAction(const G4Step *) override;
 
@@ -56,38 +52,18 @@ private:
   StatusCode initSubdetNames();
   static constexpr size_t UnknownIndex = 0;
   static constexpr size_t PWorldIndex = 1;
-  static constexpr size_t SpecialCount = 2;
-  static constexpr size_t UninitializedIndex = UINT_MAX;
+  static constexpr size_t MAX_SUBDET_NUM = 30;
   std::vector<std::string> m_subdetNames;
-  inline size_t getSubdetNameIdx(const G4VTouchable *);
-  size_t m_currentSubdetIndex = UninitializedIndex;
+  inline size_t getSubdetNameIdx(const G4Step *step);
+  NTuple::Array<time_t> m_timePerSubdet;
+  static constexpr size_t Other = 27;
+  static constexpr size_t TPC = 25;
+  static constexpr size_t ECAL_BARREL = 13;
+  static constexpr size_t ECAL_ENDCAP = 14;
+  size_t m_currentSubdetIndex = Other;
 
   // simtime over Track
-  static constexpr size_t MAX_TRACK_NUM = 10'000'000; // 10M
-  static constexpr size_t MAX_SUBDET_NUM = 30;
-  NanoClock::time_point m_track_start;
   NanoClock::time_point m_subdet_enter;
-  NanoClock::duration m_step_overhead{0};
-  size_t m_currentStepNum = 0;
-
-  NTuple::Item<size_t> m_trackNum;
-  NTuple::Array<int> m_trackID;
-  NTuple::Array<int> m_trackParentID;
-  NTuple::Array<double> m_trackKineticEnergy;
-  NTuple::Array<double> m_px, m_py, m_pz;
-  NTuple::Array<double> m_x, m_y, m_z;
-  NTuple::Array<double> m_trackLengths;
-  NTuple::Array<double> m_trackCharge;
-  NTuple::Array<time_t> m_trackTimes;
-  NTuple::Array<time_t> m_trackTimeOverhead;
-  NTuple::Matrix<time_t>
-      m_trackTimePerSubdet; // tracknum * subdet_size + subdet_idx
-  NTuple::Matrix<time_t> m_trackStepsPerSubdet;
-  NTuple::Matrix<time_t> m_trackTriggerCountPerSubdet;
-  NTuple::Array<int> m_trackStepNum;
-  NTuple::Array<int> m_trackPDGIds;
-  NTuple::Array<size_t> m_trackStartSubdet;
-  NTuple::Array<size_t> m_trackEndSubdet;
 };
 
 #endif
-- 
GitLab


From 6a5074259ce670c6a4908e60e5beda8891bad44f Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Thu, 29 May 2025 16:51:43 +0800
Subject: [PATCH 40/74] fix: use map to find subdet names

---
 .../DetSimAna/src/ChronoAnaElemTool.cpp       | 75 +++++++++++++++----
 Simulation/DetSimAna/src/ChronoAnaElemTool.h  | 15 ++--
 2 files changed, 65 insertions(+), 25 deletions(-)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
index a4b5015d..8709f3c3 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -11,6 +11,7 @@
 #include "G4Event.hh"
 #include "G4Step.hh"
 #include "G4Track.hh"
+#include "G4TransportationManager.hh"
 #include "GaudiAlg/GaudiAlgorithm.h"
 #include "GaudiKernel/ClassID.h"
 #include "GaudiKernel/MsgStream.h"
@@ -21,10 +22,46 @@
 
 DECLARE_COMPONENT(ChronoAnaElemTool)
 
+StatusCode ChronoAnaElemTool::initSubdetVolumes() {
+  // Code to get all the volume Names
+  auto world_vol = G4TransportationManager::GetTransportationManager()
+                       ->GetNavigatorForTracking()
+                       ->GetWorldVolume();
+  auto num_dau = world_vol->GetLogicalVolume()->GetNoDaughters();
+  debug() << "World Physical Volume - Name: " << world_vol->GetName()
+          << " Pointer: " << world_vol << " World Logical Volume - Name: "
+          << world_vol->GetLogicalVolume()->GetName()
+          << " Pointer: " << world_vol->GetLogicalVolume() << endmsg;
+  m_logicalVolumePtrToSubdetIdx[world_vol->GetLogicalVolume()] = PWorldIndex;
+  debug() << "Set m_logicalVolumePtrToSubdetIdx["
+          << world_vol->GetLogicalVolume() << "] to subdet index "
+          << PWorldIndex << endmsg;
+  for (size_t _dau = 0; _dau < num_dau; ++_dau) {
+    auto dau_vol = world_vol->GetLogicalVolume()->GetDaughter(_dau);
+    debug() << "Daughter Physical Volume - Name: " << dau_vol->GetName()
+            << " Pointer: " << dau_vol << " Daughter Logical Volume - Name: "
+            << dau_vol->GetLogicalVolume()->GetName()
+            << " Pointer: " << dau_vol->GetLogicalVolume() << endmsg;
+    auto dau_name = dau_vol->GetName();
+    for (size_t _subdet = PWorldIndex; _subdet < m_subdetNames.size();
+         ++_subdet) {
+      if (dau_name.find(m_subdetNames[_subdet]) != std::string::npos) {
+        m_logicalVolumePtrToSubdetIdx[dau_vol->GetLogicalVolume()] = _subdet;
+        debug() << "Set m_logicalVolumePtrToSubdetIdx["
+                << dau_vol->GetLogicalVolume() << "] to subdet index "
+                << _subdet << endmsg;
+        continue;
+      }
+    }
+  }
+  return StatusCode::SUCCESS;
+}
+
 StatusCode ChronoAnaElemTool::initSubdetNames() {
   const auto &children = m_geomSvc->getDD4HepGeo().children();
   m_subdetNames.clear();
-  m_subdetNames.reserve(MAX_SUBDET_NUM); // add pWorld and Unknown subdet
+  m_subdetNames.reserve(m_subdetNames.size() +
+                        2); // add pWorld and Unknown subdet
 
   // add pWorld
   m_subdetNames.emplace_back("Unknown");
@@ -61,8 +98,10 @@ StatusCode ChronoAnaElemTool::initNtuple() {
       suc &= m_ntuple->addItem("EventNumber", m_eventNumber, 0U, UINT_MAX)
                  .isSuccess();
       suc &= m_ntuple->addItem("TotSimTime", m_eventTotSimTime).isSuccess();
-      suc &= m_ntuple->addItem("TimePerSubdet", MAX_SUBDET_NUM, m_timePerSubdet)
-                 .isSuccess();
+      suc &=
+          m_ntuple
+              ->addItem("TimePerSubdet", m_subdetNames.size(), m_timePerSubdet)
+              .isSuccess();
     } else {
       error() << "Cannot book N-tuple." << endmsg;
       return StatusCode::FAILURE;
@@ -87,6 +126,7 @@ StatusCode ChronoAnaElemTool::initialize() {
 
   // init subdet names and number
   suc &= initSubdetNames().isSuccess();
+  suc &= initSubdetVolumes().isSuccess();
   suc &= initNtuple().isSuccess();
   return suc ? StatusCode::SUCCESS : StatusCode::FAILURE;
 }
@@ -114,19 +154,22 @@ void ChronoAnaElemTool::BeginOfEventAction(const G4Event *event) {
 }
 
 inline ChronoAnaElemTool::size_t
-ChronoAnaElemTool::getSubdetNameIdx(const G4Step *step) {
-  const auto &pos = step->GetPreStepPoint()->GetPosition();
-  const double r = pos.r();
-  const double z = std::abs(pos.z());
-
-  if ((r >= 600 && r <= 1800) && (z <= 2900)) {
-    return TPC;
-  } else if ((r >= 1830 && r <= 2130) && (z <= 2900)) {
-    return ECAL_BARREL;
-  } else if ((r >= 350 && r <= 2130) && (2930 <= z && z <= 2930 + 300)) {
-    return ECAL_ENDCAP;
+ChronoAnaElemTool::getSubdetNameIdx(const G4Track *track) {
+  auto historyDepthNum = track->GetTouchable()->GetHistoryDepth();
+  if (historyDepthNum == 0) {
+    return PWorldIndex;
   } else {
-    return Other;
+    auto lastVolume = track->GetTouchable()
+                          ->GetVolume(historyDepthNum - 1)
+                          ->GetLogicalVolume();
+    auto it = m_logicalVolumePtrToSubdetIdx.find(lastVolume);
+    if (it != m_logicalVolumePtrToSubdetIdx.end()) {
+      return it->second;
+    } else {
+      // error() << "Error find " << lastVolume->GetName()
+      //          << " ptr: " << lastVolume << endmsg;
+      return UnknownIndex;
+    }
   }
 }
 
@@ -135,7 +178,7 @@ void ChronoAnaElemTool::UserSteppingAction(const G4Step *step) {
     return;
   }
   if (step->IsFirstStepInVolume()) {
-    auto newSubdet = getSubdetNameIdx(step);
+    auto newSubdet = getSubdetNameIdx(step->GetTrack());
     if (newSubdet != m_currentSubdetIndex) {
       auto stop = NanoClock::now();
       m_timePerSubdet[m_currentSubdetIndex] +=
diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.h b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
index ce68ef83..4a39d88b 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.h
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
@@ -5,6 +5,7 @@
 #include <chrono>
 #include <climits>
 #include <string>
+#include <unordered_map>
 #include <vector>
 
 #include "DetInterface/IGeomSvc.h"
@@ -50,20 +51,16 @@ private:
 
   // simtime over subdet
   StatusCode initSubdetNames();
+  StatusCode initSubdetVolumes();
   static constexpr size_t UnknownIndex = 0;
   static constexpr size_t PWorldIndex = 1;
-  static constexpr size_t MAX_SUBDET_NUM = 30;
   std::vector<std::string> m_subdetNames;
-  inline size_t getSubdetNameIdx(const G4Step *step);
-  NTuple::Array<time_t> m_timePerSubdet;
-  static constexpr size_t Other = 27;
-  static constexpr size_t TPC = 25;
-  static constexpr size_t ECAL_BARREL = 13;
-  static constexpr size_t ECAL_ENDCAP = 14;
-  size_t m_currentSubdetIndex = Other;
+  std::unordered_map<G4LogicalVolume *, size_t> m_logicalVolumePtrToSubdetIdx;
 
-  // simtime over Track
+  inline size_t getSubdetNameIdx(const G4Track *track);
+  size_t m_currentSubdetIndex = UnknownIndex;
   NanoClock::time_point m_subdet_enter;
+  NTuple::Array<time_t> m_timePerSubdet;
 };
 
 #endif
-- 
GitLab


From 1c91dd39fa395877119935e2a37d89cefa09793e Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Thu, 29 May 2025 17:01:58 +0800
Subject: [PATCH 41/74] fix: restrict useage of unknown

---
 Simulation/DetSimAna/src/ChronoAnaElemTool.cpp | 4 ++--
 Simulation/DetSimAna/src/ChronoAnaElemTool.h   | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
index 8709f3c3..b1eaa5b3 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -166,8 +166,8 @@ ChronoAnaElemTool::getSubdetNameIdx(const G4Track *track) {
     if (it != m_logicalVolumePtrToSubdetIdx.end()) {
       return it->second;
     } else {
-      // error() << "Error find " << lastVolume->GetName()
-      //          << " ptr: " << lastVolume << endmsg;
+      error() << "Error find " << lastVolume->GetName()
+              << " ptr: " << lastVolume << endmsg;
       return UnknownIndex;
     }
   }
diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.h b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
index 4a39d88b..498f2c2f 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.h
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
@@ -58,7 +58,7 @@ private:
   std::unordered_map<G4LogicalVolume *, size_t> m_logicalVolumePtrToSubdetIdx;
 
   inline size_t getSubdetNameIdx(const G4Track *track);
-  size_t m_currentSubdetIndex = UnknownIndex;
+  size_t m_currentSubdetIndex = PWorldIndex;
   NanoClock::time_point m_subdet_enter;
   NTuple::Array<time_t> m_timePerSubdet;
 };
-- 
GitLab


From a356f7bcee2c955dee630260d8ad272f977b0de0 Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Thu, 29 May 2025 19:55:25 +0800
Subject: [PATCH 42/74] fix: add track info

---
 .../DetSimAna/src/ChronoAnaElemTool.cpp       | 103 +++++++++++++++++-
 Simulation/DetSimAna/src/ChronoAnaElemTool.h  |  32 ++++++
 2 files changed, 130 insertions(+), 5 deletions(-)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
index b1eaa5b3..f755e141 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -8,6 +8,7 @@
 
 #include "ChronoAnaElemTool.h"
 
+#include "CLHEP/Units/SystemOfUnits.h"
 #include "G4Event.hh"
 #include "G4Step.hh"
 #include "G4Track.hh"
@@ -102,6 +103,58 @@ StatusCode ChronoAnaElemTool::initNtuple() {
           m_ntuple
               ->addItem("TimePerSubdet", m_subdetNames.size(), m_timePerSubdet)
               .isSuccess();
+      suc &= m_ntuple->addItem("TrackNum", m_trackNum, 0U, MAX_TRACK_NUM)
+                 .isSuccess();
+      suc &= m_ntuple->addItem("TrackPx", m_trackNum, m_trackPx);
+      suc &= m_ntuple->addItem("TrackPy", m_trackNum, m_trackPy);
+      suc &= m_ntuple->addItem("TrackPz", m_trackNum, m_trackPz);
+      suc &= m_ntuple->addItem("TrackX", m_trackNum, m_trackX);
+      suc &= m_ntuple->addItem("TrackY", m_trackNum, m_trackY);
+      suc &= m_ntuple->addItem("TrackZ", m_trackNum, m_trackZ);
+      suc &= m_ntuple->addIndexedItem("TrackLengths", m_trackNum, m_trackLength)
+                 .isSuccess();
+      suc &= m_ntuple->addIndexedItem("TrackTimes", m_trackNum, m_trackTime)
+                 .isSuccess();
+      suc &= m_ntuple
+                 ->addIndexedItem("TrackTimeOverhead", m_trackNum,
+                                  m_trackTimeOverhead)
+                 .isSuccess();
+      suc &= m_ntuple->addIndexedItem("TrackPDGID", m_trackNum, m_trackPDGId)
+                 .isSuccess();
+      suc &= m_ntuple
+                 ->addIndexedItem("TrackStartSubdet", m_trackNum,
+                                  m_trackStartSubdet)
+                 .isSuccess();
+      suc &=
+          m_ntuple
+              ->addIndexedItem("TrackEndSubdet", m_trackNum, m_trackEndSubdet)
+              .isSuccess();
+      suc &= m_ntuple->addIndexedItem("TrackCharge", m_trackNum, m_trackCharge)
+                 .isSuccess();
+      suc &=
+          m_ntuple->addIndexedItem("TrackStepNum", m_trackNum, m_trackStepNum)
+              .isSuccess();
+      suc &= m_ntuple->addIndexedItem("TrackID", m_trackNum, m_trackID)
+                 .isSuccess();
+      suc &=
+          m_ntuple->addIndexedItem("TrackParentID", m_trackNum, m_trackParentID)
+              .isSuccess();
+      suc &= m_ntuple
+                 ->addIndexedItem("TrackKineticEnergy", m_trackNum,
+                                  m_trackKineticEnergy)
+                 .isSuccess();
+      suc &= m_ntuple
+                 ->addIndexedItem("TrackTimePerSubdet", m_trackNum,
+                                  MAX_SUBDET_NUM, m_trackTimePerSubdet)
+                 .isSuccess();
+      suc &= m_ntuple
+                 ->addIndexedItem("TrackStepsPerSubdet", m_trackNum,
+                                  MAX_SUBDET_NUM, m_trackStepsPerSubdet)
+                 .isSuccess();
+      suc &= m_ntuple
+                 ->addIndexedItem("TrackTriggerCountPerSubdet", m_trackNum,
+                                  MAX_SUBDET_NUM, m_trackTriggerCountPerSubdet)
+                 .isSuccess();
     } else {
       error() << "Cannot book N-tuple." << endmsg;
       return StatusCode::FAILURE;
@@ -150,7 +203,39 @@ void ChronoAnaElemTool::BeginOfEventAction(const G4Event *event) {
     return;
   }
 
-  m_subdet_enter = m_event_start = NanoClock::now();
+  m_event_start = NanoClock::now();
+}
+
+void ChronoAnaElemTool::PreUserTrackingAction(const G4Track *track) {
+
+  m_trackID[m_trackNum] = track->GetTrackID();
+  m_trackParentID[m_trackNum] = track->GetParentID();
+  m_trackKineticEnergy[m_trackNum] = track->GetKineticEnergy() / CLHEP::GeV;
+  auto mom = track->GetMomentum();
+  auto pos = track->GetPosition();
+  m_trackPx[m_trackNum] = mom.x() / CLHEP::GeV;
+  m_trackPy[m_trackNum] = mom.y() / CLHEP::GeV;
+  m_trackPz[m_trackNum] = mom.z() / CLHEP::GeV;
+  m_trackX[m_trackNum] = pos.x() / CLHEP::mm;
+  m_trackY[m_trackNum] = pos.y() / CLHEP::mm;
+  m_trackZ[m_trackNum] = pos.z() / CLHEP::mm;
+  m_trackCharge[m_trackNum] = track->GetParticleDefinition()->GetPDGCharge();
+  m_trackPDGId[m_trackNum] =
+      track->GetParticleDefinition()->GetParticleDefinitionID();
+  m_trackStartSubdet[m_trackNum] = getSubdetNameIdx(track);
+  m_current_step_num = track->GetCurrentStepNumber();
+  m_track_overhead = m_track_overhead.zero();
+  m_subdet_enter = m_track_start = NanoClock::now();
+}
+
+void ChronoAnaElemTool::PostUserTrackingAction(const G4Track *track) {
+  auto stop = NanoClock::now();
+  m_trackTime[m_trackNum] = NanoClock::duration(stop - m_track_start).count();
+  m_trackLength[m_trackNum] = track->GetTrackLength();
+  m_trackStepNum[m_trackNum] = track->GetCurrentStepNumber();
+  m_trackEndSubdet[m_trackNum] = getSubdetNameIdx(track);
+  m_trackTimeOverhead[m_trackNum] = m_track_overhead.count();
+  ++m_trackNum;
 }
 
 inline ChronoAnaElemTool::size_t
@@ -178,13 +263,21 @@ void ChronoAnaElemTool::UserSteppingAction(const G4Step *step) {
     return;
   }
   if (step->IsFirstStepInVolume()) {
-    auto newSubdet = getSubdetNameIdx(step->GetTrack());
+    auto track = step->GetTrack();
+    auto newSubdet = getSubdetNameIdx(track);
     if (newSubdet != m_currentSubdetIndex) {
       auto stop = NanoClock::now();
-      m_timePerSubdet[m_currentSubdetIndex] +=
-          NanoClock::duration(stop - m_subdet_enter).count();
-      m_subdet_enter = stop;
+      auto duration = NanoClock::duration(stop - m_subdet_enter).count();
+      auto newStepNum = track->GetCurrentStepNumber();
+      m_timePerSubdet[m_currentSubdetIndex] += duration;
+      m_trackTimePerSubdet[m_trackNum][m_currentSubdetIndex] += duration;
+      m_trackStepsPerSubdet[m_trackNum][m_currentSubdetIndex] +=
+          newStepNum - m_current_step_num;
+      m_trackTriggerCountPerSubdet[m_trackNum][m_currentSubdetIndex] += 1;
       m_currentSubdetIndex = newSubdet;
+      m_current_step_num = newStepNum;
+      m_subdet_enter = NanoClock::now();
+      m_track_overhead += (m_subdet_enter - stop);
     }
   }
 }
diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.h b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
index 498f2c2f..c2edc016 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.h
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
@@ -29,6 +29,10 @@ public:
   virtual void BeginOfEventAction(const G4Event *) override;
   virtual void EndOfEventAction(const G4Event *) override;
 
+  // Tracking
+  virtual void PreUserTrackingAction(const G4Track *) override;
+  virtual void PostUserTrackingAction(const G4Track *) override;
+
   // Stepping
   virtual void UserSteppingAction(const G4Step *) override;
 
@@ -61,6 +65,34 @@ private:
   size_t m_currentSubdetIndex = PWorldIndex;
   NanoClock::time_point m_subdet_enter;
   NTuple::Array<time_t> m_timePerSubdet;
+
+  // simtime over track
+  static constexpr size_t MAX_TRACK_NUM = 10'000'000; // 10M
+  static constexpr size_t MAX_SUBDET_NUM = 30;
+  NanoClock::time_point m_track_start;
+  time_t m_current_step_num;
+  NanoClock::duration m_track_overhead{0};
+
+  NTuple::Item<size_t> m_trackNum;
+  NTuple::Array<int> m_trackID;
+  NTuple::Array<int> m_trackParentID;
+  NTuple::Array<double> m_trackKineticEnergy;
+  NTuple::Array<double> m_trackPx, m_trackPy, m_trackPz;
+  NTuple::Array<double> m_trackX, m_trackY, m_trackZ;
+  NTuple::Array<double> m_trackLength;
+  NTuple::Array<double> m_trackCharge;
+  NTuple::Array<int> m_trackPDGId;
+  NTuple::Array<int> m_trackStepNum;
+  NTuple::Array<time_t> m_trackTime;
+  NTuple::Array<time_t> m_trackTimeOverhead;
+  NTuple::Array<size_t> m_trackStartSubdet;
+  NTuple::Array<size_t> m_trackEndSubdet;
+
+  NTuple::Matrix<time_t>
+      m_trackTimePerSubdet; // tracknum * subdet_size + subdet_idx
+  NTuple::Matrix<time_t> m_trackStepsPerSubdet;
+  NTuple::Matrix<time_t> m_trackTriggerCountPerSubdet;
+
 };
 
 #endif
-- 
GitLab


From 24e57c4af46d8b1cf5f672748adecc9f004e2408 Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Thu, 29 May 2025 20:07:25 +0800
Subject: [PATCH 43/74] fix: track time remove overhead

---
 Simulation/DetSimAna/src/ChronoAnaElemTool.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
index f755e141..73a09c8d 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -230,7 +230,8 @@ void ChronoAnaElemTool::PreUserTrackingAction(const G4Track *track) {
 
 void ChronoAnaElemTool::PostUserTrackingAction(const G4Track *track) {
   auto stop = NanoClock::now();
-  m_trackTime[m_trackNum] = NanoClock::duration(stop - m_track_start).count();
+  m_trackTime[m_trackNum] =
+      NanoClock::duration(stop - m_track_start - m_track_overhead).count();
   m_trackLength[m_trackNum] = track->GetTrackLength();
   m_trackStepNum[m_trackNum] = track->GetCurrentStepNumber();
   m_trackEndSubdet[m_trackNum] = getSubdetNameIdx(track);
-- 
GitLab


From 0137e66fb9aecf930939c8822c95b4a1c072ddcb Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Thu, 29 May 2025 20:49:17 +0800
Subject: [PATCH 44/74] fix: track may stay in same subdet

---
 .../DetSimAna/src/ChronoAnaElemTool.cpp       | 40 +++++++++----------
 Simulation/DetSimAna/src/ChronoAnaElemTool.h  |  4 +-
 2 files changed, 19 insertions(+), 25 deletions(-)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
index 73a09c8d..0358cee0 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -147,14 +147,6 @@ StatusCode ChronoAnaElemTool::initNtuple() {
                  ->addIndexedItem("TrackTimePerSubdet", m_trackNum,
                                   MAX_SUBDET_NUM, m_trackTimePerSubdet)
                  .isSuccess();
-      suc &= m_ntuple
-                 ->addIndexedItem("TrackStepsPerSubdet", m_trackNum,
-                                  MAX_SUBDET_NUM, m_trackStepsPerSubdet)
-                 .isSuccess();
-      suc &= m_ntuple
-                 ->addIndexedItem("TrackTriggerCountPerSubdet", m_trackNum,
-                                  MAX_SUBDET_NUM, m_trackTriggerCountPerSubdet)
-                 .isSuccess();
     } else {
       error() << "Cannot book N-tuple." << endmsg;
       return StatusCode::FAILURE;
@@ -203,7 +195,7 @@ void ChronoAnaElemTool::BeginOfEventAction(const G4Event *event) {
     return;
   }
 
-  m_event_start = NanoClock::now();
+  m_subdet_enter = m_event_start = NanoClock::now();
 }
 
 void ChronoAnaElemTool::PreUserTrackingAction(const G4Track *track) {
@@ -223,19 +215,26 @@ void ChronoAnaElemTool::PreUserTrackingAction(const G4Track *track) {
   m_trackPDGId[m_trackNum] =
       track->GetParticleDefinition()->GetParticleDefinitionID();
   m_trackStartSubdet[m_trackNum] = getSubdetNameIdx(track);
-  m_current_step_num = track->GetCurrentStepNumber();
   m_track_overhead = m_track_overhead.zero();
-  m_subdet_enter = m_track_start = NanoClock::now();
+  m_subdet_track_enter = m_track_start = NanoClock::now();
 }
 
 void ChronoAnaElemTool::PostUserTrackingAction(const G4Track *track) {
   auto stop = NanoClock::now();
   m_trackTime[m_trackNum] =
       NanoClock::duration(stop - m_track_start - m_track_overhead).count();
+  auto track_end_subdet = getSubdetNameIdx(track);
   m_trackLength[m_trackNum] = track->GetTrackLength();
   m_trackStepNum[m_trackNum] = track->GetCurrentStepNumber();
-  m_trackEndSubdet[m_trackNum] = getSubdetNameIdx(track);
+  m_trackEndSubdet[m_trackNum] = track_end_subdet;
   m_trackTimeOverhead[m_trackNum] = m_track_overhead.count();
+  // track did not change subdet, add time
+  m_trackTimePerSubdet[m_trackNum][m_currentSubdetIndex] +=
+      NanoClock::duration(stop - m_subdet_track_enter).count();
+  if (m_currentSubdetIndex != track_end_subdet) {
+    error() << "Current Subdet " << m_currentSubdetIndex
+            << " not equal with track end subdet" << track_end_subdet << endmsg;
+  }
   ++m_trackNum;
 }
 
@@ -268,17 +267,14 @@ void ChronoAnaElemTool::UserSteppingAction(const G4Step *step) {
     auto newSubdet = getSubdetNameIdx(track);
     if (newSubdet != m_currentSubdetIndex) {
       auto stop = NanoClock::now();
-      auto duration = NanoClock::duration(stop - m_subdet_enter).count();
-      auto newStepNum = track->GetCurrentStepNumber();
-      m_timePerSubdet[m_currentSubdetIndex] += duration;
-      m_trackTimePerSubdet[m_trackNum][m_currentSubdetIndex] += duration;
-      m_trackStepsPerSubdet[m_trackNum][m_currentSubdetIndex] +=
-          newStepNum - m_current_step_num;
-      m_trackTriggerCountPerSubdet[m_trackNum][m_currentSubdetIndex] += 1;
+      auto event_duration = NanoClock::duration(stop - m_subdet_enter).count();
+      auto track_duration =
+          NanoClock::duration(stop - m_subdet_track_enter).count();
+      m_timePerSubdet[m_currentSubdetIndex] += event_duration;
+      m_trackTimePerSubdet[m_trackNum][m_currentSubdetIndex] += track_duration;
       m_currentSubdetIndex = newSubdet;
-      m_current_step_num = newStepNum;
-      m_subdet_enter = NanoClock::now();
-      m_track_overhead += (m_subdet_enter - stop);
+      m_subdet_track_enter = m_subdet_enter = NanoClock::now();
+      m_track_overhead += (m_subdet_track_enter - stop);
     }
   }
 }
diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.h b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
index c2edc016..fd1cf30e 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.h
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
@@ -70,8 +70,8 @@ private:
   static constexpr size_t MAX_TRACK_NUM = 10'000'000; // 10M
   static constexpr size_t MAX_SUBDET_NUM = 30;
   NanoClock::time_point m_track_start;
-  time_t m_current_step_num;
   NanoClock::duration m_track_overhead{0};
+  NanoClock::time_point m_subdet_track_enter;
 
   NTuple::Item<size_t> m_trackNum;
   NTuple::Array<int> m_trackID;
@@ -90,8 +90,6 @@ private:
 
   NTuple::Matrix<time_t>
       m_trackTimePerSubdet; // tracknum * subdet_size + subdet_idx
-  NTuple::Matrix<time_t> m_trackStepsPerSubdet;
-  NTuple::Matrix<time_t> m_trackTriggerCountPerSubdet;
 
 };
 
-- 
GitLab


From 020fbd8518fc63a2b70aa37daf80dfa9daa789b6 Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Thu, 29 May 2025 21:03:54 +0800
Subject: [PATCH 45/74] fix: skip first n events

---
 Simulation/DetSimAna/src/ChronoAnaElemTool.cpp | 16 +++++++++++++---
 1 file changed, 13 insertions(+), 3 deletions(-)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
index 0358cee0..c66c3f9d 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -190,7 +190,7 @@ void ChronoAnaElemTool::EndOfEventAction(const G4Event *) {
 void ChronoAnaElemTool::BeginOfEventAction(const G4Event *event) {
   m_ntuple->reset();
   m_eventNumber = static_cast<size_t>(event->GetEventID());
-  always() << "ChronoAnaElemTool: Event number: " << m_eventNumber << endmsg;
+  always() << " ChronoAnaElemTool: Event number: " << m_eventNumber << endmsg;
   if (m_eventNumber < m_skipFirstNEvents.value()) {
     return;
   }
@@ -199,7 +199,9 @@ void ChronoAnaElemTool::BeginOfEventAction(const G4Event *event) {
 }
 
 void ChronoAnaElemTool::PreUserTrackingAction(const G4Track *track) {
-
+  if (m_eventNumber < m_skipFirstNEvents.value()) {
+    return;
+  }
   m_trackID[m_trackNum] = track->GetTrackID();
   m_trackParentID[m_trackNum] = track->GetParentID();
   m_trackKineticEnergy[m_trackNum] = track->GetKineticEnergy() / CLHEP::GeV;
@@ -216,10 +218,14 @@ void ChronoAnaElemTool::PreUserTrackingAction(const G4Track *track) {
       track->GetParticleDefinition()->GetParticleDefinitionID();
   m_trackStartSubdet[m_trackNum] = getSubdetNameIdx(track);
   m_track_overhead = m_track_overhead.zero();
+  // always() << "track start in " << m_trackStartSubdet[m_trackNum] << endmsg;
   m_subdet_track_enter = m_track_start = NanoClock::now();
 }
 
 void ChronoAnaElemTool::PostUserTrackingAction(const G4Track *track) {
+  if (m_eventNumber < m_skipFirstNEvents.value()) {
+    return;
+  }
   auto stop = NanoClock::now();
   m_trackTime[m_trackNum] =
       NanoClock::duration(stop - m_track_start - m_track_overhead).count();
@@ -233,8 +239,10 @@ void ChronoAnaElemTool::PostUserTrackingAction(const G4Track *track) {
       NanoClock::duration(stop - m_subdet_track_enter).count();
   if (m_currentSubdetIndex != track_end_subdet) {
     error() << "Current Subdet " << m_currentSubdetIndex
-            << " not equal with track end subdet" << track_end_subdet << endmsg;
+            << " not equal with track end subdet " << track_end_subdet
+            << endmsg;
   }
+  // always() << "track end in " << track_end_subdet << endmsg;
   ++m_trackNum;
 }
 
@@ -272,6 +280,8 @@ void ChronoAnaElemTool::UserSteppingAction(const G4Step *step) {
           NanoClock::duration(stop - m_subdet_track_enter).count();
       m_timePerSubdet[m_currentSubdetIndex] += event_duration;
       m_trackTimePerSubdet[m_trackNum][m_currentSubdetIndex] += track_duration;
+      // always() << "track " << track->GetTrackID() << " entering " << newSubdet
+      //          << " from " << m_currentSubdetIndex << endmsg;
       m_currentSubdetIndex = newSubdet;
       m_subdet_track_enter = m_subdet_enter = NanoClock::now();
       m_track_overhead += (m_subdet_track_enter - stop);
-- 
GitLab


From 21feef49ee1512ba8b1166c6b49822564c3d28cd Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Fri, 30 May 2025 08:43:28 +0800
Subject: [PATCH 46/74] fix: add event end subdet

---
 Simulation/DetSimAna/src/ChronoAnaElemTool.cpp | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
index c66c3f9d..7d7a2fa6 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -181,6 +181,8 @@ void ChronoAnaElemTool::EndOfEventAction(const G4Event *) {
     return;
   }
   NanoClock::time_point event_stop = NanoClock::now();
+  m_timePerSubdet[m_currentSubdetIndex] +=
+      NanoClock::duration(event_stop - m_subdet_enter).count();
   m_eventTotSimTime = NanoClock::duration(event_stop - m_event_start).count();
   if (m_ntuple->write().isFailure()) {
     error() << "Error write track tuple at event " << m_eventNumber << endmsg;
@@ -280,7 +282,8 @@ void ChronoAnaElemTool::UserSteppingAction(const G4Step *step) {
           NanoClock::duration(stop - m_subdet_track_enter).count();
       m_timePerSubdet[m_currentSubdetIndex] += event_duration;
       m_trackTimePerSubdet[m_trackNum][m_currentSubdetIndex] += track_duration;
-      // always() << "track " << track->GetTrackID() << " entering " << newSubdet
+      // always() << "track " << track->GetTrackID() << " entering " <<
+      // newSubdet
       //          << " from " << m_currentSubdetIndex << endmsg;
       m_currentSubdetIndex = newSubdet;
       m_subdet_track_enter = m_subdet_enter = NanoClock::now();
-- 
GitLab


From 5b3c684bd72533d4344ca265fb7505ffce6586ca Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Fri, 30 May 2025 09:37:32 +0800
Subject: [PATCH 47/74] fix: change include sequence

---
 Simulation/DetSimAna/src/ChronoAnaElemTool.cpp | 3 +--
 Simulation/DetSimAna/src/ChronoAnaElemTool.h   | 3 +--
 2 files changed, 2 insertions(+), 4 deletions(-)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
index 7d7a2fa6..c1f5e7e4 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -6,8 +6,6 @@
 #include <cstdlib>
 #include <string>
 
-#include "ChronoAnaElemTool.h"
-
 #include "CLHEP/Units/SystemOfUnits.h"
 #include "G4Event.hh"
 #include "G4Step.hh"
@@ -19,6 +17,7 @@
 #include "GaudiKernel/NTuple.h"
 #include "GaudiKernel/SmartIF.h"
 
+#include "ChronoAnaElemTool.h"
 #include "DetInterface/IGeomSvc.h"
 
 DECLARE_COMPONENT(ChronoAnaElemTool)
diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.h b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
index fd1cf30e..1efecba0 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.h
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
@@ -1,7 +1,6 @@
 #ifndef ChronoAnaElemTool_h
 #define ChronoAnaElemTool_h
 
-#include <G4VTouchable.hh>
 #include <chrono>
 #include <climits>
 #include <string>
@@ -11,6 +10,7 @@
 #include "DetInterface/IGeomSvc.h"
 #include "DetSimInterface/IAnaElemTool.h"
 
+#include "G4LogicalVolume.hh"
 #include "GaudiKernel/AlgTool.h"
 #include "GaudiKernel/INTupleSvc.h"
 #include "GaudiKernel/NTuple.h"
@@ -90,7 +90,6 @@ private:
 
   NTuple::Matrix<time_t>
       m_trackTimePerSubdet; // tracknum * subdet_size + subdet_idx
-
 };
 
 #endif
-- 
GitLab


From 90bc2f30dfc7b2a88a70985cdb84a819c1dd5d64 Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Sat, 31 May 2025 20:48:28 +0800
Subject: [PATCH 48/74] fix: remove overhead

---
 Simulation/DetSimAna/src/ChronoAnaElemTool.cpp | 10 +---------
 Simulation/DetSimAna/src/ChronoAnaElemTool.h   |  4 +---
 2 files changed, 2 insertions(+), 12 deletions(-)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
index c1f5e7e4..3299c457 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -114,10 +114,6 @@ StatusCode ChronoAnaElemTool::initNtuple() {
                  .isSuccess();
       suc &= m_ntuple->addIndexedItem("TrackTimes", m_trackNum, m_trackTime)
                  .isSuccess();
-      suc &= m_ntuple
-                 ->addIndexedItem("TrackTimeOverhead", m_trackNum,
-                                  m_trackTimeOverhead)
-                 .isSuccess();
       suc &= m_ntuple->addIndexedItem("TrackPDGID", m_trackNum, m_trackPDGId)
                  .isSuccess();
       suc &= m_ntuple
@@ -218,7 +214,6 @@ void ChronoAnaElemTool::PreUserTrackingAction(const G4Track *track) {
   m_trackPDGId[m_trackNum] =
       track->GetParticleDefinition()->GetParticleDefinitionID();
   m_trackStartSubdet[m_trackNum] = getSubdetNameIdx(track);
-  m_track_overhead = m_track_overhead.zero();
   // always() << "track start in " << m_trackStartSubdet[m_trackNum] << endmsg;
   m_subdet_track_enter = m_track_start = NanoClock::now();
 }
@@ -228,13 +223,11 @@ void ChronoAnaElemTool::PostUserTrackingAction(const G4Track *track) {
     return;
   }
   auto stop = NanoClock::now();
-  m_trackTime[m_trackNum] =
-      NanoClock::duration(stop - m_track_start - m_track_overhead).count();
+  m_trackTime[m_trackNum] = NanoClock::duration(stop - m_track_start).count();
   auto track_end_subdet = getSubdetNameIdx(track);
   m_trackLength[m_trackNum] = track->GetTrackLength();
   m_trackStepNum[m_trackNum] = track->GetCurrentStepNumber();
   m_trackEndSubdet[m_trackNum] = track_end_subdet;
-  m_trackTimeOverhead[m_trackNum] = m_track_overhead.count();
   // track did not change subdet, add time
   m_trackTimePerSubdet[m_trackNum][m_currentSubdetIndex] +=
       NanoClock::duration(stop - m_subdet_track_enter).count();
@@ -286,7 +279,6 @@ void ChronoAnaElemTool::UserSteppingAction(const G4Step *step) {
       //          << " from " << m_currentSubdetIndex << endmsg;
       m_currentSubdetIndex = newSubdet;
       m_subdet_track_enter = m_subdet_enter = NanoClock::now();
-      m_track_overhead += (m_subdet_track_enter - stop);
     }
   }
 }
diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.h b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
index 1efecba0..1be07656 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.h
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
@@ -11,11 +11,11 @@
 #include "DetSimInterface/IAnaElemTool.h"
 
 #include "G4LogicalVolume.hh"
+#include "Gaudi/Timers/RdtscClock.h"
 #include "GaudiKernel/AlgTool.h"
 #include "GaudiKernel/INTupleSvc.h"
 #include "GaudiKernel/NTuple.h"
 #include "GaudiKernel/SmartIF.h"
-#include <Gaudi/Timers/RdtscClock.h>
 
 class ChronoAnaElemTool : public extends<AlgTool, IAnaElemTool> {
 
@@ -70,7 +70,6 @@ private:
   static constexpr size_t MAX_TRACK_NUM = 10'000'000; // 10M
   static constexpr size_t MAX_SUBDET_NUM = 30;
   NanoClock::time_point m_track_start;
-  NanoClock::duration m_track_overhead{0};
   NanoClock::time_point m_subdet_track_enter;
 
   NTuple::Item<size_t> m_trackNum;
@@ -84,7 +83,6 @@ private:
   NTuple::Array<int> m_trackPDGId;
   NTuple::Array<int> m_trackStepNum;
   NTuple::Array<time_t> m_trackTime;
-  NTuple::Array<time_t> m_trackTimeOverhead;
   NTuple::Array<size_t> m_trackStartSubdet;
   NTuple::Array<size_t> m_trackEndSubdet;
 
-- 
GitLab


From 87f990c079b637026865ecb5cd7e46abf95b6feb Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Mon, 2 Jun 2025 09:54:52 +0800
Subject: [PATCH 49/74] fix: reset start subdet to unknown

---
 Simulation/DetSimAna/src/ChronoAnaElemTool.cpp | 14 +++++++++-----
 Simulation/DetSimAna/src/ChronoAnaElemTool.h   |  2 +-
 2 files changed, 10 insertions(+), 6 deletions(-)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
index 3299c457..4b4e01be 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -192,6 +192,7 @@ void ChronoAnaElemTool::BeginOfEventAction(const G4Event *event) {
     return;
   }
 
+  m_currentSubdetIndex = UnknownIndex;
   m_subdet_enter = m_event_start = NanoClock::now();
 }
 
@@ -214,7 +215,8 @@ void ChronoAnaElemTool::PreUserTrackingAction(const G4Track *track) {
   m_trackPDGId[m_trackNum] =
       track->GetParticleDefinition()->GetParticleDefinitionID();
   m_trackStartSubdet[m_trackNum] = getSubdetNameIdx(track);
-  // always() << "track start in " << m_trackStartSubdet[m_trackNum] << endmsg;
+  // always() << "track " << m_trackID[m_trackNum] << " start in "
+  //          << m_trackStartSubdet[m_trackNum] << endmsg;
   m_subdet_track_enter = m_track_start = NanoClock::now();
 }
 
@@ -236,7 +238,8 @@ void ChronoAnaElemTool::PostUserTrackingAction(const G4Track *track) {
             << " not equal with track end subdet " << track_end_subdet
             << endmsg;
   }
-  // always() << "track end in " << track_end_subdet << endmsg;
+  // always() << "track " << m_trackID[m_trackNum] << " end in "
+  //          << track_end_subdet << endmsg;
   ++m_trackNum;
 }
 
@@ -274,9 +277,10 @@ void ChronoAnaElemTool::UserSteppingAction(const G4Step *step) {
           NanoClock::duration(stop - m_subdet_track_enter).count();
       m_timePerSubdet[m_currentSubdetIndex] += event_duration;
       m_trackTimePerSubdet[m_trackNum][m_currentSubdetIndex] += track_duration;
-      // always() << "track " << track->GetTrackID() << " entering " <<
-      // newSubdet
-      //          << " from " << m_currentSubdetIndex << endmsg;
+      // always() << "track " << track->GetTrackID() << " from "
+      //          << m_currentSubdetIndex << " entering " << newSubdet
+      //          << " event time " << event_duration << " track time "
+      //          << track_duration << endmsg;
       m_currentSubdetIndex = newSubdet;
       m_subdet_track_enter = m_subdet_enter = NanoClock::now();
     }
diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.h b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
index 1be07656..4893f5ca 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.h
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
@@ -62,7 +62,7 @@ private:
   std::unordered_map<G4LogicalVolume *, size_t> m_logicalVolumePtrToSubdetIdx;
 
   inline size_t getSubdetNameIdx(const G4Track *track);
-  size_t m_currentSubdetIndex = PWorldIndex;
+  size_t m_currentSubdetIndex = UnknownIndex;
   NanoClock::time_point m_subdet_enter;
   NTuple::Array<time_t> m_timePerSubdet;
 
-- 
GitLab


From 4144c49cdcee58c30ce50f7461f7cc8ba70fde49 Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Tue, 3 Jun 2025 09:17:34 +0800
Subject: [PATCH 50/74] fix: process current subdet change in pre track

---
 Simulation/DetSimAna/src/ChronoAnaElemTool.cpp | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
index 4b4e01be..96441910 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -214,7 +214,14 @@ void ChronoAnaElemTool::PreUserTrackingAction(const G4Track *track) {
   m_trackCharge[m_trackNum] = track->GetParticleDefinition()->GetPDGCharge();
   m_trackPDGId[m_trackNum] =
       track->GetParticleDefinition()->GetParticleDefinitionID();
-  m_trackStartSubdet[m_trackNum] = getSubdetNameIdx(track);
+  auto newSubdet = m_trackStartSubdet[m_trackNum] = getSubdetNameIdx(track);
+  if (newSubdet != m_currentSubdetIndex) {
+    auto stop = NanoClock::now();
+    auto event_duration = NanoClock::duration(stop - m_subdet_enter).count();
+    m_timePerSubdet[m_currentSubdetIndex] += event_duration;
+    m_currentSubdetIndex = newSubdet;
+    m_subdet_enter = NanoClock::now();
+  }
   // always() << "track " << m_trackID[m_trackNum] << " start in "
   //          << m_trackStartSubdet[m_trackNum] << endmsg;
   m_subdet_track_enter = m_track_start = NanoClock::now();
-- 
GitLab


From 3730e3a9428211d3ecb8f14d43fc6687a9af922e Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Tue, 3 Jun 2025 16:13:38 +0800
Subject: [PATCH 51/74] fix: change Simulation Granularity

---
 .../DetSimAna/src/ChronoAnaElemTool.cpp       | 45 +++++++++++++++----
 Simulation/DetSimAna/src/ChronoAnaElemTool.h  |  5 +++
 2 files changed, 42 insertions(+), 8 deletions(-)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
index 96441910..38b8b4ff 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -6,9 +6,11 @@
 #include <cstdlib>
 #include <string>
 
-#include "CLHEP/Units/SystemOfUnits.h"
+#include "G4ChordFinder.hh"
 #include "G4Event.hh"
+#include "G4FieldManager.hh"
 #include "G4Step.hh"
+#include "G4SystemOfUnits.hh"
 #include "G4Track.hh"
 #include "G4TransportationManager.hh"
 #include "GaudiAlg/GaudiAlgorithm.h"
@@ -161,6 +163,9 @@ StatusCode ChronoAnaElemTool::initialize() {
     return StatusCode::FAILURE;
   }
 
+  m_fieldManager =
+      G4TransportationManager::GetTransportationManager()->GetFieldManager();
+
   // calibrate the clock before starting the simulation
   NanoClock::calibrate();
 
@@ -202,15 +207,15 @@ void ChronoAnaElemTool::PreUserTrackingAction(const G4Track *track) {
   }
   m_trackID[m_trackNum] = track->GetTrackID();
   m_trackParentID[m_trackNum] = track->GetParentID();
-  m_trackKineticEnergy[m_trackNum] = track->GetKineticEnergy() / CLHEP::GeV;
+  m_trackKineticEnergy[m_trackNum] = track->GetKineticEnergy() / GeV;
   auto mom = track->GetMomentum();
   auto pos = track->GetPosition();
-  m_trackPx[m_trackNum] = mom.x() / CLHEP::GeV;
-  m_trackPy[m_trackNum] = mom.y() / CLHEP::GeV;
-  m_trackPz[m_trackNum] = mom.z() / CLHEP::GeV;
-  m_trackX[m_trackNum] = pos.x() / CLHEP::mm;
-  m_trackY[m_trackNum] = pos.y() / CLHEP::mm;
-  m_trackZ[m_trackNum] = pos.z() / CLHEP::mm;
+  m_trackPx[m_trackNum] = mom.x() / GeV;
+  m_trackPy[m_trackNum] = mom.y() / GeV;
+  m_trackPz[m_trackNum] = mom.z() / GeV;
+  m_trackX[m_trackNum] = pos.x() / mm;
+  m_trackY[m_trackNum] = pos.y() / mm;
+  m_trackZ[m_trackNum] = pos.z() / mm;
   m_trackCharge[m_trackNum] = track->GetParticleDefinition()->GetPDGCharge();
   m_trackPDGId[m_trackNum] =
       track->GetParticleDefinition()->GetParticleDefinitionID();
@@ -219,6 +224,7 @@ void ChronoAnaElemTool::PreUserTrackingAction(const G4Track *track) {
     auto stop = NanoClock::now();
     auto event_duration = NanoClock::duration(stop - m_subdet_enter).count();
     m_timePerSubdet[m_currentSubdetIndex] += event_duration;
+    // switchSimulationGranularity(newSubdet, m_currentSubdetIndex);
     m_currentSubdetIndex = newSubdet;
     m_subdet_enter = NanoClock::now();
   }
@@ -288,12 +294,35 @@ void ChronoAnaElemTool::UserSteppingAction(const G4Step *step) {
       //          << m_currentSubdetIndex << " entering " << newSubdet
       //          << " event time " << event_duration << " track time "
       //          << track_duration << endmsg;
+      // switchSimulationGranularity(newSubdet, m_currentSubdetIndex);
       m_currentSubdetIndex = newSubdet;
       m_subdet_track_enter = m_subdet_enter = NanoClock::now();
     }
   }
 }
 
+inline void ChronoAnaElemTool::switchSimulationGranularity(
+    const ChronoAnaElemTool::size_t newsubdet,
+    const ChronoAnaElemTool::size_t currentSubdet) {
+  if (newsubdet != currentSubdet) {
+    if (currentSubdet == TPCIndex) {
+      // leaving TPC, turn high
+      m_fieldManager->SetDeltaIntersection(1.0e-5 * mm);
+      m_fieldManager->SetDeltaOneStep(1.0e-4 * mm);
+      m_fieldManager->SetMinimumEpsilonStep(5.0e-5 * mm);
+      m_fieldManager->SetMaximumEpsilonStep(1.0e-3 * mm);
+      m_fieldManager->GetChordFinder()->SetDeltaChord(0.25 * mm);
+    } else if (newsubdet == TPCIndex) {
+      // entering TPC, turn low
+      m_fieldManager->SetDeltaIntersection(0.001 * mm);
+      m_fieldManager->SetDeltaOneStep(0.01 * mm);
+      m_fieldManager->SetMinimumEpsilonStep(5.0e-5 * mm);
+      m_fieldManager->SetMaximumEpsilonStep(1.0e-3 * mm);
+      m_fieldManager->GetChordFinder()->SetDeltaChord(25 * mm);
+    }
+  }
+}
+
 StatusCode ChronoAnaElemTool::finalize() {
   m_ntuple->reset();
 
diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.h b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
index 4893f5ca..b889c32d 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.h
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
@@ -58,6 +58,7 @@ private:
   StatusCode initSubdetVolumes();
   static constexpr size_t UnknownIndex = 0;
   static constexpr size_t PWorldIndex = 1;
+  static constexpr size_t TPCIndex = 25;
   std::vector<std::string> m_subdetNames;
   std::unordered_map<G4LogicalVolume *, size_t> m_logicalVolumePtrToSubdetIdx;
 
@@ -88,6 +89,10 @@ private:
 
   NTuple::Matrix<time_t>
       m_trackTimePerSubdet; // tracknum * subdet_size + subdet_idx
+
+  inline void switchSimulationGranularity(const size_t newsubdet,
+                                          const size_t currentSubdet);
+  G4FieldManager *m_fieldManager;
 };
 
 #endif
-- 
GitLab


From 6b2cd43e8225c71ac07911fd55fac4b3b0867aa3 Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Wed, 4 Jun 2025 15:58:44 +0800
Subject: [PATCH 52/74] fix: remove field manager

---
 .../DetSimAna/src/ChronoAnaElemTool.cpp       | 46 +++++++++----------
 Simulation/DetSimAna/src/ChronoAnaElemTool.h  |  6 +--
 2 files changed, 26 insertions(+), 26 deletions(-)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
index 38b8b4ff..e095aa15 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -163,8 +163,8 @@ StatusCode ChronoAnaElemTool::initialize() {
     return StatusCode::FAILURE;
   }
 
-  m_fieldManager =
-      G4TransportationManager::GetTransportationManager()->GetFieldManager();
+  // m_fieldManager =
+  //     G4TransportationManager::GetTransportationManager()->GetFieldManager();
 
   // calibrate the clock before starting the simulation
   NanoClock::calibrate();
@@ -301,27 +301,27 @@ void ChronoAnaElemTool::UserSteppingAction(const G4Step *step) {
   }
 }
 
-inline void ChronoAnaElemTool::switchSimulationGranularity(
-    const ChronoAnaElemTool::size_t newsubdet,
-    const ChronoAnaElemTool::size_t currentSubdet) {
-  if (newsubdet != currentSubdet) {
-    if (currentSubdet == TPCIndex) {
-      // leaving TPC, turn high
-      m_fieldManager->SetDeltaIntersection(1.0e-5 * mm);
-      m_fieldManager->SetDeltaOneStep(1.0e-4 * mm);
-      m_fieldManager->SetMinimumEpsilonStep(5.0e-5 * mm);
-      m_fieldManager->SetMaximumEpsilonStep(1.0e-3 * mm);
-      m_fieldManager->GetChordFinder()->SetDeltaChord(0.25 * mm);
-    } else if (newsubdet == TPCIndex) {
-      // entering TPC, turn low
-      m_fieldManager->SetDeltaIntersection(0.001 * mm);
-      m_fieldManager->SetDeltaOneStep(0.01 * mm);
-      m_fieldManager->SetMinimumEpsilonStep(5.0e-5 * mm);
-      m_fieldManager->SetMaximumEpsilonStep(1.0e-3 * mm);
-      m_fieldManager->GetChordFinder()->SetDeltaChord(25 * mm);
-    }
-  }
-}
+// inline void ChronoAnaElemTool::switchSimulationGranularity(
+//     const ChronoAnaElemTool::size_t newsubdet,
+//     const ChronoAnaElemTool::size_t currentSubdet) {
+//   if (newsubdet != currentSubdet) {
+//     if (currentSubdet == TPCIndex) {
+//       // leaving TPC, turn high
+//       m_fieldManager->SetDeltaIntersection(1.0e-5 * mm);
+//       m_fieldManager->SetDeltaOneStep(1.0e-4 * mm);
+//       m_fieldManager->SetMinimumEpsilonStep(5.0e-5 * mm);
+//       m_fieldManager->SetMaximumEpsilonStep(1.0e-3 * mm);
+//       m_fieldManager->GetChordFinder()->SetDeltaChord(0.25 * mm);
+//     } else if (newsubdet == TPCIndex) {
+//       // entering TPC, turn low
+//       m_fieldManager->SetDeltaIntersection(0.001 * mm);
+//       m_fieldManager->SetDeltaOneStep(0.01 * mm);
+//       m_fieldManager->SetMinimumEpsilonStep(5.0e-5 * mm);
+//       m_fieldManager->SetMaximumEpsilonStep(1.0e-3 * mm);
+//       m_fieldManager->GetChordFinder()->SetDeltaChord(25 * mm);
+//     }
+//   }
+// }
 
 StatusCode ChronoAnaElemTool::finalize() {
   m_ntuple->reset();
diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.h b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
index b889c32d..93ff125f 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.h
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
@@ -90,9 +90,9 @@ private:
   NTuple::Matrix<time_t>
       m_trackTimePerSubdet; // tracknum * subdet_size + subdet_idx
 
-  inline void switchSimulationGranularity(const size_t newsubdet,
-                                          const size_t currentSubdet);
-  G4FieldManager *m_fieldManager;
+  // inline void switchSimulationGranularity(const size_t newsubdet,
+  //                                         const size_t currentSubdet);
+  // G4FieldManager *m_fieldManager;
 };
 
 #endif
-- 
GitLab


From dfed17ef3b06a054708bfd807cae9251247c469e Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Fri, 6 Jun 2025 10:35:24 +0800
Subject: [PATCH 53/74] fix: remove sim grant change

---
 .../DetSimAna/src/ChronoAnaElemTool.cpp       | 25 -------------------
 Simulation/DetSimAna/src/ChronoAnaElemTool.h  |  4 ---
 2 files changed, 29 deletions(-)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
index e095aa15..0e9fe1a6 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -6,7 +6,6 @@
 #include <cstdlib>
 #include <string>
 
-#include "G4ChordFinder.hh"
 #include "G4Event.hh"
 #include "G4FieldManager.hh"
 #include "G4Step.hh"
@@ -224,7 +223,6 @@ void ChronoAnaElemTool::PreUserTrackingAction(const G4Track *track) {
     auto stop = NanoClock::now();
     auto event_duration = NanoClock::duration(stop - m_subdet_enter).count();
     m_timePerSubdet[m_currentSubdetIndex] += event_duration;
-    // switchSimulationGranularity(newSubdet, m_currentSubdetIndex);
     m_currentSubdetIndex = newSubdet;
     m_subdet_enter = NanoClock::now();
   }
@@ -294,35 +292,12 @@ void ChronoAnaElemTool::UserSteppingAction(const G4Step *step) {
       //          << m_currentSubdetIndex << " entering " << newSubdet
       //          << " event time " << event_duration << " track time "
       //          << track_duration << endmsg;
-      // switchSimulationGranularity(newSubdet, m_currentSubdetIndex);
       m_currentSubdetIndex = newSubdet;
       m_subdet_track_enter = m_subdet_enter = NanoClock::now();
     }
   }
 }
 
-// inline void ChronoAnaElemTool::switchSimulationGranularity(
-//     const ChronoAnaElemTool::size_t newsubdet,
-//     const ChronoAnaElemTool::size_t currentSubdet) {
-//   if (newsubdet != currentSubdet) {
-//     if (currentSubdet == TPCIndex) {
-//       // leaving TPC, turn high
-//       m_fieldManager->SetDeltaIntersection(1.0e-5 * mm);
-//       m_fieldManager->SetDeltaOneStep(1.0e-4 * mm);
-//       m_fieldManager->SetMinimumEpsilonStep(5.0e-5 * mm);
-//       m_fieldManager->SetMaximumEpsilonStep(1.0e-3 * mm);
-//       m_fieldManager->GetChordFinder()->SetDeltaChord(0.25 * mm);
-//     } else if (newsubdet == TPCIndex) {
-//       // entering TPC, turn low
-//       m_fieldManager->SetDeltaIntersection(0.001 * mm);
-//       m_fieldManager->SetDeltaOneStep(0.01 * mm);
-//       m_fieldManager->SetMinimumEpsilonStep(5.0e-5 * mm);
-//       m_fieldManager->SetMaximumEpsilonStep(1.0e-3 * mm);
-//       m_fieldManager->GetChordFinder()->SetDeltaChord(25 * mm);
-//     }
-//   }
-// }
-
 StatusCode ChronoAnaElemTool::finalize() {
   m_ntuple->reset();
 
diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.h b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
index 93ff125f..26575e00 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.h
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
@@ -89,10 +89,6 @@ private:
 
   NTuple::Matrix<time_t>
       m_trackTimePerSubdet; // tracknum * subdet_size + subdet_idx
-
-  // inline void switchSimulationGranularity(const size_t newsubdet,
-  //                                         const size_t currentSubdet);
-  // G4FieldManager *m_fieldManager;
 };
 
 #endif
-- 
GitLab


From 7862565adbef7232c5ece2349b568ae261e5754f Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Mon, 9 Jun 2025 11:23:52 +0800
Subject: [PATCH 54/74] feat: reformat using track user info

---
 .../DetSimAna/src/ChronoAnaElemTool.cpp       | 267 +++++++++---------
 Simulation/DetSimAna/src/ChronoAnaElemTool.h  |  49 ++--
 .../DetSimInterface/CommonUserTrackInfo.hh    |  72 ++++-
 3 files changed, 226 insertions(+), 162 deletions(-)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
index 0e9fe1a6..7c193eb5 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -1,15 +1,15 @@
-#include <G4StepStatus.hh>
-#include <GaudiKernel/StatusCode.h>
 #include <cassert>
 #include <cerrno>
 #include <climits>
+#include <cstddef>
+#include <cstdint>
 #include <cstdlib>
 #include <string>
 
 #include "G4Event.hh"
 #include "G4FieldManager.hh"
 #include "G4Step.hh"
-#include "G4SystemOfUnits.hh"
+#include "G4StepStatus.hh"
 #include "G4Track.hh"
 #include "G4TransportationManager.hh"
 #include "GaudiAlg/GaudiAlgorithm.h"
@@ -17,9 +17,11 @@
 #include "GaudiKernel/MsgStream.h"
 #include "GaudiKernel/NTuple.h"
 #include "GaudiKernel/SmartIF.h"
+#include "GaudiKernel/StatusCode.h"
 
 #include "ChronoAnaElemTool.h"
 #include "DetInterface/IGeomSvc.h"
+#include "DetSimInterface/CommonUserTrackInfo.hh"
 
 DECLARE_COMPONENT(ChronoAnaElemTool)
 
@@ -29,28 +31,28 @@ StatusCode ChronoAnaElemTool::initSubdetVolumes() {
                        ->GetNavigatorForTracking()
                        ->GetWorldVolume();
   auto num_dau = world_vol->GetLogicalVolume()->GetNoDaughters();
-  debug() << "World Physical Volume - Name: " << world_vol->GetName()
-          << " Pointer: " << world_vol << " World Logical Volume - Name: "
-          << world_vol->GetLogicalVolume()->GetName()
-          << " Pointer: " << world_vol->GetLogicalVolume() << endmsg;
+  // debug() << "World Physical Volume - Name: " << world_vol->GetName()
+  //         << " Pointer: " << world_vol << " World Logical Volume - Name: "
+  //         << world_vol->GetLogicalVolume()->GetName()
+  //         << " Pointer: " << world_vol->GetLogicalVolume() << endmsg;
   m_logicalVolumePtrToSubdetIdx[world_vol->GetLogicalVolume()] = PWorldIndex;
-  debug() << "Set m_logicalVolumePtrToSubdetIdx["
-          << world_vol->GetLogicalVolume() << "] to subdet index "
-          << PWorldIndex << endmsg;
+  // debug() << "Set m_logicalVolumePtrToSubdetIdx["
+  //         << world_vol->GetLogicalVolume() << "] to subdet index "
+  //         << PWorldIndex << endmsg;
   for (size_t _dau = 0; _dau < num_dau; ++_dau) {
     auto dau_vol = world_vol->GetLogicalVolume()->GetDaughter(_dau);
-    debug() << "Daughter Physical Volume - Name: " << dau_vol->GetName()
-            << " Pointer: " << dau_vol << " Daughter Logical Volume - Name: "
-            << dau_vol->GetLogicalVolume()->GetName()
-            << " Pointer: " << dau_vol->GetLogicalVolume() << endmsg;
+    // debug() << "Daughter Physical Volume - Name: " << dau_vol->GetName()
+    //         << " Pointer: " << dau_vol << " Daughter Logical Volume - Name: "
+    //         << dau_vol->GetLogicalVolume()->GetName()
+    //         << " Pointer: " << dau_vol->GetLogicalVolume() << endmsg;
     auto dau_name = dau_vol->GetName();
-    for (size_t _subdet = PWorldIndex; _subdet < m_subdetNames.size();
+    for (uint8_t _subdet = PWorldIndex; _subdet < m_subdetNames.size();
          ++_subdet) {
       if (dau_name.find(m_subdetNames[_subdet]) != std::string::npos) {
         m_logicalVolumePtrToSubdetIdx[dau_vol->GetLogicalVolume()] = _subdet;
-        debug() << "Set m_logicalVolumePtrToSubdetIdx["
-                << dau_vol->GetLogicalVolume() << "] to subdet index "
-                << _subdet << endmsg;
+        // debug() << "Set m_logicalVolumePtrToSubdetIdx["
+        //         << dau_vol->GetLogicalVolume() << "] to subdet index "
+        //         << _subdet << endmsg;
         continue;
       }
     }
@@ -98,51 +100,55 @@ StatusCode ChronoAnaElemTool::initNtuple() {
       info() << "Create Ntuple Success" << endmsg;
       suc &= m_ntuple->addItem("EventNumber", m_eventNumber, 0U, UINT_MAX)
                  .isSuccess();
-      suc &= m_ntuple->addItem("TotSimTime", m_eventTotSimTime).isSuccess();
       suc &=
-          m_ntuple
-              ->addItem("TimePerSubdet", m_subdetNames.size(), m_timePerSubdet)
+          m_ntuple->addItem("TotSimTime", m_eventTotSimTime, 0ULL, ULLONG_MAX)
               .isSuccess();
-      suc &= m_ntuple->addItem("TrackNum", m_trackNum, 0U, MAX_TRACK_NUM)
+      suc &= m_ntuple->addItem("TrackNum", m_trackIdx, 0U, MAX_TRACK_NUM)
                  .isSuccess();
-      suc &= m_ntuple->addItem("TrackPx", m_trackNum, m_trackPx);
-      suc &= m_ntuple->addItem("TrackPy", m_trackNum, m_trackPy);
-      suc &= m_ntuple->addItem("TrackPz", m_trackNum, m_trackPz);
-      suc &= m_ntuple->addItem("TrackX", m_trackNum, m_trackX);
-      suc &= m_ntuple->addItem("TrackY", m_trackNum, m_trackY);
-      suc &= m_ntuple->addItem("TrackZ", m_trackNum, m_trackZ);
-      suc &= m_ntuple->addIndexedItem("TrackLengths", m_trackNum, m_trackLength)
+      suc &= m_ntuple->addItem("TrackPx", m_trackIdx, m_trackPx);
+      suc &= m_ntuple->addItem("TrackPy", m_trackIdx, m_trackPy);
+      suc &= m_ntuple->addItem("TrackPz", m_trackIdx, m_trackPz);
+      suc &= m_ntuple->addItem("TrackX", m_trackIdx, m_trackX);
+      suc &= m_ntuple->addItem("TrackY", m_trackIdx, m_trackY);
+      suc &= m_ntuple->addItem("TrackZ", m_trackIdx, m_trackZ);
+      suc &= m_ntuple->addIndexedItem("TrackLengths", m_trackIdx, m_trackLength)
                  .isSuccess();
-      suc &= m_ntuple->addIndexedItem("TrackTimes", m_trackNum, m_trackTime)
+      suc &= m_ntuple->addIndexedItem("TrackTimes", m_trackIdx, m_trackTime)
                  .isSuccess();
-      suc &= m_ntuple->addIndexedItem("TrackPDGID", m_trackNum, m_trackPDGId)
+      suc &= m_ntuple->addIndexedItem("TrackPDGID", m_trackIdx, m_trackPDGId)
                  .isSuccess();
       suc &= m_ntuple
-                 ->addIndexedItem("TrackStartSubdet", m_trackNum,
+                 ->addIndexedItem("TrackStartSubdet", m_trackIdx,
                                   m_trackStartSubdet)
                  .isSuccess();
       suc &=
           m_ntuple
-              ->addIndexedItem("TrackEndSubdet", m_trackNum, m_trackEndSubdet)
+              ->addIndexedItem("TrackEndSubdet", m_trackIdx, m_trackEndSubdet)
               .isSuccess();
-      suc &= m_ntuple->addIndexedItem("TrackCharge", m_trackNum, m_trackCharge)
-                 .isSuccess();
       suc &=
-          m_ntuple->addIndexedItem("TrackStepNum", m_trackNum, m_trackStepNum)
+          m_ntuple->addIndexedItem("TrackStepNum", m_trackIdx, m_trackStepNum)
               .isSuccess();
-      suc &= m_ntuple->addIndexedItem("TrackID", m_trackNum, m_trackID)
+      suc &= m_ntuple->addIndexedItem("TrackEnergy", m_trackIdx, m_trackE)
                  .isSuccess();
-      suc &=
-          m_ntuple->addIndexedItem("TrackParentID", m_trackNum, m_trackParentID)
-              .isSuccess();
-      suc &= m_ntuple
-                 ->addIndexedItem("TrackKineticEnergy", m_trackNum,
-                                  m_trackKineticEnergy)
+      suc &= m_ntuple->addIndexedItem("TrackDeltaEnergy", m_trackIdx, m_trackdE)
                  .isSuccess();
       suc &= m_ntuple
-                 ->addIndexedItem("TrackTimePerSubdet", m_trackNum,
+                 ->addIndexedItem("TrackTimePerSubdet", m_trackIdx,
                                   MAX_SUBDET_NUM, m_trackTimePerSubdet)
                  .isSuccess();
+      suc &= m_ntuple
+                 ->addIndexedItem("TrackStepPerSubdet", m_trackIdx,
+                                  MAX_SUBDET_NUM, m_trackStepPerSubdet)
+                 .isSuccess();
+      suc &= m_ntuple
+                 ->addIndexedItem("TrackNumSubdetSequence", m_trackIdx,
+                                  m_trackNumSubdetSequence)
+                 .isSuccess();
+      suc &=
+          m_ntuple
+              ->addIndexedItem("TrackSubdetSequence", m_trackIdx,
+                               MAX_SUBDET_SEQUENCE_NUM, m_trackSubdetSequence)
+              .isSuccess();
     } else {
       error() << "Cannot book N-tuple." << endmsg;
       return StatusCode::FAILURE;
@@ -162,9 +168,6 @@ StatusCode ChronoAnaElemTool::initialize() {
     return StatusCode::FAILURE;
   }
 
-  // m_fieldManager =
-  //     G4TransportationManager::GetTransportationManager()->GetFieldManager();
-
   // calibrate the clock before starting the simulation
   NanoClock::calibrate();
 
@@ -175,94 +178,128 @@ StatusCode ChronoAnaElemTool::initialize() {
   return suc ? StatusCode::SUCCESS : StatusCode::FAILURE;
 }
 
+void ChronoAnaElemTool::BeginOfEventAction(const G4Event *event) {
+  m_ntuple->reset();
+  m_eventNumber = static_cast<uint>(event->GetEventID());
+  always() << " ChronoAnaElemTool: Event number: " << m_eventNumber << endmsg;
+  if (m_eventNumber < m_skipFirstNEvents.value()) {
+    return;
+  }
+
+  m_event_start = NanoClock::now();
+}
+
 void ChronoAnaElemTool::EndOfEventAction(const G4Event *) {
   if (m_eventNumber < m_skipFirstNEvents.value()) {
     return;
   }
   NanoClock::time_point event_stop = NanoClock::now();
-  m_timePerSubdet[m_currentSubdetIndex] +=
-      NanoClock::duration(event_stop - m_subdet_enter).count();
   m_eventTotSimTime = NanoClock::duration(event_stop - m_event_start).count();
   if (m_ntuple->write().isFailure()) {
     error() << "Error write track tuple at event " << m_eventNumber << endmsg;
   }
 }
 
-void ChronoAnaElemTool::BeginOfEventAction(const G4Event *event) {
-  m_ntuple->reset();
-  m_eventNumber = static_cast<size_t>(event->GetEventID());
-  always() << " ChronoAnaElemTool: Event number: " << m_eventNumber << endmsg;
+void ChronoAnaElemTool::PreUserTrackingAction(const G4Track *track) {
   if (m_eventNumber < m_skipFirstNEvents.value()) {
     return;
   }
 
-  m_currentSubdetIndex = UnknownIndex;
-  m_subdet_enter = m_event_start = NanoClock::now();
+  // ensure info exists
+  auto info = static_cast<CommonUserTrackInfo *>(track->GetUserInformation());
+  if (!info) {
+    info = new CommonUserTrackInfo();
+    track->SetUserInformation(info);
+  }
+  auto currentSubdet = getSubdetNameIdx(track->GetTouchable());
+  // 根据已有的 chronoTrackIdx 判断是新 track 还是 resumed
+  int chronoTrackIdx = info->GetChronoTrackIdx();
+  if (chronoTrackIdx < 0) {
+    // new track
+    chronoTrackIdx = m_trackIdx++; // first copy then self-add
+    info->SetChronoTrackIdx(chronoTrackIdx);
+    const auto &mom = track->GetMomentum();
+    const auto &pos = track->GetPosition();
+    m_trackPx[chronoTrackIdx] = mom.x();
+    m_trackPy[chronoTrackIdx] = mom.y();
+    m_trackPz[chronoTrackIdx] = mom.z();
+    m_trackX[chronoTrackIdx] = pos.x();
+    m_trackY[chronoTrackIdx] = pos.y();
+    m_trackZ[chronoTrackIdx] = pos.z();
+    m_trackE[chronoTrackIdx] = track->GetTotalEnergy();
+    m_trackPDGId[chronoTrackIdx] =
+        track->GetParticleDefinition()->GetParticleDefinitionID();
+    m_trackStartSubdet[chronoTrackIdx] = currentSubdet;
+  } else {
+    // resumed track
+    error() << "resumed track!!! TrackIdx=" << chronoTrackIdx << endmsg;
+  }
+
+  info->GetStepNumDeltaAndSet(track->GetCurrentStepNumber());
+  info->SetCurrentSubdet(currentSubdet);
+  const auto start = NanoClock::now();
+  info->SetTrackStartTime(start);
+  info->SetTrackSubdetChangeTime(start);
 }
 
-void ChronoAnaElemTool::PreUserTrackingAction(const G4Track *track) {
+void ChronoAnaElemTool::PostUserTrackingAction(const G4Track *track) {
   if (m_eventNumber < m_skipFirstNEvents.value()) {
     return;
   }
-  m_trackID[m_trackNum] = track->GetTrackID();
-  m_trackParentID[m_trackNum] = track->GetParentID();
-  m_trackKineticEnergy[m_trackNum] = track->GetKineticEnergy() / GeV;
-  auto mom = track->GetMomentum();
-  auto pos = track->GetPosition();
-  m_trackPx[m_trackNum] = mom.x() / GeV;
-  m_trackPy[m_trackNum] = mom.y() / GeV;
-  m_trackPz[m_trackNum] = mom.z() / GeV;
-  m_trackX[m_trackNum] = pos.x() / mm;
-  m_trackY[m_trackNum] = pos.y() / mm;
-  m_trackZ[m_trackNum] = pos.z() / mm;
-  m_trackCharge[m_trackNum] = track->GetParticleDefinition()->GetPDGCharge();
-  m_trackPDGId[m_trackNum] =
-      track->GetParticleDefinition()->GetParticleDefinitionID();
-  auto newSubdet = m_trackStartSubdet[m_trackNum] = getSubdetNameIdx(track);
-  if (newSubdet != m_currentSubdetIndex) {
-    auto stop = NanoClock::now();
-    auto event_duration = NanoClock::duration(stop - m_subdet_enter).count();
-    m_timePerSubdet[m_currentSubdetIndex] += event_duration;
-    m_currentSubdetIndex = newSubdet;
-    m_subdet_enter = NanoClock::now();
+
+  auto trackStop = NanoClock::now();
+  auto info = static_cast<CommonUserTrackInfo *>(track->GetUserInformation());
+  auto chronoTrackIdx = info->GetChronoTrackIdx();
+  auto currentStep = track->GetCurrentStepNumber();
+
+  // record value
+  m_trackdE[chronoTrackIdx] =
+      track->GetTotalEnergy() - m_trackE[chronoTrackIdx];
+  m_trackTime[chronoTrackIdx] +=
+      NanoClock::duration(trackStop - info->GetTrackStartTime()).count();
+  auto track_end_subdet = getSubdetNameIdx(track->GetTouchable());
+  m_trackLength[chronoTrackIdx] = track->GetTrackLength();
+  m_trackStepNum[chronoTrackIdx] = currentStep;
+  m_trackEndSubdet[chronoTrackIdx] = track_end_subdet;
+  // add remained time
+  info->UpdateTrackInfo(trackStop, currentStep);
+  const auto trackStepPerSubdet = info->GetTrackStepPerSubdet();
+  const auto trackTimePerSubdet = info->GetTrackTimePerSubdet();
+  for (size_t i = 0; i < MAX_SUBDET_NUM; i++) {
+    m_trackTimePerSubdet[chronoTrackIdx][i] = trackTimePerSubdet[i];
+    m_trackStepPerSubdet[chronoTrackIdx][i] = trackStepPerSubdet[i];
   }
-  // always() << "track " << m_trackID[m_trackNum] << " start in "
-  //          << m_trackStartSubdet[m_trackNum] << endmsg;
-  m_subdet_track_enter = m_track_start = NanoClock::now();
 }
 
-void ChronoAnaElemTool::PostUserTrackingAction(const G4Track *track) {
+void ChronoAnaElemTool::UserSteppingAction(const G4Step *step) {
   if (m_eventNumber < m_skipFirstNEvents.value()) {
     return;
   }
-  auto stop = NanoClock::now();
-  m_trackTime[m_trackNum] = NanoClock::duration(stop - m_track_start).count();
-  auto track_end_subdet = getSubdetNameIdx(track);
-  m_trackLength[m_trackNum] = track->GetTrackLength();
-  m_trackStepNum[m_trackNum] = track->GetCurrentStepNumber();
-  m_trackEndSubdet[m_trackNum] = track_end_subdet;
-  // track did not change subdet, add time
-  m_trackTimePerSubdet[m_trackNum][m_currentSubdetIndex] +=
-      NanoClock::duration(stop - m_subdet_track_enter).count();
-  if (m_currentSubdetIndex != track_end_subdet) {
-    error() << "Current Subdet " << m_currentSubdetIndex
-            << " not equal with track end subdet " << track_end_subdet
-            << endmsg;
+
+  const auto &stepStatus = step->GetPostStepPoint()->GetStepStatus();
+  if (stepStatus == fGeomBoundary || stepStatus == fWorldBoundary) {
+    // last step in a volume or world
+    const auto &track = step->GetTrack();
+    auto info = static_cast<CommonUserTrackInfo *>(track->GetUserInformation());
+    auto newSubdet = getSubdetNameIdx(track->GetNextTouchable());
+    if (info->IsSubdetChanged(newSubdet)) {
+      auto stop = NanoClock::now();
+      auto currentStep = track->GetCurrentStepNumber();
+      info->UpdateTrackInfo(stop, currentStep);
+      info->SetCurrentSubdet(newSubdet);
+      info->SetTrackSubdetChangeTime(stop);
+    }
   }
-  // always() << "track " << m_trackID[m_trackNum] << " end in "
-  //          << track_end_subdet << endmsg;
-  ++m_trackNum;
 }
 
-inline ChronoAnaElemTool::size_t
-ChronoAnaElemTool::getSubdetNameIdx(const G4Track *track) {
-  auto historyDepthNum = track->GetTouchable()->GetHistoryDepth();
+inline uint8_t
+ChronoAnaElemTool::getSubdetNameIdx(const G4VTouchable *touchable) {
+  auto historyDepthNum = touchable->GetHistoryDepth();
   if (historyDepthNum == 0) {
     return PWorldIndex;
   } else {
-    auto lastVolume = track->GetTouchable()
-                          ->GetVolume(historyDepthNum - 1)
-                          ->GetLogicalVolume();
+    auto lastVolume =
+        touchable->GetVolume(historyDepthNum - 1)->GetLogicalVolume();
     auto it = m_logicalVolumePtrToSubdetIdx.find(lastVolume);
     if (it != m_logicalVolumePtrToSubdetIdx.end()) {
       return it->second;
@@ -274,30 +311,6 @@ ChronoAnaElemTool::getSubdetNameIdx(const G4Track *track) {
   }
 }
 
-void ChronoAnaElemTool::UserSteppingAction(const G4Step *step) {
-  if (m_eventNumber < m_skipFirstNEvents.value()) {
-    return;
-  }
-  if (step->IsFirstStepInVolume()) {
-    auto track = step->GetTrack();
-    auto newSubdet = getSubdetNameIdx(track);
-    if (newSubdet != m_currentSubdetIndex) {
-      auto stop = NanoClock::now();
-      auto event_duration = NanoClock::duration(stop - m_subdet_enter).count();
-      auto track_duration =
-          NanoClock::duration(stop - m_subdet_track_enter).count();
-      m_timePerSubdet[m_currentSubdetIndex] += event_duration;
-      m_trackTimePerSubdet[m_trackNum][m_currentSubdetIndex] += track_duration;
-      // always() << "track " << track->GetTrackID() << " from "
-      //          << m_currentSubdetIndex << " entering " << newSubdet
-      //          << " event time " << event_duration << " track time "
-      //          << track_duration << endmsg;
-      m_currentSubdetIndex = newSubdet;
-      m_subdet_track_enter = m_subdet_enter = NanoClock::now();
-    }
-  }
-}
-
 StatusCode ChronoAnaElemTool::finalize() {
   m_ntuple->reset();
 
diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.h b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
index 26575e00..463edabb 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.h
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
@@ -3,6 +3,7 @@
 
 #include <chrono>
 #include <climits>
+#include <cstdint>
 #include <string>
 #include <unordered_map>
 #include <vector>
@@ -11,6 +12,7 @@
 #include "DetSimInterface/IAnaElemTool.h"
 
 #include "G4LogicalVolume.hh"
+#include "G4VTouchable.hh"
 #include "Gaudi/Timers/RdtscClock.h"
 #include "GaudiKernel/AlgTool.h"
 #include "GaudiKernel/INTupleSvc.h"
@@ -23,7 +25,7 @@ public:
   using extends::extends;
   using NanoClock = Gaudi::Timers::RdtscClock<std::chrono::nanoseconds>;
   typedef unsigned long long time_t;
-  typedef unsigned int size_t;
+  typedef unsigned long long uint64_t;
 
   // Event
   virtual void BeginOfEventAction(const G4Event *) override;
@@ -47,48 +49,41 @@ private:
   NTuple::Tuple *m_ntuple;
   StatusCode initNtuple();
 
-  NTuple::Item<size_t> m_eventNumber;
+  NTuple::Item<uint> m_eventNumber;
   NTuple::Item<time_t> m_eventTotSimTime;
   NanoClock::time_point m_event_start;
 
-  Gaudi::Property<size_t> m_skipFirstNEvents{this, "SkipFirstNEvents", 5};
+  Gaudi::Property<uint> m_skipFirstNEvents{this, "SkipFirstNEvents", 5};
 
   // simtime over subdet
   StatusCode initSubdetNames();
   StatusCode initSubdetVolumes();
-  static constexpr size_t UnknownIndex = 0;
-  static constexpr size_t PWorldIndex = 1;
-  static constexpr size_t TPCIndex = 25;
+  static constexpr uint8_t UnknownIndex = 0;
+  static constexpr uint8_t PWorldIndex = 1;
   std::vector<std::string> m_subdetNames;
-  std::unordered_map<G4LogicalVolume *, size_t> m_logicalVolumePtrToSubdetIdx;
+  std::unordered_map<G4LogicalVolume *, uint8_t> m_logicalVolumePtrToSubdetIdx;
 
-  inline size_t getSubdetNameIdx(const G4Track *track);
-  size_t m_currentSubdetIndex = UnknownIndex;
-  NanoClock::time_point m_subdet_enter;
-  NTuple::Array<time_t> m_timePerSubdet;
+  inline uint8_t getSubdetNameIdx(const G4VTouchable *touchable);
 
   // simtime over track
-  static constexpr size_t MAX_TRACK_NUM = 10'000'000; // 10M
-  static constexpr size_t MAX_SUBDET_NUM = 30;
+  static constexpr uint MAX_TRACK_NUM = 1'000'000; // 1M
   NanoClock::time_point m_track_start;
-  NanoClock::time_point m_subdet_track_enter;
 
-  NTuple::Item<size_t> m_trackNum;
-  NTuple::Array<int> m_trackID;
-  NTuple::Array<int> m_trackParentID;
-  NTuple::Array<double> m_trackKineticEnergy;
+  NTuple::Item<uint> m_trackIdx;
   NTuple::Array<double> m_trackPx, m_trackPy, m_trackPz;
   NTuple::Array<double> m_trackX, m_trackY, m_trackZ;
-  NTuple::Array<double> m_trackLength;
-  NTuple::Array<double> m_trackCharge;
-  NTuple::Array<int> m_trackPDGId;
-  NTuple::Array<int> m_trackStepNum;
+  NTuple::Array<double> m_trackLength, m_trackE, m_trackdE;
+  NTuple::Array<int> m_trackPDGId, m_trackStepNum;
   NTuple::Array<time_t> m_trackTime;
-  NTuple::Array<size_t> m_trackStartSubdet;
-  NTuple::Array<size_t> m_trackEndSubdet;
-
-  NTuple::Matrix<time_t>
-      m_trackTimePerSubdet; // tracknum * subdet_size + subdet_idx
+  NTuple::Array<uint8_t> m_trackStartSubdet, m_trackEndSubdet;
+
+  static constexpr uint MAX_SUBDET_NUM = 26;
+  NTuple::Matrix<time_t> m_trackTimePerSubdet;
+  NTuple::Matrix<int> m_trackStepPerSubdet;
+  static constexpr uint MAX_SUBDET_SEQUENCE_NUM = 10;
+  NTuple::Array<int> m_trackNumSubdetSequence;
+  NTuple::Matrix<uint64_t>
+      m_trackSubdetSequence; // 5bit for one subdet, uint64 for 12 subdets
 };
 
 #endif
diff --git a/Simulation/DetSimInterface/include/DetSimInterface/CommonUserTrackInfo.hh b/Simulation/DetSimInterface/include/DetSimInterface/CommonUserTrackInfo.hh
index ce63e602..7ae3590d 100644
--- a/Simulation/DetSimInterface/include/DetSimInterface/CommonUserTrackInfo.hh
+++ b/Simulation/DetSimInterface/include/DetSimInterface/CommonUserTrackInfo.hh
@@ -14,22 +14,78 @@
  */
 
 #include "G4VUserTrackInformation.hh"
+#include "Gaudi/Timers/RdtscClock.h"
+#include <array>
+#include <cstdint>
 
-class CommonUserTrackInfo: public G4VUserTrackInformation {
+class CommonUserTrackInfo : public G4VUserTrackInformation {
 public:
-    CommonUserTrackInfo();
-    ~CommonUserTrackInfo();
+  using NanoClock = Gaudi::Timers::RdtscClock<std::chrono::nanoseconds>;
+  static constexpr uint MAX_SUBDET_NUM = 26;
+  CommonUserTrackInfo();
+  ~CommonUserTrackInfo();
 
 public:
+  virtual void Print() const;
 
-    virtual void Print() const;
+  // get the idx in the EDM4hep MC particle collection
+  bool setIdxEdm4hep(int idxEdm4hep);
+  int idxEdm4hep() const;
 
-    // get the idx in the EDM4hep MC particle collection
-    bool setIdxEdm4hep(int idxEdm4hep);
-    int idxEdm4hep() const;
+  inline int GetChronoTrackIdx() { return m_chronoTrackIdx; };
+  inline void SetChronoTrackIdx(const int chronoTrackIdx) {
+    m_chronoTrackIdx = chronoTrackIdx;
+  };
+  inline uint8_t GetCurrentSubdet() const { return m_currentSubdet; };
+  inline bool IsSubdetChanged(const uint8_t newSubdet) {
+    return newSubdet != m_currentSubdet;
+  };
+  inline void SetCurrentSubdet(const uint8_t newSubdet) {
+    m_currentSubdet = newSubdet;
+  };
+  inline int GetStepNumDeltaAndSet(const int newStepNum) {
+    const int delta = newStepNum - m_currentStepNum;
+    m_currentStepNum = newStepNum;
+    return delta;
+  };
+
+  inline void UpdateTrackInfo(const NanoClock::time_point &stop,
+                              const int currentStep) {
+    m_trackTimePerSubdet[m_currentSubdet] +=
+        NanoClock::duration(stop - m_trackSubdetChangeTime).count();
+    m_trackStepPerSubdet[m_currentSubdet] += GetStepNumDeltaAndSet(currentStep);
+  }
+
+  inline NanoClock::time_point GetTrackStartTime() const {
+    return m_trackStartTime;
+  }
+  inline NanoClock::time_point GetTrackSubdetChangeTime() const {
+    return m_trackSubdetChangeTime;
+  }
+  inline const std::array<unsigned long long, MAX_SUBDET_NUM> &
+  GetTrackTimePerSubdet() const {
+    return m_trackTimePerSubdet;
+  }
+  inline const std::array<unsigned long long, MAX_SUBDET_NUM> &
+  GetTrackStepPerSubdet() const {
+    return m_trackStepPerSubdet;
+  }
+
+  inline void SetTrackStartTime(const NanoClock::time_point &time) {
+    m_trackStartTime = time;
+  }
+  inline void SetTrackSubdetChangeTime(const NanoClock::time_point &time) {
+    m_trackSubdetChangeTime = time;
+  }
 
 private:
-    int m_idxEdm4hep = -1;
+  int m_idxEdm4hep = -1;
+  int m_chronoTrackIdx = -1;
+  uint8_t m_currentSubdet = 0;
+  int m_currentStepNum = 0;
+  NanoClock::time_point m_trackStartTime, m_trackSubdetChangeTime;
+  std::array<unsigned long long, MAX_SUBDET_NUM> m_trackTimePerSubdet{},
+      m_trackStepPerSubdet{};
 };
 
 #endif
-- 
GitLab


From fcfe9b7c70aa9f86862750c60c58e1e8e9603d18 Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Mon, 9 Jun 2025 11:25:59 +0800
Subject: [PATCH 55/74] fix: SetCurrentSubdet in post tracking

---
 Simulation/DetSimAna/src/ChronoAnaElemTool.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
index 7c193eb5..2bf422e0 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -263,6 +263,7 @@ void ChronoAnaElemTool::PostUserTrackingAction(const G4Track *track) {
   m_trackEndSubdet[chronoTrackIdx] = track_end_subdet;
   // add remained time
   info->UpdateTrackInfo(trackStop, currentStep);
+  info->SetCurrentSubdet(track_end_subdet);
   const auto trackStepPerSubdet = info->GetTrackStepPerSubdet();
   const auto trackTimePerSubdet = info->GetTrackTimePerSubdet();
   for (size_t i = 0; i < MAX_SUBDET_NUM; i++) {
-- 
GitLab


From 22fb9c15d40441c93c9969d7247a75c1c578339b Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Mon, 9 Jun 2025 12:11:45 +0800
Subject: [PATCH 56/74] feat: record track sequence

---
 .../DetSimAna/src/ChronoAnaElemTool.cpp       |  22 ++--
 Simulation/DetSimAna/src/ChronoAnaElemTool.h  |   2 +-
 .../DetSimInterface/CommonUserTrackInfo.hh    |  74 +++++-------
 .../src/CommonUserTrackInfo.cc                | 105 ++++++++++++++++--
 4 files changed, 142 insertions(+), 61 deletions(-)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
index 2bf422e0..67ded825 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -140,10 +140,10 @@ StatusCode ChronoAnaElemTool::initNtuple() {
                  ->addIndexedItem("TrackStepPerSubdet", m_trackIdx,
                                   MAX_SUBDET_NUM, m_trackStepPerSubdet)
                  .isSuccess();
-      suc &= m_ntuple
-                 ->addIndexedItem("TrackNumSubdetSequence", m_trackIdx,
-                                  m_trackNumSubdetSequence)
-                 .isSuccess();
+      suc &=
+          m_ntuple
+              ->addIndexedItem("TrackNumSubdet", m_trackIdx, m_trackNumSubdet)
+              .isSuccess();
       suc &=
           m_ntuple
               ->addIndexedItem("TrackSubdetSequence", m_trackIdx,
@@ -230,13 +230,12 @@ void ChronoAnaElemTool::PreUserTrackingAction(const G4Track *track) {
     m_trackPDGId[chronoTrackIdx] =
         track->GetParticleDefinition()->GetParticleDefinitionID();
     m_trackStartSubdet[chronoTrackIdx] = currentSubdet;
+    info->SetStepNum(track->GetCurrentStepNumber());
+    info->SetCurrentSubdet(currentSubdet);
   } else {
     // resumed track
     error() << "resumed track!!! TrackIdx=" << chronoTrackIdx << endmsg;
   }
-
-  info->GetStepNumDeltaAndSet(track->GetCurrentStepNumber());
-  info->SetCurrentSubdet(currentSubdet);
   const auto start = NanoClock::now();
   info->SetTrackStartTime(start);
   info->SetTrackSubdetChangeTime(start);
@@ -263,13 +262,17 @@ void ChronoAnaElemTool::PostUserTrackingAction(const G4Track *track) {
   m_trackEndSubdet[chronoTrackIdx] = track_end_subdet;
   // add remained time
   info->UpdateTrackInfo(trackStop, currentStep);
-  info->SetCurrentSubdet(track_end_subdet);
   const auto trackStepPerSubdet = info->GetTrackStepPerSubdet();
   const auto trackTimePerSubdet = info->GetTrackTimePerSubdet();
   for (size_t i = 0; i < MAX_SUBDET_NUM; i++) {
     m_trackTimePerSubdet[chronoTrackIdx][i] = trackTimePerSubdet[i];
     m_trackStepPerSubdet[chronoTrackIdx][i] = trackStepPerSubdet[i];
   }
+  const auto trackSubdetSequence = info->GetTrackSubdetSequence();
+  m_trackNumSubdet[chronoTrackIdx] = info->GetNumSubdet();
+  for (size_t i = 0; i < MAX_SUBDET_SEQUENCE_NUM; i++) {
+    m_trackSubdetSequence[chronoTrackIdx][i] = trackSubdetSequence[i];
+  }
 }
 
 void ChronoAnaElemTool::UserSteppingAction(const G4Step *step) {
@@ -285,9 +288,10 @@ void ChronoAnaElemTool::UserSteppingAction(const G4Step *step) {
     auto newSubdet = getSubdetNameIdx(track->GetNextTouchable());
     if (info->IsSubdetChanged(newSubdet)) {
       auto stop = NanoClock::now();
+      info->SetCurrentSubdet(newSubdet);
       auto currentStep = track->GetCurrentStepNumber();
       info->UpdateTrackInfo(stop, currentStep);
-      info->SetCurrentSubdet(newSubdet);
+      info->SetStepNum(currentStep);
       info->SetTrackSubdetChangeTime(stop);
     }
   }
diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.h b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
index 463edabb..90890a37 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.h
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
@@ -81,7 +81,7 @@ private:
   NTuple::Matrix<time_t> m_trackTimePerSubdet;
   NTuple::Matrix<int> m_trackStepPerSubdet;
   static constexpr uint MAX_SUBDET_SEQUENCE_NUM = 10;
-  NTuple::Array<int> m_trackNumSubdetSequence;
+  NTuple::Array<int> m_trackNumSubdet;
   NTuple::Matrix<uint64_t>
       m_trackSubdetSequence; // 5bit for one subdet, uint64 for 12 subdets
 };
diff --git a/Simulation/DetSimInterface/include/DetSimInterface/CommonUserTrackInfo.hh b/Simulation/DetSimInterface/include/DetSimInterface/CommonUserTrackInfo.hh
index 7ae3590d..33e80187 100644
--- a/Simulation/DetSimInterface/include/DetSimInterface/CommonUserTrackInfo.hh
+++ b/Simulation/DetSimInterface/include/DetSimInterface/CommonUserTrackInfo.hh
@@ -22,6 +22,7 @@ class CommonUserTrackInfo : public G4VUserTrackInformation {
 public:
   using NanoClock = Gaudi::Timers::RdtscClock<std::chrono::nanoseconds>;
   static constexpr uint MAX_SUBDET_NUM = 26;
+  static constexpr uint MAX_SUBDET_SEQUENCE_NUM = 10;
   CommonUserTrackInfo();
   ~CommonUserTrackInfo();
 
@@ -32,60 +33,45 @@ public:
   bool setIdxEdm4hep(int idxEdm4hep);
   int idxEdm4hep() const;
 
-  inline int GetChronoTrackIdx() { return m_chronoTrackIdx; };
-  inline void SetChronoTrackIdx(const int chronoTrackIdx) {
-    m_chronoTrackIdx = chronoTrackIdx;
-  };
-  inline uint8_t GetCurrentSubdet() const { return m_currentSubdet; };
-  inline bool IsSubdetChanged(const uint8_t newSubdet) {
-    return newSubdet != m_currentSubdet;
-  };
-  inline void SetCurrentSubdet(const uint8_t newSubdet) {
-    m_currentSubdet = newSubdet;
-  };
-  inline int GetStepNumDeltaAndSet(const int newStepNum) {
-    const int delta = newStepNum - m_currentStepNum;
-    m_currentStepNum = newStepNum;
-    return delta;
-  };
+  int GetChronoTrackIdx();
+  void SetChronoTrackIdx(const int chronoTrackIdx);
 
-  inline void UpdateTrackInfo(const NanoClock::time_point &stop,
-                              const int currentStep) {
-    m_trackTimePerSubdet[m_currentSubdet] +=
-        NanoClock::duration(stop - m_trackSubdetChangeTime).count();
-    m_trackStepPerSubdet[m_currentSubdet] += GetStepNumDeltaAndSet(currentStep);
-  }
+  // Subdetector related
+  uint8_t GetCurrentSubdet() const;
+  bool IsSubdetChanged(const uint8_t newSubdet);
+  void SetCurrentSubdet(const uint8_t newSubdet);
 
-  inline NanoClock::time_point GetTrackStartTime() const {
-    return m_trackStartTime;
-  }
-  inline NanoClock::time_point GetTrackSubdetChangeTime() const {
-    return m_trackSubdetChangeTime;
-  }
-  inline const std::array<unsigned long long, MAX_SUBDET_NUM> &
-  GetTrackTimePerSubdet() const {
-    return m_trackTimePerSubdet;
-  }
-  inline const std::array<unsigned long long, MAX_SUBDET_NUM> &
-  GetTrackStepPerSubdet() const {
-    return m_trackStepPerSubdet;
-  }
+  // Step number related
+  void SetStepNum(const int newStepNum);
 
-  inline void SetTrackStartTime(const NanoClock::time_point &time) {
-    m_trackStartTime = time;
-  }
-  inline void SetTrackSubdetChangeTime(const NanoClock::time_point &time) {
-    m_trackSubdetChangeTime = time;
-  }
+  // Track timing and step information
+  void UpdateTrackInfo(const NanoClock::time_point &stop,
+                       const int currentStep);
+  NanoClock::time_point GetTrackStartTime() const;
+  NanoClock::time_point GetTrackSubdetChangeTime() const;
+  const std::array<unsigned long long, MAX_SUBDET_NUM> &
+  GetTrackTimePerSubdet() const;
+  const std::array<int, MAX_SUBDET_NUM> &GetTrackStepPerSubdet() const;
+  void SetTrackStartTime(const NanoClock::time_point &time);
+  void SetTrackSubdetChangeTime(const NanoClock::time_point &time);
+
+  int GetNumSubdet() const;
+  void AddSubdet(const uint8_t subdet);
+  const std::array<unsigned long long, MAX_SUBDET_SEQUENCE_NUM> &
+  GetTrackSubdetSequence() const;
 
 private:
   int m_idxEdm4hep = -1;
   int m_chronoTrackIdx = -1;
   uint8_t m_currentSubdet = 0;
   int m_currentStepNum = 0;
+  int m_currentSubdetNum = 0;
+
   NanoClock::time_point m_trackStartTime, m_trackSubdetChangeTime;
-  std::array<unsigned long long, MAX_SUBDET_NUM> m_trackTimePerSubdet{},
-      m_trackStepPerSubdet{};
+  std::array<unsigned long long, MAX_SUBDET_NUM> m_trackTimePerSubdet{};
+  std::array<int, MAX_SUBDET_NUM> m_trackStepPerSubdet{};
+  std::array<unsigned long long, MAX_SUBDET_SEQUENCE_NUM>
+      m_trackSubdetSequence{};
 };
 
 #endif
diff --git a/Simulation/DetSimInterface/src/CommonUserTrackInfo.cc b/Simulation/DetSimInterface/src/CommonUserTrackInfo.cc
index 9a923a35..f211f19a 100644
--- a/Simulation/DetSimInterface/src/CommonUserTrackInfo.cc
+++ b/Simulation/DetSimInterface/src/CommonUserTrackInfo.cc
@@ -1,22 +1,113 @@
 #include "DetSimInterface/CommonUserTrackInfo.hh"
 #include <iostream>
+#include <ostream>
 
-CommonUserTrackInfo::CommonUserTrackInfo() {
+CommonUserTrackInfo::CommonUserTrackInfo() {}
 
+CommonUserTrackInfo::~CommonUserTrackInfo() {}
+
+inline void CommonUserTrackInfo::Print() const {}
+
+inline bool CommonUserTrackInfo::setIdxEdm4hep(int idxEdm4hep) {
+  m_idxEdm4hep = idxEdm4hep;
 }
 
-CommonUserTrackInfo::~CommonUserTrackInfo() {
+inline int CommonUserTrackInfo::idxEdm4hep() const { return m_idxEdm4hep; }
+
+inline int CommonUserTrackInfo::GetChronoTrackIdx() {
+  return m_chronoTrackIdx;
+};
+
+inline void CommonUserTrackInfo::SetChronoTrackIdx(const int chronoTrackIdx) {
+  m_chronoTrackIdx = chronoTrackIdx;
+};
+
+inline uint8_t CommonUserTrackInfo::GetCurrentSubdet() const {
+  return m_currentSubdet;
+};
+
+inline bool CommonUserTrackInfo::IsSubdetChanged(const uint8_t newSubdet) {
+  return newSubdet != m_currentSubdet;
+};
 
+inline void CommonUserTrackInfo::SetCurrentSubdet(const uint8_t newSubdet) {
+  m_currentSubdet = newSubdet;
+  AddSubdet(newSubdet);
+};
+
+inline void CommonUserTrackInfo::SetStepNum(const int newStepNum) {
+  m_currentStepNum = newStepNum;
+};
+
+inline void
+CommonUserTrackInfo::UpdateTrackInfo(const NanoClock::time_point &stop,
+                                     const int currentStep) {
+  m_trackTimePerSubdet[m_currentSubdet] +=
+      NanoClock::duration(stop - m_trackSubdetChangeTime).count();
+  m_trackStepPerSubdet[m_currentSubdet] += currentStep - m_currentStepNum;
 }
 
-void CommonUserTrackInfo::Print() const {
+inline CommonUserTrackInfo::NanoClock::time_point
+CommonUserTrackInfo::GetTrackStartTime() const {
+  return m_trackStartTime;
+}
+
+inline CommonUserTrackInfo::NanoClock::time_point
+CommonUserTrackInfo::GetTrackSubdetChangeTime() const {
+  return m_trackSubdetChangeTime;
+}
 
+inline const std::array<unsigned long long,
+                        CommonUserTrackInfo::MAX_SUBDET_NUM> &
+CommonUserTrackInfo::GetTrackTimePerSubdet() const {
+  return m_trackTimePerSubdet;
 }
 
-bool CommonUserTrackInfo::setIdxEdm4hep(int idxEdm4hep) {
-    m_idxEdm4hep = idxEdm4hep;
+inline const std::array<int, CommonUserTrackInfo::MAX_SUBDET_NUM> &
+CommonUserTrackInfo::GetTrackStepPerSubdet() const {
+  return m_trackStepPerSubdet;
 }
 
-int CommonUserTrackInfo::idxEdm4hep() const {
-    return m_idxEdm4hep;
+inline void
+CommonUserTrackInfo::SetTrackStartTime(const NanoClock::time_point &time) {
+  m_trackStartTime = time;
 }
+
+inline void CommonUserTrackInfo::SetTrackSubdetChangeTime(
+    const NanoClock::time_point &time) {
+  m_trackSubdetChangeTime = time;
+}
+
+inline const std::array<unsigned long long,
+                        CommonUserTrackInfo::MAX_SUBDET_SEQUENCE_NUM> &
+CommonUserTrackInfo::GetTrackSubdetSequence() const {
+  return m_trackSubdetSequence;
+}
+
+inline int CommonUserTrackInfo::GetNumSubdet() const {
+  return m_currentSubdetNum;
+};
+
+inline void CommonUserTrackInfo::AddSubdet(const uint8_t subdet) {
+  static constexpr int kSlotsPerULL = 64 / 5;
+
+  if (m_currentSubdetNum >= MAX_SUBDET_SEQUENCE_NUM * kSlotsPerULL) {
+    std::cerr
+        << "CommonUserTrackInfo::SetNthSubdet: error! subdet num overflow!"
+        << std::endl;
+    return;
+  }
+  // 每个unsigned long long可以存储12个5bit值(64/5=12)
+  const int array_idx = m_currentSubdetNum / kSlotsPerULL;
+  // 在当前元素中的bit位置
+  const int bit_pos = (m_currentSubdetNum % kSlotsPerULL) * 5;
+
+  // 确保subdet值不超过5bit范围
+  uint8_t masked_subdet = subdet & 0x1F; // 0x1F = 0b11111
+
+  // 清除原有位置的值并写入新值
+  m_trackSubdetSequence[array_idx] &= ~(0x1FULL << bit_pos);
+  m_trackSubdetSequence[array_idx] |= (masked_subdet & 0x1FULL) << bit_pos;
+
+  m_currentSubdetNum++;
+};
\ No newline at end of file
-- 
GitLab


From dd28fe518a195b496dfb288701b59985ffdbe66a Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Mon, 9 Jun 2025 12:12:29 +0800
Subject: [PATCH 57/74] chore: comment

---
 Simulation/DetSimAna/src/ChronoAnaElemTool.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.h b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
index 90890a37..ed8f4537 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.h
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
@@ -82,8 +82,8 @@ private:
   NTuple::Matrix<int> m_trackStepPerSubdet;
   static constexpr uint MAX_SUBDET_SEQUENCE_NUM = 10;
   NTuple::Array<int> m_trackNumSubdet;
-  NTuple::Matrix<uint64_t>
-      m_trackSubdetSequence; // 5bit for one subdet, uint64 for 12 subdets
+  // 5bit for one subdet, uint64 for 12 subdets
+  NTuple::Matrix<uint64_t> m_trackSubdetSequence;
 };
 
 #endif
-- 
GitLab


From f423eb7feb0aadcc050ee381e7eba356badf1e19 Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Mon, 9 Jun 2025 12:16:32 +0800
Subject: [PATCH 58/74] fix: always enable magnetic field even disable SD

---
 Simulation/DetSimGeom/src/DDG4DetElemTool.cpp | 62 +++++++++----------
 1 file changed, 31 insertions(+), 31 deletions(-)

diff --git a/Simulation/DetSimGeom/src/DDG4DetElemTool.cpp b/Simulation/DetSimGeom/src/DDG4DetElemTool.cpp
index 63f33a25..ac308888 100644
--- a/Simulation/DetSimGeom/src/DDG4DetElemTool.cpp
+++ b/Simulation/DetSimGeom/src/DDG4DetElemTool.cpp
@@ -77,6 +77,37 @@ DDG4DetElemTool::getLV() {
 void
 DDG4DetElemTool::ConstructSDandField() {
 
+     auto lcdd = m_geosvc->lcdd();
+     
+    // =======================================================================
+    // Construct Field
+    // =======================================================================
+    // TODO: integrate the field between DD4hep and Geant4
+    // Note:
+    //   DD4hep provides the parameters of fields
+    //   Geant4 will setup the field based on the DD4hep fields
+    
+    // Related Examples:
+    // - G4: G4GlobalMagFieldMessenger.cc
+
+    G4FieldManager* fieldManager
+        = G4TransportationManager::GetTransportationManager()->GetFieldManager();
+
+    // // Below is a uniform B-field
+    // G4ThreeVector value(0,0,3.*tesla);
+    // G4UniformMagField* mag_field = new G4UniformMagField(value);
+
+    // DDG4 based B-field
+    dd4hep::OverlayedField fld  = lcdd->field();
+    G4MagneticField* mag_field  = new dd4hep::sim::Geant4Field(fld);
+
+    fieldManager->SetDetectorField(mag_field);
+    fieldManager->CreateChordFinder(mag_field);
+    fieldManager->SetDeltaIntersection(m_DeltaIntersection.value());
+    fieldManager->SetDeltaOneStep(m_DeltaOneStep.value());
+    fieldManager->SetMinimumEpsilonStep(m_MinimumEpsilonStep.value());
+    fieldManager->SetMaximumEpsilonStep(m_MaximumEpsilonStep.value());
+
     // DEBUG ONLY: turn off all the SD.
     if (not m_SD_enabled) {
         warning() << "All the Sensitive Detectors will be disabled by default. " << endmsg;
@@ -94,8 +125,6 @@ DDG4DetElemTool::ConstructSDandField() {
     dd4hep::sim::Geant4GeometryInfo* p = dd4hep::sim::Geant4Mapping::instance().ptr();
     _SV& vols = p->sensitives;
 
-    auto lcdd = m_geosvc->lcdd();
-
     for (_SV::const_iterator iv = vols.begin(); iv != vols.end(); ++iv) {
         dd4hep::SensitiveDetector sd = (*iv).first;
         std::string typ = sd.type(), nam = sd.name();
@@ -200,35 +229,6 @@ DDG4DetElemTool::ConstructSDandField() {
                << " senstive volumes are registered. " << endmsg;
     }
 
-    // =======================================================================
-    // Construct Field
-    // =======================================================================
-    // TODO: integrate the field between DD4hep and Geant4
-    // Note:
-    //   DD4hep provides the parameters of fields
-    //   Geant4 will setup the field based on the DD4hep fields
-    
-    // Related Examples:
-    // - G4: G4GlobalMagFieldMessenger.cc
-
-    G4FieldManager* fieldManager
-        = G4TransportationManager::GetTransportationManager()->GetFieldManager();
-
-    // // Below is a uniform B-field
-    // G4ThreeVector value(0,0,3.*tesla);
-    // G4UniformMagField* mag_field = new G4UniformMagField(value);
-
-    // DDG4 based B-field
-    dd4hep::OverlayedField fld  = lcdd->field();
-    G4MagneticField* mag_field  = new dd4hep::sim::Geant4Field(fld);
-
-    fieldManager->SetDetectorField(mag_field);
-    fieldManager->CreateChordFinder(mag_field);
-    fieldManager->SetDeltaIntersection(m_DeltaIntersection.value());
-    fieldManager->SetDeltaOneStep(m_DeltaOneStep.value());
-    fieldManager->SetMinimumEpsilonStep(m_MinimumEpsilonStep.value());
-    fieldManager->SetMaximumEpsilonStep(m_MaximumEpsilonStep.value());
-
 }
 
 StatusCode
-- 
GitLab


From 630f1df35e22333994828d7737c17cff11a49b8c Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Mon, 9 Jun 2025 12:54:26 +0800
Subject: [PATCH 59/74] fix: put inline func into header file

---
 .../DetSimInterface/CommonUserTrackInfo.hh    | 62 ++++++++++---
 .../src/CommonUserTrackInfo.cc                | 86 +++----------------
 2 files changed, 65 insertions(+), 83 deletions(-)

diff --git a/Simulation/DetSimInterface/include/DetSimInterface/CommonUserTrackInfo.hh b/Simulation/DetSimInterface/include/DetSimInterface/CommonUserTrackInfo.hh
index 33e80187..d61e14c9 100644
--- a/Simulation/DetSimInterface/include/DetSimInterface/CommonUserTrackInfo.hh
+++ b/Simulation/DetSimInterface/include/DetSimInterface/CommonUserTrackInfo.hh
@@ -17,6 +17,7 @@
 #include "Gaudi/Timers/RdtscClock.h"
 #include <array>
 #include <cstdint>
+#include <sys/types.h>
 
 class CommonUserTrackInfo : public G4VUserTrackInformation {
 public:
@@ -38,25 +39,15 @@ public:
 
   // Subdetector related
   uint8_t GetCurrentSubdet() const;
-  bool IsSubdetChanged(const uint8_t newSubdet);
-  void SetCurrentSubdet(const uint8_t newSubdet);
-
-  // Step number related
-  void SetStepNum(const int newStepNum);
 
   // Track timing and step information
-  void UpdateTrackInfo(const NanoClock::time_point &stop,
-                       const int currentStep);
   NanoClock::time_point GetTrackStartTime() const;
   NanoClock::time_point GetTrackSubdetChangeTime() const;
   const std::array<unsigned long long, MAX_SUBDET_NUM> &
   GetTrackTimePerSubdet() const;
   const std::array<int, MAX_SUBDET_NUM> &GetTrackStepPerSubdet() const;
   void SetTrackStartTime(const NanoClock::time_point &time);
-  void SetTrackSubdetChangeTime(const NanoClock::time_point &time);
-
   int GetNumSubdet() const;
-  void AddSubdet(const uint8_t subdet);
   const std::array<unsigned long long, MAX_SUBDET_SEQUENCE_NUM> &
   GetTrackSubdetSequence() const;
 
@@ -65,13 +56,62 @@ private:
   int m_chronoTrackIdx = -1;
   uint8_t m_currentSubdet = 0;
   int m_currentStepNum = 0;
-  int m_currentSubdetNum = 0;
+  uint m_currentSubdetNum = 0;
 
   NanoClock::time_point m_trackStartTime, m_trackSubdetChangeTime;
   std::array<unsigned long long, MAX_SUBDET_NUM> m_trackTimePerSubdet{};
   std::array<int, MAX_SUBDET_NUM> m_trackStepPerSubdet{};
   std::array<unsigned long long, MAX_SUBDET_SEQUENCE_NUM>
       m_trackSubdetSequence{};
+
+public:
+  inline bool IsSubdetChanged(const uint8_t newSubdet) {
+    return newSubdet != m_currentSubdet;
+  };
+
+  inline void SetCurrentSubdet(const uint8_t newSubdet) {
+    m_currentSubdet = newSubdet;
+    AddSubdet(newSubdet);
+  };
+
+  void SetTrackSubdetChangeTime(const NanoClock::time_point &time) {
+    m_trackSubdetChangeTime = time;
+  };
+
+  inline void AddSubdet(const uint8_t subdet) {
+    static constexpr int kSlotsPerULL = 64 / 5;
+
+    if (m_currentSubdetNum >= MAX_SUBDET_SEQUENCE_NUM * kSlotsPerULL) {
+      std::cerr
+          << "CommonUserTrackInfo::SetNthSubdet: error! subdet num overflow!"
+          << std::endl;
+      return;
+    }
+    // 每个unsigned long long可以存储12个5bit值(64/5=12)
+    const int array_idx = m_currentSubdetNum / kSlotsPerULL;
+    // 在当前元素中的bit位置
+    const int bit_pos = (m_currentSubdetNum % kSlotsPerULL) * 5;
+
+    // 确保subdet值不超过5bit范围
+    uint8_t masked_subdet = subdet & 0x1F; // 0x1F = 0b11111
+
+    // 清除原有位置的值并写入新值
+    m_trackSubdetSequence[array_idx] &= ~(0x1FULL << bit_pos);
+    m_trackSubdetSequence[array_idx] |= (masked_subdet & 0x1FULL) << bit_pos;
+
+    m_currentSubdetNum++;
+  };
+
+  inline void UpdateTrackInfo(const NanoClock::time_point &stop,
+                              const int currentStep) {
+    m_trackTimePerSubdet[m_currentSubdet] +=
+        NanoClock::duration(stop - m_trackSubdetChangeTime).count();
+    m_trackStepPerSubdet[m_currentSubdet] += currentStep - m_currentStepNum;
+  };
+
+  inline void SetStepNum(const int newStepNum) {
+    m_currentStepNum = newStepNum;
+  };
 };
 
 #endif
diff --git a/Simulation/DetSimInterface/src/CommonUserTrackInfo.cc b/Simulation/DetSimInterface/src/CommonUserTrackInfo.cc
index f211f19a..d83f53d5 100644
--- a/Simulation/DetSimInterface/src/CommonUserTrackInfo.cc
+++ b/Simulation/DetSimInterface/src/CommonUserTrackInfo.cc
@@ -1,113 +1,55 @@
 #include "DetSimInterface/CommonUserTrackInfo.hh"
-#include <iostream>
-#include <ostream>
 
 CommonUserTrackInfo::CommonUserTrackInfo() {}
 
 CommonUserTrackInfo::~CommonUserTrackInfo() {}
 
-inline void CommonUserTrackInfo::Print() const {}
+void CommonUserTrackInfo::Print() const {}
 
-inline bool CommonUserTrackInfo::setIdxEdm4hep(int idxEdm4hep) {
+bool CommonUserTrackInfo::setIdxEdm4hep(int idxEdm4hep) {
   m_idxEdm4hep = idxEdm4hep;
 }
 
-inline int CommonUserTrackInfo::idxEdm4hep() const { return m_idxEdm4hep; }
+int CommonUserTrackInfo::idxEdm4hep() const { return m_idxEdm4hep; }
 
-inline int CommonUserTrackInfo::GetChronoTrackIdx() {
-  return m_chronoTrackIdx;
-};
+int CommonUserTrackInfo::GetChronoTrackIdx() { return m_chronoTrackIdx; };
 
-inline void CommonUserTrackInfo::SetChronoTrackIdx(const int chronoTrackIdx) {
+void CommonUserTrackInfo::SetChronoTrackIdx(const int chronoTrackIdx) {
   m_chronoTrackIdx = chronoTrackIdx;
 };
 
-inline uint8_t CommonUserTrackInfo::GetCurrentSubdet() const {
+uint8_t CommonUserTrackInfo::GetCurrentSubdet() const {
   return m_currentSubdet;
 };
 
-inline bool CommonUserTrackInfo::IsSubdetChanged(const uint8_t newSubdet) {
-  return newSubdet != m_currentSubdet;
-};
-
-inline void CommonUserTrackInfo::SetCurrentSubdet(const uint8_t newSubdet) {
-  m_currentSubdet = newSubdet;
-  AddSubdet(newSubdet);
-};
-
-inline void CommonUserTrackInfo::SetStepNum(const int newStepNum) {
-  m_currentStepNum = newStepNum;
-};
-
-inline void
-CommonUserTrackInfo::UpdateTrackInfo(const NanoClock::time_point &stop,
-                                     const int currentStep) {
-  m_trackTimePerSubdet[m_currentSubdet] +=
-      NanoClock::duration(stop - m_trackSubdetChangeTime).count();
-  m_trackStepPerSubdet[m_currentSubdet] += currentStep - m_currentStepNum;
-}
-
-inline CommonUserTrackInfo::NanoClock::time_point
+CommonUserTrackInfo::NanoClock::time_point
 CommonUserTrackInfo::GetTrackStartTime() const {
   return m_trackStartTime;
 }
 
-inline CommonUserTrackInfo::NanoClock::time_point
+CommonUserTrackInfo::NanoClock::time_point
 CommonUserTrackInfo::GetTrackSubdetChangeTime() const {
   return m_trackSubdetChangeTime;
 }
 
-inline const std::array<unsigned long long,
-                        CommonUserTrackInfo::MAX_SUBDET_NUM> &
+const std::array<unsigned long long, CommonUserTrackInfo::MAX_SUBDET_NUM> &
 CommonUserTrackInfo::GetTrackTimePerSubdet() const {
   return m_trackTimePerSubdet;
 }
 
-inline const std::array<int, CommonUserTrackInfo::MAX_SUBDET_NUM> &
+const std::array<int, CommonUserTrackInfo::MAX_SUBDET_NUM> &
 CommonUserTrackInfo::GetTrackStepPerSubdet() const {
   return m_trackStepPerSubdet;
 }
 
-inline void
-CommonUserTrackInfo::SetTrackStartTime(const NanoClock::time_point &time) {
+void CommonUserTrackInfo::SetTrackStartTime(const NanoClock::time_point &time) {
   m_trackStartTime = time;
 }
 
-inline void CommonUserTrackInfo::SetTrackSubdetChangeTime(
-    const NanoClock::time_point &time) {
-  m_trackSubdetChangeTime = time;
-}
-
-inline const std::array<unsigned long long,
-                        CommonUserTrackInfo::MAX_SUBDET_SEQUENCE_NUM> &
+const std::array<unsigned long long,
+                 CommonUserTrackInfo::MAX_SUBDET_SEQUENCE_NUM> &
 CommonUserTrackInfo::GetTrackSubdetSequence() const {
   return m_trackSubdetSequence;
 }
 
-inline int CommonUserTrackInfo::GetNumSubdet() const {
-  return m_currentSubdetNum;
-};
-
-inline void CommonUserTrackInfo::AddSubdet(const uint8_t subdet) {
-  static constexpr int kSlotsPerULL = 64 / 5;
-
-  if (m_currentSubdetNum >= MAX_SUBDET_SEQUENCE_NUM * kSlotsPerULL) {
-    std::cerr
-        << "CommonUserTrackInfo::SetNthSubdet: error! subdet num overflow!"
-        << std::endl;
-    return;
-  }
-  // 每个unsigned long long可以存储12个5bit值(64/5=12)
-  const int array_idx = m_currentSubdetNum / kSlotsPerULL;
-  // 在当前元素中的bit位置
-  const int bit_pos = (m_currentSubdetNum % kSlotsPerULL) * 5;
-
-  // 确保subdet值不超过5bit范围
-  uint8_t masked_subdet = subdet & 0x1F; // 0x1F = 0b11111
-
-  // 清除原有位置的值并写入新值
-  m_trackSubdetSequence[array_idx] &= ~(0x1FULL << bit_pos);
-  m_trackSubdetSequence[array_idx] |= (masked_subdet & 0x1FULL) << bit_pos;
-
-  m_currentSubdetNum++;
-};
\ No newline at end of file
+int CommonUserTrackInfo::GetNumSubdet() const { return m_currentSubdetNum; };
-- 
GitLab


From 74d154ef538254cd58fa2e90202905bd28623d71 Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Mon, 9 Jun 2025 13:01:40 +0800
Subject: [PATCH 60/74] fix: chronoTrackIdx start from -1

---
 Simulation/DetSimAna/src/ChronoAnaElemTool.cpp              | 3 ++-
 .../include/DetSimInterface/CommonUserTrackInfo.hh          | 6 +++---
 2 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
index 67ded825..4caeb644 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -216,7 +216,8 @@ void ChronoAnaElemTool::PreUserTrackingAction(const G4Track *track) {
   int chronoTrackIdx = info->GetChronoTrackIdx();
   if (chronoTrackIdx < 0) {
     // new track
-    chronoTrackIdx = m_trackIdx++; // first copy then self-add
+    // first self-add then copy, for m_trackIdx start from -1
+    chronoTrackIdx = ++m_trackIdx;
     info->SetChronoTrackIdx(chronoTrackIdx);
     const auto &mom = track->GetMomentum();
     const auto &pos = track->GetPosition();
diff --git a/Simulation/DetSimInterface/include/DetSimInterface/CommonUserTrackInfo.hh b/Simulation/DetSimInterface/include/DetSimInterface/CommonUserTrackInfo.hh
index d61e14c9..b6d63767 100644
--- a/Simulation/DetSimInterface/include/DetSimInterface/CommonUserTrackInfo.hh
+++ b/Simulation/DetSimInterface/include/DetSimInterface/CommonUserTrackInfo.hh
@@ -79,7 +79,7 @@ public:
   };
 
   inline void AddSubdet(const uint8_t subdet) {
-    static constexpr int kSlotsPerULL = 64 / 5;
+    static constexpr uint kSlotsPerULL = 64 / 5;
 
     if (m_currentSubdetNum >= MAX_SUBDET_SEQUENCE_NUM * kSlotsPerULL) {
       std::cerr
@@ -88,9 +88,9 @@ public:
       return;
     }
     // 每个unsigned long long可以存储12个5bit值(64/5=12)
-    const int array_idx = m_currentSubdetNum / kSlotsPerULL;
+    const uint array_idx = m_currentSubdetNum / kSlotsPerULL;
     // 在当前元素中的bit位置
-    const int bit_pos = (m_currentSubdetNum % kSlotsPerULL) * 5;
+    const uint bit_pos = (m_currentSubdetNum % kSlotsPerULL) * 5;
 
     // 确保subdet值不超过5bit范围
     uint8_t masked_subdet = subdet & 0x1F; // 0x1F = 0b11111
-- 
GitLab


From 596b6c28d1c29d4f8cf3ecfb708cce1cc3e2e258 Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Mon, 9 Jun 2025 14:44:02 +0800
Subject: [PATCH 61/74] fix(Edm4hepWriter): add idxEdm4hep initialization check
 in track handling

---
 Simulation/DetSimAna/src/Edm4hepWriterAnaElemTool.cpp    | 6 +++---
 .../include/DetSimInterface/CommonUserTrackInfo.hh       | 9 +++++++--
 Simulation/DetSimInterface/src/CommonUserTrackInfo.cc    | 6 ------
 3 files changed, 10 insertions(+), 11 deletions(-)

diff --git a/Simulation/DetSimAna/src/Edm4hepWriterAnaElemTool.cpp b/Simulation/DetSimAna/src/Edm4hepWriterAnaElemTool.cpp
index 3139e2ef..19046c54 100644
--- a/Simulation/DetSimAna/src/Edm4hepWriterAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/Edm4hepWriterAnaElemTool.cpp
@@ -368,7 +368,7 @@ Edm4hepWriterAnaElemTool::PreUserTrackingAction(const G4Track* track) {
     // If it is associated, then we will record this info.
     if (not m_istrk2primary.value()) {
         auto trackinfo = dynamic_cast<CommonUserTrackInfo*>(track->GetUserInformation());
-        if (trackinfo) {
+        if (trackinfo && trackinfo->IsIdxEdm4hepInited()) {
             curparid = 0; // force to zero, so that it will be set as primary
         }
     }
@@ -437,7 +437,7 @@ Edm4hepWriterAnaElemTool::PostUserTrackingAction(const G4Track* track) {
 
     if (idxedm4hep == -1) {
         auto trackinfo = dynamic_cast<CommonUserTrackInfo*>(track->GetUserInformation());
-        if (trackinfo) {
+        if (trackinfo && trackinfo->IsIdxEdm4hepInited()) {
             idxedm4hep = trackinfo->idxEdm4hep();
             // if the current track is a secondary track and it is associated
             // with a MCParticle, we need to update the event level user info
@@ -591,7 +591,7 @@ Edm4hepWriterAnaElemTool::UserSteppingAction(const G4Step* aStep) {
     // Note:
     // if there is no track info, then do nothing. 
     // ========================================================================
-    if (trackinfo) {
+    if (trackinfo && trackinfo->IsIdxEdm4hepInited()) {
         // back scattering is defined as following:
         // - pre point is not in tracker
         // - post point is in tracker
diff --git a/Simulation/DetSimInterface/include/DetSimInterface/CommonUserTrackInfo.hh b/Simulation/DetSimInterface/include/DetSimInterface/CommonUserTrackInfo.hh
index b6d63767..49b4e5d9 100644
--- a/Simulation/DetSimInterface/include/DetSimInterface/CommonUserTrackInfo.hh
+++ b/Simulation/DetSimInterface/include/DetSimInterface/CommonUserTrackInfo.hh
@@ -31,8 +31,12 @@ public:
   virtual void Print() const;
 
   // get the idx in the EDM4hep MC particle collection
-  bool setIdxEdm4hep(int idxEdm4hep);
-  int idxEdm4hep() const;
+  inline void setIdxEdm4hep(int idxEdm4hep) {
+    m_idxEdm4hep = idxEdm4hep;
+    m_idxEdm4hepInited = true;
+  };
+  inline int idxEdm4hep() const { return m_idxEdm4hep; };
+  inline bool IsIdxEdm4hepInited() const { return m_idxEdm4hepInited; };
 
   int GetChronoTrackIdx();
   void SetChronoTrackIdx(const int chronoTrackIdx);
@@ -53,6 +57,7 @@ public:
 
 private:
   int m_idxEdm4hep = -1;
+  bool m_idxEdm4hepInited = false;
   int m_chronoTrackIdx = -1;
   uint8_t m_currentSubdet = 0;
   int m_currentStepNum = 0;
diff --git a/Simulation/DetSimInterface/src/CommonUserTrackInfo.cc b/Simulation/DetSimInterface/src/CommonUserTrackInfo.cc
index d83f53d5..84bcddb2 100644
--- a/Simulation/DetSimInterface/src/CommonUserTrackInfo.cc
+++ b/Simulation/DetSimInterface/src/CommonUserTrackInfo.cc
@@ -6,12 +6,6 @@ CommonUserTrackInfo::~CommonUserTrackInfo() {}
 
 void CommonUserTrackInfo::Print() const {}
 
-bool CommonUserTrackInfo::setIdxEdm4hep(int idxEdm4hep) {
-  m_idxEdm4hep = idxEdm4hep;
-}
-
-int CommonUserTrackInfo::idxEdm4hep() const { return m_idxEdm4hep; }
-
 int CommonUserTrackInfo::GetChronoTrackIdx() { return m_chronoTrackIdx; };
 
 void CommonUserTrackInfo::SetChronoTrackIdx(const int chronoTrackIdx) {
-- 
GitLab


From 342097d11c5df9118b63610eeb8fb39b0ab6cad6 Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Mon, 9 Jun 2025 14:44:24 +0800
Subject: [PATCH 62/74] fix: change name not shadow

---
 .../DetSimAna/src/ChronoAnaElemTool.cpp       | 87 ++++++++++---------
 1 file changed, 46 insertions(+), 41 deletions(-)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
index 4caeb644..c501eee7 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -31,28 +31,28 @@ StatusCode ChronoAnaElemTool::initSubdetVolumes() {
                        ->GetNavigatorForTracking()
                        ->GetWorldVolume();
   auto num_dau = world_vol->GetLogicalVolume()->GetNoDaughters();
-  // debug() << "World Physical Volume - Name: " << world_vol->GetName()
-  //         << " Pointer: " << world_vol << " World Logical Volume - Name: "
-  //         << world_vol->GetLogicalVolume()->GetName()
-  //         << " Pointer: " << world_vol->GetLogicalVolume() << endmsg;
+  debug() << "World Physical Volume - Name: " << world_vol->GetName()
+          << " Pointer: " << world_vol << " World Logical Volume - Name: "
+          << world_vol->GetLogicalVolume()->GetName()
+          << " Pointer: " << world_vol->GetLogicalVolume() << endmsg;
   m_logicalVolumePtrToSubdetIdx[world_vol->GetLogicalVolume()] = PWorldIndex;
-  // debug() << "Set m_logicalVolumePtrToSubdetIdx["
-  //         << world_vol->GetLogicalVolume() << "] to subdet index "
-  //         << PWorldIndex << endmsg;
+  debug() << "Set m_logicalVolumePtrToSubdetIdx["
+          << world_vol->GetLogicalVolume() << "] to subdet index "
+          << PWorldIndex << endmsg;
   for (size_t _dau = 0; _dau < num_dau; ++_dau) {
     auto dau_vol = world_vol->GetLogicalVolume()->GetDaughter(_dau);
-    // debug() << "Daughter Physical Volume - Name: " << dau_vol->GetName()
-    //         << " Pointer: " << dau_vol << " Daughter Logical Volume - Name: "
-    //         << dau_vol->GetLogicalVolume()->GetName()
-    //         << " Pointer: " << dau_vol->GetLogicalVolume() << endmsg;
+    debug() << "Daughter Physical Volume - Name: " << dau_vol->GetName()
+            << " Pointer: " << dau_vol << " Daughter Logical Volume - Name: "
+            << dau_vol->GetLogicalVolume()->GetName()
+            << " Pointer: " << dau_vol->GetLogicalVolume() << endmsg;
     auto dau_name = dau_vol->GetName();
     for (uint8_t _subdet = PWorldIndex; _subdet < m_subdetNames.size();
          ++_subdet) {
       if (dau_name.find(m_subdetNames[_subdet]) != std::string::npos) {
         m_logicalVolumePtrToSubdetIdx[dau_vol->GetLogicalVolume()] = _subdet;
-        // debug() << "Set m_logicalVolumePtrToSubdetIdx["
-        //         << dau_vol->GetLogicalVolume() << "] to subdet index "
-        //         << _subdet << endmsg;
+        debug() << "Set m_logicalVolumePtrToSubdetIdx["
+                << dau_vol->GetLogicalVolume() << "] to subdet index "
+                << _subdet << endmsg;
         continue;
       }
     }
@@ -206,19 +206,22 @@ void ChronoAnaElemTool::PreUserTrackingAction(const G4Track *track) {
   }
 
   // ensure info exists
-  auto info = static_cast<CommonUserTrackInfo *>(track->GetUserInformation());
-  if (!info) {
-    info = new CommonUserTrackInfo();
-    track->SetUserInformation(info);
+  auto trackInfo =
+      static_cast<CommonUserTrackInfo *>(track->GetUserInformation());
+  if (!trackInfo) {
+    trackInfo = new CommonUserTrackInfo();
+    track->SetUserInformation(trackInfo);
+    info() << "initialize new user track info " << track->GetTrackID()
+           << endmsg;
   }
   auto currentSubdet = getSubdetNameIdx(track->GetTouchable());
   // 根据已有的 chronoTrackIdx 判断是新 track 还是 resumed
-  int chronoTrackIdx = info->GetChronoTrackIdx();
+  int chronoTrackIdx = trackInfo->GetChronoTrackIdx();
   if (chronoTrackIdx < 0) {
     // new track
-    // first self-add then copy, for m_trackIdx start from -1
-    chronoTrackIdx = ++m_trackIdx;
-    info->SetChronoTrackIdx(chronoTrackIdx);
+    // first self-add then copy, for m_trackIdx start from 0
+    chronoTrackIdx = m_trackIdx++;
+    trackInfo->SetChronoTrackIdx(chronoTrackIdx);
     const auto &mom = track->GetMomentum();
     const auto &pos = track->GetPosition();
     m_trackPx[chronoTrackIdx] = mom.x();
@@ -231,15 +234,15 @@ void ChronoAnaElemTool::PreUserTrackingAction(const G4Track *track) {
     m_trackPDGId[chronoTrackIdx] =
         track->GetParticleDefinition()->GetParticleDefinitionID();
     m_trackStartSubdet[chronoTrackIdx] = currentSubdet;
-    info->SetStepNum(track->GetCurrentStepNumber());
-    info->SetCurrentSubdet(currentSubdet);
+    trackInfo->SetStepNum(track->GetCurrentStepNumber());
+    trackInfo->SetCurrentSubdet(currentSubdet);
   } else {
     // resumed track
-    error() << "resumed track!!! TrackIdx=" << chronoTrackIdx << endmsg;
+    error() << "Resumed track!!! TrackIdx=" << chronoTrackIdx << endmsg;
   }
   const auto start = NanoClock::now();
-  info->SetTrackStartTime(start);
-  info->SetTrackSubdetChangeTime(start);
+  trackInfo->SetTrackStartTime(start);
+  trackInfo->SetTrackSubdetChangeTime(start);
 }
 
 void ChronoAnaElemTool::PostUserTrackingAction(const G4Track *track) {
@@ -248,29 +251,30 @@ void ChronoAnaElemTool::PostUserTrackingAction(const G4Track *track) {
   }
 
   auto trackStop = NanoClock::now();
-  auto info = static_cast<CommonUserTrackInfo *>(track->GetUserInformation());
-  auto chronoTrackIdx = info->GetChronoTrackIdx();
+  auto trackInfo =
+      static_cast<CommonUserTrackInfo *>(track->GetUserInformation());
+  auto chronoTrackIdx = trackInfo->GetChronoTrackIdx();
   auto currentStep = track->GetCurrentStepNumber();
 
   // record value
   m_trackdE[chronoTrackIdx] =
       track->GetTotalEnergy() - m_trackE[chronoTrackIdx];
   m_trackTime[chronoTrackIdx] +=
-      NanoClock::duration(trackStop - info->GetTrackStartTime()).count();
+      NanoClock::duration(trackStop - trackInfo->GetTrackStartTime()).count();
   auto track_end_subdet = getSubdetNameIdx(track->GetTouchable());
   m_trackLength[chronoTrackIdx] = track->GetTrackLength();
   m_trackStepNum[chronoTrackIdx] = currentStep;
   m_trackEndSubdet[chronoTrackIdx] = track_end_subdet;
   // add remained time
-  info->UpdateTrackInfo(trackStop, currentStep);
-  const auto trackStepPerSubdet = info->GetTrackStepPerSubdet();
-  const auto trackTimePerSubdet = info->GetTrackTimePerSubdet();
+  trackInfo->UpdateTrackInfo(trackStop, currentStep);
+  const auto trackStepPerSubdet = trackInfo->GetTrackStepPerSubdet();
+  const auto trackTimePerSubdet = trackInfo->GetTrackTimePerSubdet();
   for (size_t i = 0; i < MAX_SUBDET_NUM; i++) {
     m_trackTimePerSubdet[chronoTrackIdx][i] = trackTimePerSubdet[i];
     m_trackStepPerSubdet[chronoTrackIdx][i] = trackStepPerSubdet[i];
   }
-  const auto trackSubdetSequence = info->GetTrackSubdetSequence();
-  m_trackNumSubdet[chronoTrackIdx] = info->GetNumSubdet();
+  const auto trackSubdetSequence = trackInfo->GetTrackSubdetSequence();
+  m_trackNumSubdet[chronoTrackIdx] = trackInfo->GetNumSubdet();
   for (size_t i = 0; i < MAX_SUBDET_SEQUENCE_NUM; i++) {
     m_trackSubdetSequence[chronoTrackIdx][i] = trackSubdetSequence[i];
   }
@@ -285,15 +289,16 @@ void ChronoAnaElemTool::UserSteppingAction(const G4Step *step) {
   if (stepStatus == fGeomBoundary || stepStatus == fWorldBoundary) {
     // last step in a volume or world
     const auto &track = step->GetTrack();
-    auto info = static_cast<CommonUserTrackInfo *>(track->GetUserInformation());
+    auto trackInfo =
+        static_cast<CommonUserTrackInfo *>(track->GetUserInformation());
     auto newSubdet = getSubdetNameIdx(track->GetNextTouchable());
-    if (info->IsSubdetChanged(newSubdet)) {
+    if (trackInfo->IsSubdetChanged(newSubdet)) {
       auto stop = NanoClock::now();
-      info->SetCurrentSubdet(newSubdet);
+      trackInfo->SetCurrentSubdet(newSubdet);
       auto currentStep = track->GetCurrentStepNumber();
-      info->UpdateTrackInfo(stop, currentStep);
-      info->SetStepNum(currentStep);
-      info->SetTrackSubdetChangeTime(stop);
+      trackInfo->UpdateTrackInfo(stop, currentStep);
+      trackInfo->SetStepNum(currentStep);
+      trackInfo->SetTrackSubdetChangeTime(stop);
     }
   }
 }
-- 
GitLab


From c01d0d80c7e935aea723e53343e059bc4ae0c325 Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Mon, 9 Jun 2025 14:53:23 +0800
Subject: [PATCH 63/74] fix: remove frequent log

---
 Simulation/DetSimAna/src/ChronoAnaElemTool.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
index c501eee7..bd0ba285 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -211,8 +211,8 @@ void ChronoAnaElemTool::PreUserTrackingAction(const G4Track *track) {
   if (!trackInfo) {
     trackInfo = new CommonUserTrackInfo();
     track->SetUserInformation(trackInfo);
-    info() << "initialize new user track info " << track->GetTrackID()
-           << endmsg;
+    // debug() << "initialize new user track info " << track->GetTrackID()
+    //        << endmsg;
   }
   auto currentSubdet = getSubdetNameIdx(track->GetTouchable());
   // 根据已有的 chronoTrackIdx 判断是新 track 还是 resumed
-- 
GitLab


From 515df98b91d0edf5c4c00b45da38f8ee7a19409f Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Mon, 9 Jun 2025 15:06:16 +0800
Subject: [PATCH 64/74] fix: add doc

---
 Simulation/DetSimAna/src/ChronoAnaElemTool.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
index bd0ba285..8e04d84f 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -209,6 +209,7 @@ void ChronoAnaElemTool::PreUserTrackingAction(const G4Track *track) {
   auto trackInfo =
       static_cast<CommonUserTrackInfo *>(track->GetUserInformation());
   if (!trackInfo) {
+    // 不影响,edm4heptool设置trackinfo是secondary,secondary的pre不会比primary的post早
     trackInfo = new CommonUserTrackInfo();
     track->SetUserInformation(trackInfo);
     // debug() << "initialize new user track info " << track->GetTrackID()
-- 
GitLab


From 9c6fc18c1c7b00dd364086472eed03d85302454b Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Mon, 9 Jun 2025 15:39:12 +0800
Subject: [PATCH 65/74] fix: enlarge subdet sequence

---
 Simulation/DetSimAna/src/ChronoAnaElemTool.h                    | 2 +-
 .../include/DetSimInterface/CommonUserTrackInfo.hh              | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.h b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
index ed8f4537..52e4ec46 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.h
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
@@ -80,7 +80,7 @@ private:
   static constexpr uint MAX_SUBDET_NUM = 26;
   NTuple::Matrix<time_t> m_trackTimePerSubdet;
   NTuple::Matrix<int> m_trackStepPerSubdet;
-  static constexpr uint MAX_SUBDET_SEQUENCE_NUM = 10;
+  static constexpr uint MAX_SUBDET_SEQUENCE_NUM = 100;
   NTuple::Array<int> m_trackNumSubdet;
   // 5bit for one subdet, uint64 for 12 subdets
   NTuple::Matrix<uint64_t> m_trackSubdetSequence;
diff --git a/Simulation/DetSimInterface/include/DetSimInterface/CommonUserTrackInfo.hh b/Simulation/DetSimInterface/include/DetSimInterface/CommonUserTrackInfo.hh
index 49b4e5d9..b82d3b3f 100644
--- a/Simulation/DetSimInterface/include/DetSimInterface/CommonUserTrackInfo.hh
+++ b/Simulation/DetSimInterface/include/DetSimInterface/CommonUserTrackInfo.hh
@@ -23,7 +23,7 @@ class CommonUserTrackInfo : public G4VUserTrackInformation {
 public:
   using NanoClock = Gaudi::Timers::RdtscClock<std::chrono::nanoseconds>;
   static constexpr uint MAX_SUBDET_NUM = 26;
-  static constexpr uint MAX_SUBDET_SEQUENCE_NUM = 10;
+  static constexpr uint MAX_SUBDET_SEQUENCE_NUM = 100;
   CommonUserTrackInfo();
   ~CommonUserTrackInfo();
 
-- 
GitLab


From 86cf149f81c79caa61d2f4e35d65133e837dc898 Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Mon, 9 Jun 2025 16:10:52 +0800
Subject: [PATCH 66/74] fix: remove cout

---
 .../include/DetSimInterface/CommonUserTrackInfo.hh          | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/Simulation/DetSimInterface/include/DetSimInterface/CommonUserTrackInfo.hh b/Simulation/DetSimInterface/include/DetSimInterface/CommonUserTrackInfo.hh
index b82d3b3f..0004be9f 100644
--- a/Simulation/DetSimInterface/include/DetSimInterface/CommonUserTrackInfo.hh
+++ b/Simulation/DetSimInterface/include/DetSimInterface/CommonUserTrackInfo.hh
@@ -87,9 +87,9 @@ public:
     static constexpr uint kSlotsPerULL = 64 / 5;
 
     if (m_currentSubdetNum >= MAX_SUBDET_SEQUENCE_NUM * kSlotsPerULL) {
-      std::cerr
-          << "CommonUserTrackInfo::SetNthSubdet: error! subdet num overflow!"
-          << std::endl;
+      // std::cerr << "CommonUserTrackInfo::SetNthSubdet: error! subdet num "
+      //           << m_currentSubdetNum << " overflow!" << std::endl;
+      m_currentSubdetNum++;
       return;
     }
     // 每个unsigned long long可以存储12个5bit值(64/5=12)
-- 
GitLab


From 9e00639f8bdd3d73ba39a263d47ed60405ea32ba Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Tue, 10 Jun 2025 10:28:55 +0800
Subject: [PATCH 67/74] fix: update CurrentSubdet after UpdateTrackInfo

---
 Simulation/DetSimAna/src/ChronoAnaElemTool.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
index 8e04d84f..39ad3a3c 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -295,11 +295,11 @@ void ChronoAnaElemTool::UserSteppingAction(const G4Step *step) {
     auto newSubdet = getSubdetNameIdx(track->GetNextTouchable());
     if (trackInfo->IsSubdetChanged(newSubdet)) {
       auto stop = NanoClock::now();
-      trackInfo->SetCurrentSubdet(newSubdet);
       auto currentStep = track->GetCurrentStepNumber();
       trackInfo->UpdateTrackInfo(stop, currentStep);
       trackInfo->SetStepNum(currentStep);
       trackInfo->SetTrackSubdetChangeTime(stop);
+      trackInfo->SetCurrentSubdet(newSubdet);
     }
   }
 }
-- 
GitLab


From ca4e53c489d7ecb6787884fb0a97a1f250cdbbc8 Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Tue, 10 Jun 2025 10:31:12 +0800
Subject: [PATCH 68/74] fix: SetCurrentSubdet after UpdateTrackInfo

---
 Simulation/DetSimAna/src/ChronoAnaElemTool.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
index 39ad3a3c..d6d68c7b 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -297,9 +297,9 @@ void ChronoAnaElemTool::UserSteppingAction(const G4Step *step) {
       auto stop = NanoClock::now();
       auto currentStep = track->GetCurrentStepNumber();
       trackInfo->UpdateTrackInfo(stop, currentStep);
+      trackInfo->SetCurrentSubdet(newSubdet);
       trackInfo->SetStepNum(currentStep);
       trackInfo->SetTrackSubdetChangeTime(stop);
-      trackInfo->SetCurrentSubdet(newSubdet);
     }
   }
 }
-- 
GitLab


From cb3413af7a22fbc7d622e4134f223f3b7c66e710 Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Tue, 10 Jun 2025 11:54:50 +0800
Subject: [PATCH 69/74] fix: subdet num error

---
 Simulation/DetSimAna/src/ChronoAnaElemTool.h                    | 2 +-
 .../include/DetSimInterface/CommonUserTrackInfo.hh              | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.h b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
index 52e4ec46..0bb3e675 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.h
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
@@ -77,7 +77,7 @@ private:
   NTuple::Array<time_t> m_trackTime;
   NTuple::Array<uint8_t> m_trackStartSubdet, m_trackEndSubdet;
 
-  static constexpr uint MAX_SUBDET_NUM = 26;
+  static constexpr uint MAX_SUBDET_NUM = 27;
   NTuple::Matrix<time_t> m_trackTimePerSubdet;
   NTuple::Matrix<int> m_trackStepPerSubdet;
   static constexpr uint MAX_SUBDET_SEQUENCE_NUM = 100;
diff --git a/Simulation/DetSimInterface/include/DetSimInterface/CommonUserTrackInfo.hh b/Simulation/DetSimInterface/include/DetSimInterface/CommonUserTrackInfo.hh
index 0004be9f..60f1e5c2 100644
--- a/Simulation/DetSimInterface/include/DetSimInterface/CommonUserTrackInfo.hh
+++ b/Simulation/DetSimInterface/include/DetSimInterface/CommonUserTrackInfo.hh
@@ -22,7 +22,7 @@
 class CommonUserTrackInfo : public G4VUserTrackInformation {
 public:
   using NanoClock = Gaudi::Timers::RdtscClock<std::chrono::nanoseconds>;
-  static constexpr uint MAX_SUBDET_NUM = 26;
+  static constexpr uint MAX_SUBDET_NUM = 27;
   static constexpr uint MAX_SUBDET_SEQUENCE_NUM = 100;
   CommonUserTrackInfo();
   ~CommonUserTrackInfo();
-- 
GitLab


From 5e7055802a80ef0158d193b597f117ab84c98a71 Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Tue, 10 Jun 2025 15:39:04 +0800
Subject: [PATCH 70/74] fix: get real pdgid

---
 Simulation/DetSimAna/src/ChronoAnaElemTool.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
index d6d68c7b..5405d0d1 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -233,7 +233,7 @@ void ChronoAnaElemTool::PreUserTrackingAction(const G4Track *track) {
     m_trackZ[chronoTrackIdx] = pos.z();
     m_trackE[chronoTrackIdx] = track->GetTotalEnergy();
     m_trackPDGId[chronoTrackIdx] =
-        track->GetParticleDefinition()->GetParticleDefinitionID();
+        track->GetParticleDefinition()->GetPDGEncoding();
     m_trackStartSubdet[chronoTrackIdx] = currentSubdet;
     trackInfo->SetStepNum(track->GetCurrentStepNumber());
     trackInfo->SetCurrentSubdet(currentSubdet);
-- 
GitLab


From b65d30726286f80e0e2153012ac04f3da5c6506e Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Thu, 12 Jun 2025 21:46:02 +0800
Subject: [PATCH 71/74] fix: add track length per subdet

---
 Simulation/DetSimAna/src/ChronoAnaElemTool.cpp   | 16 +++++++++++++---
 Simulation/DetSimAna/src/ChronoAnaElemTool.h     |  1 +
 .../DetSimInterface/CommonUserTrackInfo.hh       | 12 +++++++++++-
 .../DetSimInterface/src/CommonUserTrackInfo.cc   |  5 +++++
 4 files changed, 30 insertions(+), 4 deletions(-)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
index 5405d0d1..3481a186 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -140,6 +140,10 @@ StatusCode ChronoAnaElemTool::initNtuple() {
                  ->addIndexedItem("TrackStepPerSubdet", m_trackIdx,
                                   MAX_SUBDET_NUM, m_trackStepPerSubdet)
                  .isSuccess();
+      suc &= m_ntuple
+                 ->addIndexedItem("TrackLengthPerSubdet", m_trackIdx,
+                                  MAX_SUBDET_NUM, m_trackLengthPerSubdet)
+                 .isSuccess();
       suc &=
           m_ntuple
               ->addIndexedItem("TrackNumSubdet", m_trackIdx, m_trackNumSubdet)
@@ -236,6 +240,7 @@ void ChronoAnaElemTool::PreUserTrackingAction(const G4Track *track) {
         track->GetParticleDefinition()->GetPDGEncoding();
     m_trackStartSubdet[chronoTrackIdx] = currentSubdet;
     trackInfo->SetStepNum(track->GetCurrentStepNumber());
+    trackInfo->SetTrackLength(track->GetTrackLength());
     trackInfo->SetCurrentSubdet(currentSubdet);
   } else {
     // resumed track
@@ -256,6 +261,7 @@ void ChronoAnaElemTool::PostUserTrackingAction(const G4Track *track) {
       static_cast<CommonUserTrackInfo *>(track->GetUserInformation());
   auto chronoTrackIdx = trackInfo->GetChronoTrackIdx();
   auto currentStep = track->GetCurrentStepNumber();
+  auto currentTrackLength = track->GetTrackLength();
 
   // record value
   m_trackdE[chronoTrackIdx] =
@@ -263,16 +269,18 @@ void ChronoAnaElemTool::PostUserTrackingAction(const G4Track *track) {
   m_trackTime[chronoTrackIdx] +=
       NanoClock::duration(trackStop - trackInfo->GetTrackStartTime()).count();
   auto track_end_subdet = getSubdetNameIdx(track->GetTouchable());
-  m_trackLength[chronoTrackIdx] = track->GetTrackLength();
+  m_trackLength[chronoTrackIdx] = currentTrackLength;
   m_trackStepNum[chronoTrackIdx] = currentStep;
   m_trackEndSubdet[chronoTrackIdx] = track_end_subdet;
   // add remained time
-  trackInfo->UpdateTrackInfo(trackStop, currentStep);
+  trackInfo->UpdateTrackInfo(trackStop, currentStep, currentTrackLength);
   const auto trackStepPerSubdet = trackInfo->GetTrackStepPerSubdet();
   const auto trackTimePerSubdet = trackInfo->GetTrackTimePerSubdet();
+  const auto trackLenPerSubdet = trackInfo->GetTrackLengthPerSubdet();
   for (size_t i = 0; i < MAX_SUBDET_NUM; i++) {
     m_trackTimePerSubdet[chronoTrackIdx][i] = trackTimePerSubdet[i];
     m_trackStepPerSubdet[chronoTrackIdx][i] = trackStepPerSubdet[i];
+    m_trackLengthPerSubdet[chronoTrackIdx][i] = trackLenPerSubdet[i];
   }
   const auto trackSubdetSequence = trackInfo->GetTrackSubdetSequence();
   m_trackNumSubdet[chronoTrackIdx] = trackInfo->GetNumSubdet();
@@ -296,8 +304,10 @@ void ChronoAnaElemTool::UserSteppingAction(const G4Step *step) {
     if (trackInfo->IsSubdetChanged(newSubdet)) {
       auto stop = NanoClock::now();
       auto currentStep = track->GetCurrentStepNumber();
-      trackInfo->UpdateTrackInfo(stop, currentStep);
+      auto currentTrackLength = track->GetTrackLength();
+      trackInfo->UpdateTrackInfo(stop, currentStep, currentTrackLength);
       trackInfo->SetCurrentSubdet(newSubdet);
+      trackInfo->SetTrackLength(currentTrackLength);
       trackInfo->SetStepNum(currentStep);
       trackInfo->SetTrackSubdetChangeTime(stop);
     }
diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.h b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
index 0bb3e675..1ae63488 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.h
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
@@ -80,6 +80,7 @@ private:
   static constexpr uint MAX_SUBDET_NUM = 27;
   NTuple::Matrix<time_t> m_trackTimePerSubdet;
   NTuple::Matrix<int> m_trackStepPerSubdet;
+  NTuple::Matrix<double> m_trackLengthPerSubdet;
   static constexpr uint MAX_SUBDET_SEQUENCE_NUM = 100;
   NTuple::Array<int> m_trackNumSubdet;
   // 5bit for one subdet, uint64 for 12 subdets
diff --git a/Simulation/DetSimInterface/include/DetSimInterface/CommonUserTrackInfo.hh b/Simulation/DetSimInterface/include/DetSimInterface/CommonUserTrackInfo.hh
index 60f1e5c2..836e0b80 100644
--- a/Simulation/DetSimInterface/include/DetSimInterface/CommonUserTrackInfo.hh
+++ b/Simulation/DetSimInterface/include/DetSimInterface/CommonUserTrackInfo.hh
@@ -54,6 +54,7 @@ public:
   int GetNumSubdet() const;
   const std::array<unsigned long long, MAX_SUBDET_SEQUENCE_NUM> &
   GetTrackSubdetSequence() const;
+  const std::array<double, MAX_SUBDET_NUM> &GetTrackLengthPerSubdet() const;
 
 private:
   int m_idxEdm4hep = -1;
@@ -62,10 +63,12 @@ private:
   uint8_t m_currentSubdet = 0;
   int m_currentStepNum = 0;
   uint m_currentSubdetNum = 0;
+  double m_currentTrackLength = 0.0;
 
   NanoClock::time_point m_trackStartTime, m_trackSubdetChangeTime;
   std::array<unsigned long long, MAX_SUBDET_NUM> m_trackTimePerSubdet{};
   std::array<int, MAX_SUBDET_NUM> m_trackStepPerSubdet{};
+  std::array<double, MAX_SUBDET_NUM> m_trackLengthPerSubdet{};
   std::array<unsigned long long, MAX_SUBDET_SEQUENCE_NUM>
       m_trackSubdetSequence{};
 
@@ -108,15 +111,22 @@ public:
   };
 
   inline void UpdateTrackInfo(const NanoClock::time_point &stop,
-                              const int currentStep) {
+                              const int currentStep,
+                              const double currentTrackLength) {
     m_trackTimePerSubdet[m_currentSubdet] +=
         NanoClock::duration(stop - m_trackSubdetChangeTime).count();
     m_trackStepPerSubdet[m_currentSubdet] += currentStep - m_currentStepNum;
+    m_trackLengthPerSubdet[m_currentSubdet] +=
+        currentTrackLength - m_currentTrackLength;
   };
 
   inline void SetStepNum(const int newStepNum) {
     m_currentStepNum = newStepNum;
   };
+
+  inline void SetTrackLength(const double newStepLen) {
+    m_currentTrackLength = newStepLen;
+  }
 };
 
 #endif
diff --git a/Simulation/DetSimInterface/src/CommonUserTrackInfo.cc b/Simulation/DetSimInterface/src/CommonUserTrackInfo.cc
index 84bcddb2..cf90d9fc 100644
--- a/Simulation/DetSimInterface/src/CommonUserTrackInfo.cc
+++ b/Simulation/DetSimInterface/src/CommonUserTrackInfo.cc
@@ -47,3 +47,8 @@ CommonUserTrackInfo::GetTrackSubdetSequence() const {
 }
 
 int CommonUserTrackInfo::GetNumSubdet() const { return m_currentSubdetNum; };
+
+const std::array<double, CommonUserTrackInfo::MAX_SUBDET_NUM> &
+CommonUserTrackInfo::GetTrackLengthPerSubdet() const {
+  return m_trackLengthPerSubdet;
+}
\ No newline at end of file
-- 
GitLab


From ebffda886641d8ecc877810289a2954724dba5d6 Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Fri, 20 Jun 2025 16:56:27 +0800
Subject: [PATCH 72/74] fix: add simplified TPC and wrapped volume

---
 Detector/DetCRD/CMakeLists.txt                |   2 +
 .../TPC_ModularEndcap_o1_v02_simplified.xml   | 220 +++++++
 .../TPC_ModularEndcap_o1_v02_wrap.xml         | 220 +++++++
 .../compact/TDR_o1_v01/TDR_o1_v01_noB.xml     |  85 +++
 .../TDR_o1_v01/TDR_o1_v01_simpleTPC.xml       |  85 +++
 .../compact/TDR_o1_v01/TDR_o1_v01_wrap.xml    |  85 +++
 .../TPC_ModularEndcap_o1_v01_simplified.cpp   | 557 +++++++++++++++++
 .../TPC_ModularEndcap_o1_v01_wrapvolume.cpp   | 582 ++++++++++++++++++
 8 files changed, 1836 insertions(+)
 create mode 100644 Detector/DetCRD/compact/CRD_common_v02/TPC_ModularEndcap_o1_v02_simplified.xml
 create mode 100644 Detector/DetCRD/compact/CRD_common_v02/TPC_ModularEndcap_o1_v02_wrap.xml
 create mode 100644 Detector/DetCRD/compact/TDR_o1_v01/TDR_o1_v01_noB.xml
 create mode 100644 Detector/DetCRD/compact/TDR_o1_v01/TDR_o1_v01_simpleTPC.xml
 create mode 100644 Detector/DetCRD/compact/TDR_o1_v01/TDR_o1_v01_wrap.xml
 create mode 100644 Detector/DetCRD/src/Tracker/TPC_ModularEndcap_o1_v01_simplified.cpp
 create mode 100644 Detector/DetCRD/src/Tracker/TPC_ModularEndcap_o1_v01_wrapvolume.cpp

diff --git a/Detector/DetCRD/CMakeLists.txt b/Detector/DetCRD/CMakeLists.txt
index c71a0e02..7909ead5 100644
--- a/Detector/DetCRD/CMakeLists.txt
+++ b/Detector/DetCRD/CMakeLists.txt
@@ -31,6 +31,8 @@ gaudi_add_module(DetCRD
                          src/Tracker/SiTrackerComposite_v03_geo.cpp
                          src/Tracker/TPC_Simple_o1_v01.cpp
                          src/Tracker/TPC_ModularEndcap_o1_v01.cpp
+                         src/Tracker/TPC_ModularEndcap_o1_v01_simplified.cpp
+                         src/Tracker/TPC_ModularEndcap_o1_v01_wrapvolume.cpp
 			 src/Tracker/SiTracker_itkbarrel_v01_geo.cpp
 			 src/Tracker/SiTracker_itkbarrel_v02_geo.cpp
                          src/Tracker/SiTracker_otkbarrel_v01_geo.cpp
diff --git a/Detector/DetCRD/compact/CRD_common_v02/TPC_ModularEndcap_o1_v02_simplified.xml b/Detector/DetCRD/compact/CRD_common_v02/TPC_ModularEndcap_o1_v02_simplified.xml
new file mode 100644
index 00000000..799174a9
--- /dev/null
+++ b/Detector/DetCRD/compact/CRD_common_v02/TPC_ModularEndcap_o1_v02_simplified.xml
@@ -0,0 +1,220 @@
+<lccdd>
+    <info
+        name   ="TPC_ModularEndcap_TDR_o1_v02"
+        status ="developing"
+        version = "TPC_ModularEndcap_TDR_o1_v02"
+        author = "Xin She">
+        <comment> The compact format for the CEPC TPC used for the TDR </comment>
+    </info>
+
+    <define> 
+        <!--from CDR baseline-->
+        <!--Readout pad size, clustering in each 10 rows-->
+        <constant name="TPC_pad_height"              value="5.0*mm"      />
+        <constant name="TPC_pad_width"               value="0.5*mm"      />
+        <constant name="TPC_max_step_length"         value="5*mm"      />
+        <constant name="TPC_sensitive_threshold_eV"      value="32*eV"     />
+        <!--Wall/Cathode/Readout/Endplate-->
+        <constant name="TPC_dr_InnerWall"            value="25.*mm"     />
+        <constant name="TPC_dr_InnerServiceArea"     value="10*mm"     />
+        <constant name="TPC_dr_OuterServiceArea"     value="23*mm"     />
+        <constant name="TPC_dr_OuterWall"            value="25.*mm"     />
+        <constant name="TPC_dz_Cathode"              value="0.06*mm"   />
+        <constant name="TPC_dz_Readout"              value="3.50*mm"     />
+        <constant name="TPC_dz_Endplate"             value="135*mm"    />
+        <constant name="TPC_dz_Cathode_Insulator"    value="0.0275*mm" />
+        <constant name="TPC_dz_Cathode_Conductor"    value="0.0025*mm" />
+        <constant name="TPC_dr_Cathode_Grip"         value="10.*mm"     />
+        <constant name="TPC_dz_Cathode_Grip"         value="15*mm"     />
+        <!-- Inermeidate variables                                --> 
+        <constant name="TPC_rMin_GasVolume"          value="TPC_inner_radius+TPC_dr_InnerWall"/>
+        <constant name="TPC_rMax_GasVolume"          value="TPC_outer_radius-TPC_dr_OuterWall"/>
+        <constant name="TPC_dz_GasVolume"            value="TPC_half_length-TPC_dz_Endplate"/>
+        <constant name="TPC_rMin_Sensitive"          value="TPC_rMin_GasVolume+TPC_dr_InnerServiceArea"/>
+        <constant name="TPC_rMax_Sensitive"          value="TPC_rMax_GasVolume-TPC_dr_OuterServiceArea"/>
+        <constant name="TPC_dz_Wall"                 value="2*TPC_dz_GasVolume"/>
+        <constant name="TPC_dz_Sensitive"            value="TPC_dz_GasVolume-TPC_dz_Readout-TPC_dz_Cathode/2."/>
+        <constant name="TPC_numberOfPadrows"         value="int((TPC_rMax_Sensitive-TPC_rMin_Sensitive)/TPC_pad_height)"/>
+    </define>
+
+    <display>
+      <vis name="CuVis"   alpha="1.0" r="0.5" g=".5"  b=".5"   showDaughters="true"  visible="true"/>
+      <vis name="KaptonVis"   alpha="0.8" r="1" g="0."  b="0."   showDaughters="true"  visible="true"/>
+      <vis name="gasVis" alpha="0.2" r="0.0" g="1.0" b="0.0"  showDaughters="true"  visible="true"/>
+      <vis name="transVis" alpha="0.1" r="1.0" g="1.0" b="1.0"  showDaughters="true"  visible="true"/>
+      <vis name="readoutVis" alpha="0.8" r="1.0" g=".0" b=".0"  showDaughters="true"  visible="true"/>
+      <vis name="aramidVis" alpha="1.0" r="0.0" g="0.3" b="0.7"  showDaughters="true"  visible="true"/>
+      <vis name="epoxyVis" alpha="0.2" r="0.0" g="0.3" b="0.3"  showDaughters="true"  visible="true"/>
+      <vis name="TPCMotherVis1"     alpha="0.5" r="0.96"  g="0.64"    b="0.90"  showDaughters="true"  visible="true"/>
+    </display>
+
+    <detectors>
+      <detector name="TPC" type="TPC_ModularEndcap_o1_v01_simplified" vis="TPCVis" id="DetID_TPC" limits="tpc_limits" readout="TPCCollection" insideTrackingVolume="true">
+
+      <envelope vis="TPCVis">
+        <shape type="Tube" rmin="TPC_inner_radius" rmax="TPC_outer_radius" dz="TPC_half_length" material="Air"/>
+      </envelope>
+      
+      <type_flags type="DetType_TRACKER +  DetType_BARREL + DetType_GASEOUS "/>
+    
+       <component name="TPCinnerWall" type="TPCinnerWall" R_start="TPC_inner_radius" R_end="TPC_rMin_GasVolume" Z_fulllength="TPC_dz_Wall" vis="SeeThrough">
+           <!--HoneyComb wall  -->
+           <!--layer material="G4_Cu"           thickness = "0.01*mm"  vis="CuVis" />
+           <layer material="Kapton"          thickness = "0.05*mm"  vis="KaptonVis" />
+           <layer material="g10-TPC"         thickness = "0.3*mm"  vis="gasVis" />
+           <layer material="Aramid"          thickness = "0.07*mm"  vis="aramidVis" />
+           <layer material="AramidHoneycomb" thickness = "23.5*mm"  vis="aramidVis" />
+           <layer material="g10-TPC"         thickness = "0.3*mm"  vis="gasVis" />
+           <layer material="Kapton"          thickness = "0.0125*mm"  vis="KaptonVis" />
+           <layer material="Copper80P"       thickness = "0.035*mm"  vis="CuVis" />
+           <layer material="Kapton"          thickness = "0.05*mm"  vis="KaptonVis" />
+           <layer material="Copper80P"       thickness = "0.035*mm"  vis="CuVis" />
+           <layer material="epoxy"           thickness = "0.042*mm"  vis="epoxy" /-->
+           <!--CF wall  -->
+           <layer material="G4_Cu"           thickness = "0.010*mm"  vis="CuVis"/>
+           <layer material="CarbonFiber"     thickness = "0.1*mm"    vis="aramidVis"/>
+           <layer material="G4_Cu"           thickness = "0.03*mm"  vis="CuVis"/>
+           <layer material="Polyimide"       thickness = "0.05*mm"  vis="KaptonVis"/>
+           <layer material="G4_Cu"           thickness = "0.03*mm"  vis="CuVis"/>
+           <layer material="CarbonFiber"     thickness = "0.1*mm"    vis="aramidVis"/>
+       </component>
+       <component name="TPCouterWall" type="TPCouterWall" R_start="TPC_rMax_GasVolume" R_end="TPC_outer_radius" Z_fulllength="TPC_dz_Wall" vis="SeeThrough">
+           <!--HoneyComb wall  -->
+           <!--layer material="Copper80P"       thickness = "0.01*mm"  vis="CuVis" />
+           <layer material="Kapton"          thickness = "0.05*mm"  vis="KaptonVis" />
+           <layer material="Copper80P"       thickness = "0.03*mm"  vis="CuVis" />
+           <layer material="Kapton"          thickness = "0.07*mm"  vis="KaptonVis" />
+           <layer material="g10-TPC"         thickness = "0.3*mm"  vis="gasVis" />
+           <layer material="AramidHoneycomb" thickness = "23.5*mm"  vis="aramidVis" />
+           <layer material="Aramid"          thickness = "0.07*mm"  vis="aramidVis" />
+           <layer material="g10-TPC"         thickness = "0.3*mm"  vis="gasVis" />
+           <layer material="Kapton"          thickness = "0.05*mm"  vis="KaptonVis" />
+           <layer material="G4_Cu"           thickness = "0.01*mm"  vis="CuVis" />
+           <layer material="epoxy"           thickness = "0.042*mm"  vis="epoxy" /-->
+           <!--CF wall  -->
+           <layer material="CarbonFiber"     thickness = "0.1*mm"    vis="aramidVis"/>
+           <layer material="G4_Cu"           thickness = "0.03*mm"   vis="CuVis"/>
+           <layer material="Polyimide"       thickness = "0.05*mm"   vis="KaptonVis"/>
+           <layer material="G4_Cu"           thickness = "0.03*mm"   vis="CuVis"/>
+           <layer material="CarbonFiber"     thickness = "0.1*mm"    vis="aramidVis"/>
+           <layer material="G4_Cu"           thickness = "0.010*mm"  vis="CuVis"/>
+       </component>
+       <component name="Grip" type="TPCGrip" material="SiC_foam"  vis="SeeThrough">
+           <layer name="TPCinnerGrip" rmin="TPC_rMin_GasVolume"  rmax="TPC_rMin_GasVolume+TPC_dr_InnerServiceArea" z_length="TPC_dz_Cathode_Grip" vis="KaptonVis"/>
+           <layer name="TPCouterGrip" rmin="TPC_rMax_GasVolume-TPC_dr_OuterServiceArea"  rmax="TPC_rMax_GasVolume" z_length="TPC_dz_Cathode_Grip" vis="KaptonVis"/>
+       </component>
+       <component name="Cathode" type="TPCCathode" vis="SeeThrough">
+           <layer name="Cathodeinsulator" material= "Kapton" rmin="TPC_rMin_Sensitive"  rmax="TPC_rMax_Sensitive" z_length="TPC_dz_Cathode_Insulator" vis="KaptonVis">
+                  <position x="0.*mm" y="0.*mm" z="+ TPC_dz_Cathode_Insulator/2."/>
+                  <position x="0.*mm" y="0.*mm" z="- TPC_dz_Cathode_Insulator/2."/>
+           </layer>
+           <layer name="Cathodeconductor" material= "G4_Cu" rmin="TPC_rMin_Sensitive" rmax="TPC_rMax_Sensitive" z_length="TPC_dz_Cathode_Conductor" vis="CuVis">
+                  <position x="0.*mm" y="0.*mm" z="+ (TPC_dz_Cathode_Insulator+ (TPC_dz_Cathode_Conductor/2.))"/>
+                  <position x="0.*mm" y="0.*mm" z="- (TPC_dz_Cathode_Insulator+ (TPC_dz_Cathode_Conductor/2.))"/>
+           </layer>
+       </component>
+       <component name="TPCreadout" type="TPCreadout" material="T2KGas1" vis="SeeThrough">
+           <dimensions rmin = "TPC_rMin_GasVolume" rmax = "TPC_rMax_GasVolume" z_length = "TPC_dz_Readout"/>
+           <position x="0.*mm" y="0.*mm" z="+(TPC_half_length-TPC_dz_Endplate-TPC_dz_Readout/2.)"/>
+           <!--Triple Gem readout structure from CEPCV4-->
+           <!--layer material="G4_Cu"       dz="0.003*mm"  comment="gating" vis="CuVis" />
+           <layer material="G4_KAPTON"   dz="0.030*mm"  comment="gating" vis="KaptonVis"  />
+           <layer material="G4_Cu"       dz="0.003*mm"  comment="gating"    vis="CuVis"/>
+           <layer material="T2KGas1"     dz="4.447*mm"  comment="gating"  vis="gasVis"/>
+           <layer material="G4_Cu"       dz="0.003*mm"  comment="mpgd"      vis="CuVis"/>
+           <layer material="G4_KAPTON"   dz="0.030*mm"  comment="mpgd"   vis="Kapton"/>
+           <layer material="G4_Cu"       dz="0.003*mm"  comment="mpgd"      vis="CuVis"/>
+           <layer material="T2KGas1"     dz="4.447*mm"  comment="mpgd"    vis="gasVis"/>
+           <layer material="G4_Cu"       dz="0.003*mm"  comment="mpgd"      vis="CuVis"/>
+           <layer material="G4_KAPTON"   dz="0.030*mm"  comment="mpgd"   vis="KaptonVis"/>
+           <layer material="G4_Cu"       dz="0.003*mm"  comment="mpgd"      vis="CuVis"/>
+           <layer material="T2KGas1"     dz="4.447*mm"  comment="mpgd"    vis="gasVis"/>
+           <layer material="G4_Cu"       dz="0.050*mm"  comment="pads"       vis="CuVis"/>
+           <layer material="g10"         dz="2.000*mm"  comment="structural"      vis="gasVis"/>
+           <layer material="G4_Si"       dz="0.500*mm"  comment="electronics" vis="epoxyVis" />
+           <layer material="epoxy"       dz="2*mm"      comment="structural"    vis="epoxyVis"/>
+           <layer material="G4_KAPTON"   dz="1*mm"      comment="structural" vis="KaptonVis"  />
+           <layer material="G4_Al"       dz="2*mm"      comment="Cooling"       vis="GrayVis"/>
+           <layer material="G4_KAPTON"   dz="1*mm"      comment="structural"  vis="KaptonVis"/>
+           <layer material="CarbonFiber" dz="3*mm"      comment="structural" vis="GrayVis"  /-->
+           <!--MicroMegas readout structure-->
+           <layer material="G4_Fe"       dz="0.025*mm"  comment="Mesh"           vis="GrayVis"/>
+           <layer material="T2KGas1"     dz="0.128*mm"  comment="GasAmpRegion"   vis="gasVis"/>
+           <layer material="G4_KAPTON"   dz="0.050*mm"  comment="Kaptonfoil"     vis="KaptonVis"/>
+           <layer material="Acrylicglue" dz="0.008*mm"  comment="gule"           vis="BlueVis"/>
+           <layer material="G4_Cu"       dz="0.017*mm"  comment="ReadoutPad"     vis="CuVis"/>
+           <layer material="PCB"         dz="1.000*mm"  comment="PCBboard"       vis="GreenVis"/>
+           <layer material="G4_Si"       dz="0.500*mm"  comment="electronics"    vis="BlackVis"/>
+           <layer material="epoxy"       dz="1.500*mm"  comment="structural"     vis="epoxyVis"/>
+       </component>
+       <component name="TPCSensitiveVol" type="TPCSensitiveVol" material="T2KGas1" vis="gasVis">
+           <dimensions rmin = "TPC_rMin_Sensitive" rmax = "TPC_rMax_Sensitive" z_length = "TPC_dz_Sensitive"/>
+           <layer      repeat="TPC_numberOfPadrows" thickness="TPC_pad_height"/>
+       </component>
+       <component name="TPCEndplate" type="TPCEndplate" z_frame="20.*mm" s_frame="20.*mm" vis="SeeThrough">
+           <dimensions rmin = "TPC_inner_radius" rmax = "TPC_outer_radius" z_length = "TPC_dz_Endplate"/>
+           <layer name="InnerPlate" type="Frame" thickness="25.*mm"/>
+           <layer name="ring1"      type="Frame" thickness="10.*mm"/>
+           <layer name="module1"    type="Module" thickness="151.*mm" repeat="23" phi0_offset="0.*deg">
+             <slice material="PCB"              dz="5.0*mm"  comment="FEE_BEEPCB"/>
+             <slice material="G4_Al"            dz="1.5*mm"  comment="cooling"/>
+             <slice material="TPC_endplate_mix" dz="50.0*mm" comment="MonitorMix"/>
+             <slice material="CarbonFiber"      dz="3.0*mm"  comment="structural"/>
+           </layer>
+           <layer name="ring2"      type="Frame" thickness="20.*mm"/>
+           <layer name="module2"    type="Module" thickness="141.*mm" repeat="27" phi0_offset="+10.*deg">
+             <slice material="PCB"              dz="5.0*mm"  comment="FEE_BEEPCB"/>
+             <slice material="G4_Al"            dz="1.5*mm"  comment="cooling"/>
+             <slice material="TPC_endplate_mix" dz="50.0*mm" comment="MonitorMix"/>
+             <slice material="CarbonFiber"      dz="3.0*mm"  comment="structural"/>
+             <slice material="PCB"   dz="10.0*mm" comment="cooling"/>
+           </layer>
+           <layer name="ring3"      type="Frame" thickness="20.*mm"/>
+           <layer name="module3"    type="Module" thickness="141.*mm" repeat="32" phi0_offset="-5.*deg">
+             <slice material="PCB"              dz="5.0*mm"  comment="FEE_BEEPCB"/>
+             <slice material="G4_Al"            dz="1.5*mm"  comment="cooling"/>
+             <slice material="TPC_endplate_mix" dz="50.0*mm" comment="MonitorMix"/>
+             <slice material="CarbonFiber"      dz="3.0*mm"  comment="structural"/>
+           </layer> 
+           <layer name="ring4"      type="Frame" thickness="20.*mm"/>
+           <layer name="module4"    type="Module" thickness="141.*mm" repeat="34" phi0_offset="0.*deg">
+             <slice material="PCB"              dz="5.0*mm"  comment="FEE_BEEPCB"/>
+             <slice material="G4_Al"            dz="1.5*mm"  comment="cooling"/>
+             <slice material="TPC_endplate_mix" dz="50.0*mm" comment="MonitorMix"/>
+             <slice material="CarbonFiber"      dz="3.0*mm"  comment="structural"/>
+           </layer>
+           <layer name="ring5"      type="Frame" thickness="20.*mm"/>
+           <layer name="module5"    type="Module" thickness="141.*mm" repeat="39" phi0_offset="+5.*deg">
+             <slice material="PCB"              dz="5.0*mm"  comment="FEE_BEEPCB"/>
+             <slice material="G4_Al"            dz="1.5*mm"  comment="cooling"/>
+             <slice material="TPC_endplate_mix" dz="50.0*mm" comment="MonitorMix"/>
+             <slice material="CarbonFiber"      dz="3.0*mm"  comment="structural"/>
+           </layer>
+           <layer name="ring6"      type="Frame" thickness="20.*mm"/>
+           <layer name="module6"    type="Module" thickness="141.*mm" repeat="44" phi0_offset="-5.*deg">
+             <slice material="PCB"              dz="5.0*mm"  comment="FEE_BEEPCB"/>
+             <slice material="G4_Al"            dz="1.5*mm"  comment="cooling"/>
+             <slice material="TPC_endplate_mix" dz="50.0*mm" comment="MonitorMix"/>
+             <slice material="CarbonFiber"      dz="3.0*mm"  comment="structural"/>
+           </layer>
+           <layer name="ring7"      type="Frame" thickness="20.*mm"/>
+           <layer name="module7"    type="Module" thickness="141.*mm" repeat="49" phi0_offset="0.*deg">
+             <slice material="PCB"              dz="5.0*mm"  comment="FEE_BEEPCB"/>
+             <slice material="G4_Al"            dz="1.5*mm"  comment="cooling"/>
+             <slice material="TPC_endplate_mix" dz="50.0*mm" comment="MonitorMix"/>
+             <slice material="CarbonFiber"      dz="3.0*mm"  comment="structural"/>
+           </layer>
+           <layer name="ring8"      type="Frame" thickness="23.*mm"/>
+           <layer name="Outerplate" type="Frame" thickness="25.*mm"/>
+       </component>
+    
+    </detector>
+  </detectors>
+
+  <readouts>
+    <readout name="TPCCollection">
+      <id>system:5,side:-2,layer:13,module:6,sensor:6</id>
+    </readout>
+  </readouts>
+
+</lccdd>
diff --git a/Detector/DetCRD/compact/CRD_common_v02/TPC_ModularEndcap_o1_v02_wrap.xml b/Detector/DetCRD/compact/CRD_common_v02/TPC_ModularEndcap_o1_v02_wrap.xml
new file mode 100644
index 00000000..70049c1b
--- /dev/null
+++ b/Detector/DetCRD/compact/CRD_common_v02/TPC_ModularEndcap_o1_v02_wrap.xml
@@ -0,0 +1,220 @@
+<lccdd>
+    <info
+        name   ="TPC_ModularEndcap_TDR_o1_v02"
+        status ="developing"
+        version = "TPC_ModularEndcap_TDR_o1_v02"
+        author = "Xin She">
+        <comment> The compact format for the CEPC TPC used for the TDR </comment>
+    </info>
+
+    <define> 
+        <!--from CDR baseline-->
+        <!--Readout pad size, clustering in each 10 rows-->
+        <constant name="TPC_pad_height"              value="5.0*mm"      />
+        <constant name="TPC_pad_width"               value="0.5*mm"      />
+        <constant name="TPC_max_step_length"         value="5*mm"      />
+        <constant name="TPC_sensitive_threshold_eV"      value="32*eV"     />
+        <!--Wall/Cathode/Readout/Endplate-->
+        <constant name="TPC_dr_InnerWall"            value="25.*mm"     />
+        <constant name="TPC_dr_InnerServiceArea"     value="10*mm"     />
+        <constant name="TPC_dr_OuterServiceArea"     value="23*mm"     />
+        <constant name="TPC_dr_OuterWall"            value="25.*mm"     />
+        <constant name="TPC_dz_Cathode"              value="0.06*mm"   />
+        <constant name="TPC_dz_Readout"              value="3.50*mm"     />
+        <constant name="TPC_dz_Endplate"             value="135*mm"    />
+        <constant name="TPC_dz_Cathode_Insulator"    value="0.0275*mm" />
+        <constant name="TPC_dz_Cathode_Conductor"    value="0.0025*mm" />
+        <constant name="TPC_dr_Cathode_Grip"         value="10.*mm"     />
+        <constant name="TPC_dz_Cathode_Grip"         value="15*mm"     />
+        <!-- Inermeidate variables                                --> 
+        <constant name="TPC_rMin_GasVolume"          value="TPC_inner_radius+TPC_dr_InnerWall"/>
+        <constant name="TPC_rMax_GasVolume"          value="TPC_outer_radius-TPC_dr_OuterWall"/>
+        <constant name="TPC_dz_GasVolume"            value="TPC_half_length-TPC_dz_Endplate"/>
+        <constant name="TPC_rMin_Sensitive"          value="TPC_rMin_GasVolume+TPC_dr_InnerServiceArea"/>
+        <constant name="TPC_rMax_Sensitive"          value="TPC_rMax_GasVolume-TPC_dr_OuterServiceArea"/>
+        <constant name="TPC_dz_Wall"                 value="2*TPC_dz_GasVolume"/>
+        <constant name="TPC_dz_Sensitive"            value="TPC_dz_GasVolume-TPC_dz_Readout-TPC_dz_Cathode/2."/>
+        <constant name="TPC_numberOfPadrows"         value="int((TPC_rMax_Sensitive-TPC_rMin_Sensitive)/TPC_pad_height)"/>
+    </define>
+
+    <display>
+      <vis name="CuVis"   alpha="1.0" r="0.5" g=".5"  b=".5"   showDaughters="true"  visible="true"/>
+      <vis name="KaptonVis"   alpha="0.8" r="1" g="0."  b="0."   showDaughters="true"  visible="true"/>
+      <vis name="gasVis" alpha="0.2" r="0.0" g="1.0" b="0.0"  showDaughters="true"  visible="true"/>
+      <vis name="transVis" alpha="0.1" r="1.0" g="1.0" b="1.0"  showDaughters="true"  visible="true"/>
+      <vis name="readoutVis" alpha="0.8" r="1.0" g=".0" b=".0"  showDaughters="true"  visible="true"/>
+      <vis name="aramidVis" alpha="1.0" r="0.0" g="0.3" b="0.7"  showDaughters="true"  visible="true"/>
+      <vis name="epoxyVis" alpha="0.2" r="0.0" g="0.3" b="0.3"  showDaughters="true"  visible="true"/>
+      <vis name="TPCMotherVis1"     alpha="0.5" r="0.96"  g="0.64"    b="0.90"  showDaughters="true"  visible="true"/>
+    </display>
+
+    <detectors>
+      <detector name="TPC" type="TPC_ModularEndcap_o1_v01_wrapvolume" vis="TPCVis" id="DetID_TPC" limits="tpc_limits" readout="TPCCollection" insideTrackingVolume="true">
+
+      <envelope vis="TPCVis">
+        <shape type="Tube" rmin="TPC_inner_radius" rmax="TPC_outer_radius" dz="TPC_half_length" material="Air"/>
+      </envelope>
+      
+      <type_flags type="DetType_TRACKER +  DetType_BARREL + DetType_GASEOUS "/>
+    
+       <component name="TPCinnerWall" type="TPCinnerWall" R_start="TPC_inner_radius" R_end="TPC_rMin_GasVolume" Z_fulllength="TPC_dz_Wall" vis="SeeThrough">
+           <!--HoneyComb wall  -->
+           <!--layer material="G4_Cu"           thickness = "0.01*mm"  vis="CuVis" />
+           <layer material="Kapton"          thickness = "0.05*mm"  vis="KaptonVis" />
+           <layer material="g10-TPC"         thickness = "0.3*mm"  vis="gasVis" />
+           <layer material="Aramid"          thickness = "0.07*mm"  vis="aramidVis" />
+           <layer material="AramidHoneycomb" thickness = "23.5*mm"  vis="aramidVis" />
+           <layer material="g10-TPC"         thickness = "0.3*mm"  vis="gasVis" />
+           <layer material="Kapton"          thickness = "0.0125*mm"  vis="KaptonVis" />
+           <layer material="Copper80P"       thickness = "0.035*mm"  vis="CuVis" />
+           <layer material="Kapton"          thickness = "0.05*mm"  vis="KaptonVis" />
+           <layer material="Copper80P"       thickness = "0.035*mm"  vis="CuVis" />
+           <layer material="epoxy"           thickness = "0.042*mm"  vis="epoxy" /-->
+           <!--CF wall  -->
+           <layer material="G4_Cu"           thickness = "0.010*mm"  vis="CuVis"/>
+           <layer material="CarbonFiber"     thickness = "0.1*mm"    vis="aramidVis"/>
+           <layer material="G4_Cu"           thickness = "0.03*mm"  vis="CuVis"/>
+           <layer material="Polyimide"       thickness = "0.05*mm"  vis="KaptonVis"/>
+           <layer material="G4_Cu"           thickness = "0.03*mm"  vis="CuVis"/>
+           <layer material="CarbonFiber"     thickness = "0.1*mm"    vis="aramidVis"/>
+       </component>
+       <component name="TPCouterWall" type="TPCouterWall" R_start="TPC_rMax_GasVolume" R_end="TPC_outer_radius" Z_fulllength="TPC_dz_Wall" vis="SeeThrough">
+           <!--HoneyComb wall  -->
+           <!--layer material="Copper80P"       thickness = "0.01*mm"  vis="CuVis" />
+           <layer material="Kapton"          thickness = "0.05*mm"  vis="KaptonVis" />
+           <layer material="Copper80P"       thickness = "0.03*mm"  vis="CuVis" />
+           <layer material="Kapton"          thickness = "0.07*mm"  vis="KaptonVis" />
+           <layer material="g10-TPC"         thickness = "0.3*mm"  vis="gasVis" />
+           <layer material="AramidHoneycomb" thickness = "23.5*mm"  vis="aramidVis" />
+           <layer material="Aramid"          thickness = "0.07*mm"  vis="aramidVis" />
+           <layer material="g10-TPC"         thickness = "0.3*mm"  vis="gasVis" />
+           <layer material="Kapton"          thickness = "0.05*mm"  vis="KaptonVis" />
+           <layer material="G4_Cu"           thickness = "0.01*mm"  vis="CuVis" />
+           <layer material="epoxy"           thickness = "0.042*mm"  vis="epoxy" /-->
+           <!--CF wall  -->
+           <layer material="CarbonFiber"     thickness = "0.1*mm"    vis="aramidVis"/>
+           <layer material="G4_Cu"           thickness = "0.03*mm"   vis="CuVis"/>
+           <layer material="Polyimide"       thickness = "0.05*mm"   vis="KaptonVis"/>
+           <layer material="G4_Cu"           thickness = "0.03*mm"   vis="CuVis"/>
+           <layer material="CarbonFiber"     thickness = "0.1*mm"    vis="aramidVis"/>
+           <layer material="G4_Cu"           thickness = "0.010*mm"  vis="CuVis"/>
+       </component>
+       <component name="Grip" type="TPCGrip" material="SiC_foam"  vis="SeeThrough">
+           <layer name="TPCinnerGrip" rmin="TPC_rMin_GasVolume"  rmax="TPC_rMin_GasVolume+TPC_dr_InnerServiceArea" z_length="TPC_dz_Cathode_Grip" vis="KaptonVis"/>
+           <layer name="TPCouterGrip" rmin="TPC_rMax_GasVolume-TPC_dr_OuterServiceArea"  rmax="TPC_rMax_GasVolume" z_length="TPC_dz_Cathode_Grip" vis="KaptonVis"/>
+       </component>
+       <component name="Cathode" type="TPCCathode" vis="SeeThrough">
+           <layer name="Cathodeinsulator" material= "Kapton" rmin="TPC_rMin_Sensitive"  rmax="TPC_rMax_Sensitive" z_length="TPC_dz_Cathode_Insulator" vis="KaptonVis">
+                  <position x="0.*mm" y="0.*mm" z="+ TPC_dz_Cathode_Insulator/2."/>
+                  <position x="0.*mm" y="0.*mm" z="- TPC_dz_Cathode_Insulator/2."/>
+           </layer>
+           <layer name="Cathodeconductor" material= "G4_Cu" rmin="TPC_rMin_Sensitive" rmax="TPC_rMax_Sensitive" z_length="TPC_dz_Cathode_Conductor" vis="CuVis">
+                  <position x="0.*mm" y="0.*mm" z="+ (TPC_dz_Cathode_Insulator+ (TPC_dz_Cathode_Conductor/2.))"/>
+                  <position x="0.*mm" y="0.*mm" z="- (TPC_dz_Cathode_Insulator+ (TPC_dz_Cathode_Conductor/2.))"/>
+           </layer>
+       </component>
+       <component name="TPCreadout" type="TPCreadout" material="T2KGas1" vis="SeeThrough">
+           <dimensions rmin = "TPC_rMin_GasVolume" rmax = "TPC_rMax_GasVolume" z_length = "TPC_dz_Readout"/>
+           <position x="0.*mm" y="0.*mm" z="+(TPC_half_length-TPC_dz_Endplate-TPC_dz_Readout/2.)"/>
+           <!--Triple Gem readout structure from CEPCV4-->
+           <!--layer material="G4_Cu"       dz="0.003*mm"  comment="gating" vis="CuVis" />
+           <layer material="G4_KAPTON"   dz="0.030*mm"  comment="gating" vis="KaptonVis"  />
+           <layer material="G4_Cu"       dz="0.003*mm"  comment="gating"    vis="CuVis"/>
+           <layer material="T2KGas1"     dz="4.447*mm"  comment="gating"  vis="gasVis"/>
+           <layer material="G4_Cu"       dz="0.003*mm"  comment="mpgd"      vis="CuVis"/>
+           <layer material="G4_KAPTON"   dz="0.030*mm"  comment="mpgd"   vis="Kapton"/>
+           <layer material="G4_Cu"       dz="0.003*mm"  comment="mpgd"      vis="CuVis"/>
+           <layer material="T2KGas1"     dz="4.447*mm"  comment="mpgd"    vis="gasVis"/>
+           <layer material="G4_Cu"       dz="0.003*mm"  comment="mpgd"      vis="CuVis"/>
+           <layer material="G4_KAPTON"   dz="0.030*mm"  comment="mpgd"   vis="KaptonVis"/>
+           <layer material="G4_Cu"       dz="0.003*mm"  comment="mpgd"      vis="CuVis"/>
+           <layer material="T2KGas1"     dz="4.447*mm"  comment="mpgd"    vis="gasVis"/>
+           <layer material="G4_Cu"       dz="0.050*mm"  comment="pads"       vis="CuVis"/>
+           <layer material="g10"         dz="2.000*mm"  comment="structural"      vis="gasVis"/>
+           <layer material="G4_Si"       dz="0.500*mm"  comment="electronics" vis="epoxyVis" />
+           <layer material="epoxy"       dz="2*mm"      comment="structural"    vis="epoxyVis"/>
+           <layer material="G4_KAPTON"   dz="1*mm"      comment="structural" vis="KaptonVis"  />
+           <layer material="G4_Al"       dz="2*mm"      comment="Cooling"       vis="GrayVis"/>
+           <layer material="G4_KAPTON"   dz="1*mm"      comment="structural"  vis="KaptonVis"/>
+           <layer material="CarbonFiber" dz="3*mm"      comment="structural" vis="GrayVis"  /-->
+           <!--MicroMegas readout structure-->
+           <layer material="G4_Fe"       dz="0.025*mm"  comment="Mesh"           vis="GrayVis"/>
+           <layer material="T2KGas1"     dz="0.128*mm"  comment="GasAmpRegion"   vis="gasVis"/>
+           <layer material="G4_KAPTON"   dz="0.050*mm"  comment="Kaptonfoil"     vis="KaptonVis"/>
+           <layer material="Acrylicglue" dz="0.008*mm"  comment="gule"           vis="BlueVis"/>
+           <layer material="G4_Cu"       dz="0.017*mm"  comment="ReadoutPad"     vis="CuVis"/>
+           <layer material="PCB"         dz="1.000*mm"  comment="PCBboard"       vis="GreenVis"/>
+           <layer material="G4_Si"       dz="0.500*mm"  comment="electronics"    vis="BlackVis"/>
+           <layer material="epoxy"       dz="1.500*mm"  comment="structural"     vis="epoxyVis"/>
+       </component>
+       <component name="TPCSensitiveVol" type="TPCSensitiveVol" material="T2KGas1" vis="gasVis">
+           <dimensions rmin = "TPC_rMin_Sensitive" rmax = "TPC_rMax_Sensitive" z_length = "TPC_dz_Sensitive"/>
+           <layer      repeat="TPC_numberOfPadrows" thickness="TPC_pad_height"/>
+       </component>
+       <component name="TPCEndplate" type="TPCEndplate" z_frame="20.*mm" s_frame="20.*mm" vis="SeeThrough">
+           <dimensions rmin = "TPC_inner_radius" rmax = "TPC_outer_radius" z_length = "TPC_dz_Endplate"/>
+           <layer name="InnerPlate" type="Frame" thickness="25.*mm"/>
+           <layer name="ring1"      type="Frame" thickness="10.*mm"/>
+           <layer name="module1"    type="Module" thickness="151.*mm" repeat="23" phi0_offset="0.*deg">
+             <slice material="PCB"              dz="5.0*mm"  comment="FEE_BEEPCB"/>
+             <slice material="G4_Al"            dz="1.5*mm"  comment="cooling"/>
+             <slice material="TPC_endplate_mix" dz="50.0*mm" comment="MonitorMix"/>
+             <slice material="CarbonFiber"      dz="3.0*mm"  comment="structural"/>
+           </layer>
+           <layer name="ring2"      type="Frame" thickness="20.*mm"/>
+           <layer name="module2"    type="Module" thickness="141.*mm" repeat="27" phi0_offset="+10.*deg">
+             <slice material="PCB"              dz="5.0*mm"  comment="FEE_BEEPCB"/>
+             <slice material="G4_Al"            dz="1.5*mm"  comment="cooling"/>
+             <slice material="TPC_endplate_mix" dz="50.0*mm" comment="MonitorMix"/>
+             <slice material="CarbonFiber"      dz="3.0*mm"  comment="structural"/>
+             <slice material="PCB"   dz="10.0*mm" comment="cooling"/>
+           </layer>
+           <layer name="ring3"      type="Frame" thickness="20.*mm"/>
+           <layer name="module3"    type="Module" thickness="141.*mm" repeat="32" phi0_offset="-5.*deg">
+             <slice material="PCB"              dz="5.0*mm"  comment="FEE_BEEPCB"/>
+             <slice material="G4_Al"            dz="1.5*mm"  comment="cooling"/>
+             <slice material="TPC_endplate_mix" dz="50.0*mm" comment="MonitorMix"/>
+             <slice material="CarbonFiber"      dz="3.0*mm"  comment="structural"/>
+           </layer> 
+           <layer name="ring4"      type="Frame" thickness="20.*mm"/>
+           <layer name="module4"    type="Module" thickness="141.*mm" repeat="34" phi0_offset="0.*deg">
+             <slice material="PCB"              dz="5.0*mm"  comment="FEE_BEEPCB"/>
+             <slice material="G4_Al"            dz="1.5*mm"  comment="cooling"/>
+             <slice material="TPC_endplate_mix" dz="50.0*mm" comment="MonitorMix"/>
+             <slice material="CarbonFiber"      dz="3.0*mm"  comment="structural"/>
+           </layer>
+           <layer name="ring5"      type="Frame" thickness="20.*mm"/>
+           <layer name="module5"    type="Module" thickness="141.*mm" repeat="39" phi0_offset="+5.*deg">
+             <slice material="PCB"              dz="5.0*mm"  comment="FEE_BEEPCB"/>
+             <slice material="G4_Al"            dz="1.5*mm"  comment="cooling"/>
+             <slice material="TPC_endplate_mix" dz="50.0*mm" comment="MonitorMix"/>
+             <slice material="CarbonFiber"      dz="3.0*mm"  comment="structural"/>
+           </layer>
+           <layer name="ring6"      type="Frame" thickness="20.*mm"/>
+           <layer name="module6"    type="Module" thickness="141.*mm" repeat="44" phi0_offset="-5.*deg">
+             <slice material="PCB"              dz="5.0*mm"  comment="FEE_BEEPCB"/>
+             <slice material="G4_Al"            dz="1.5*mm"  comment="cooling"/>
+             <slice material="TPC_endplate_mix" dz="50.0*mm" comment="MonitorMix"/>
+             <slice material="CarbonFiber"      dz="3.0*mm"  comment="structural"/>
+           </layer>
+           <layer name="ring7"      type="Frame" thickness="20.*mm"/>
+           <layer name="module7"    type="Module" thickness="141.*mm" repeat="49" phi0_offset="0.*deg">
+             <slice material="PCB"              dz="5.0*mm"  comment="FEE_BEEPCB"/>
+             <slice material="G4_Al"            dz="1.5*mm"  comment="cooling"/>
+             <slice material="TPC_endplate_mix" dz="50.0*mm" comment="MonitorMix"/>
+             <slice material="CarbonFiber"      dz="3.0*mm"  comment="structural"/>
+           </layer>
+           <layer name="ring8"      type="Frame" thickness="23.*mm"/>
+           <layer name="Outerplate" type="Frame" thickness="25.*mm"/>
+       </component>
+    
+    </detector>
+  </detectors>
+
+  <readouts>
+    <readout name="TPCCollection">
+      <id>system:5,side:-2,layer:13,module:6,sensor:6</id>
+    </readout>
+  </readouts>
+
+</lccdd>
diff --git a/Detector/DetCRD/compact/TDR_o1_v01/TDR_o1_v01_noB.xml b/Detector/DetCRD/compact/TDR_o1_v01/TDR_o1_v01_noB.xml
new file mode 100644
index 00000000..17c87159
--- /dev/null
+++ b/Detector/DetCRD/compact/TDR_o1_v01/TDR_o1_v01_noB.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<lccdd xmlns:compact="http://www.lcsim.org/schemas/compact/1.0"
+       xmlns:xs="http://www.w3.org/2001/XMLSchema"
+       xs:noNamespaceSchemaLocation="http://www.lcsim.org/schemas/compact/1.0/compact.xsd">
+  <info name="TDR_o1_v01"
+        title="CepC reference detctor for TDR"
+        author=""
+        url="http://cepc.ihep.ac.cn"
+        status="developing"
+        version="v01">
+    <comment>CepC reference detector simulation models used for TDR </comment>
+  </info>
+  
+  <includes>
+    <gdmlFile  ref="${DD4hepINSTALL}/DDDetectors/compact/elements.xml"/>
+    <gdmlFile  ref="../CRD_common_v02/materials.xml"/>
+  </includes>
+  
+  <define>
+    <constant name="world_size" value="10*m"/>
+    <constant name="world_x" value="world_size"/>
+    <constant name="world_y" value="world_size"/>
+    <constant name="world_z" value="world_size"/>
+
+    <include ref="${DD4hepINSTALL}/DDDetectors/compact/detector_types.xml"/>
+  </define>
+
+  <include ref="./TDR_Dimensions_v01_01.xml"/>
+
+  <include ref="../CRD_common_v02/Beampipe_v01_07.xml"/>
+  <!--preliminary vertex and tracker, to update/-->
+  <!--include ref="../CRD_common_v02/VXD_StaggeredLadder_v02_01.xml"/-->
+  <include ref="../CRD_common_v02/VXD_Composite_v01_03.xml"/>
+  <!--include ref="../CRD_common_v02/FTD_SkewRing_v01_07.xml"/-->
+  <!--include ref="../CRD_common_v02/SIT_SimplePixel_v01_04.xml"/-->
+  <!--include ref="../CRD_common_v02/SIT_StaggeredStave_v02.xml"/-->
+  <include ref="../CRD_common_v02/ITK_StaggeredStave_v03_01.xml"/>
+  <include ref="../CRD_common_v02/ITK_EndCap_v01.xml"/>
+  <!--include ref="../CRD_common_v01/TPC_Simple_v10_02.xml"/-->
+  <!-- use 10 rows clustering version-->
+  <include ref="../CRD_common_v02/TPC_ModularEndcap_o1_v02.xml"/>
+  <!--include ref="../CRD_common_v01/SET_SimplePixel_v01_01.xml"/-->
+  <!--include ref="../CRD_common_v01/OTKBarrel_v01_01.xml"/-->
+  <include ref="../CRD_common_v01/OTKBarrel_v02.xml"/>
+  <!--include ref="../CRD_common_v01/OTKEndcap_v01_01.xml"/-->
+  <include ref="../CRD_common_v01/OTKEndcap_v02.xml"/>
+
+  <include ref="../CRD_common_v01/Ecal_Crystal_Barrel_v02_02.xml"/>
+  <include ref="../CRD_common_v01/Ecal_Crystal_Endcap_v02_01.xml"/>
+  <include ref="../CRD_common_v01/SHcalGlass_Barrel_v05.xml"/>
+  <include ref="../CRD_common_v01/SHcalGlass_Endcaps_v01.xml"/>
+
+  <!--Lumical to update-->
+  <include ref="../CRD_common_v01/Lumical_o1_v01.xml"/>
+  <!--preliminary Magnet, to update/-->
+  <include ref="../CRD_common_v02/Coil_Simple_v01_03.xml"/>
+  <!--preliminary Muon, obselete/-->
+  <!--include ref="../CRD_common_v02/Yoke_Polyhedra_Barrel_v01_01.xml"/>
+  <include ref="../CRD_common_v02/Yoke_Polyhedra_Endcaps_v01_01.xml"/-->
+ 
+  <!--muon detector-->
+  <include ref="../CRD_common_v01/Muon_Barrel_v01_04.xml"/>
+  <include ref="../CRD_common_v01/Muon_Endcap_v01_02.xml"/>
+  <include ref="../CRD_common_v01/ParaffinEndcap_v01_01.xml"/>
+  <!--include ref="../CRD_common_v01/ConcreteWall_v01_01.xml"/-->
+ 
+  <fields>
+    <field name="InnerSolenoid" type="solenoid"
+           inner_field="0"
+           outer_field="0"
+           zmax="SolenoidCoil_half_length"
+           inner_radius="SolenoidCoil_center_radius"
+           outer_radius="Solenoid_outer_radius">
+    </field>
+    <!-- remove anti magnetic field in order to extrapolate to muon detector more easily-->
+    <!--field name="OuterSolenoid" type="solenoid"
+           inner_field="0"
+           outer_field="Field_outer_nominal_value"
+           zmax="SolenoidCoil_half_length"
+           inner_radius="Solenoid_outer_radius"
+           outer_radius="Yoke_barrel_inner_radius">
+    </field-->
+  </fields>
+
+</lccdd>
diff --git a/Detector/DetCRD/compact/TDR_o1_v01/TDR_o1_v01_simpleTPC.xml b/Detector/DetCRD/compact/TDR_o1_v01/TDR_o1_v01_simpleTPC.xml
new file mode 100644
index 00000000..af967012
--- /dev/null
+++ b/Detector/DetCRD/compact/TDR_o1_v01/TDR_o1_v01_simpleTPC.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<lccdd xmlns:compact="http://www.lcsim.org/schemas/compact/1.0"
+       xmlns:xs="http://www.w3.org/2001/XMLSchema"
+       xs:noNamespaceSchemaLocation="http://www.lcsim.org/schemas/compact/1.0/compact.xsd">
+  <info name="TDR_o1_v01"
+        title="CepC reference detctor for TDR"
+        author=""
+        url="http://cepc.ihep.ac.cn"
+        status="developing"
+        version="v01">
+    <comment>CepC reference detector simulation models used for TDR </comment>
+  </info>
+  
+  <includes>
+    <gdmlFile  ref="${DD4hepINSTALL}/DDDetectors/compact/elements.xml"/>
+    <gdmlFile  ref="../CRD_common_v02/materials.xml"/>
+  </includes>
+  
+  <define>
+    <constant name="world_size" value="10*m"/>
+    <constant name="world_x" value="world_size"/>
+    <constant name="world_y" value="world_size"/>
+    <constant name="world_z" value="world_size"/>
+
+    <include ref="${DD4hepINSTALL}/DDDetectors/compact/detector_types.xml"/>
+  </define>
+
+  <include ref="./TDR_Dimensions_v01_01.xml"/>
+
+  <include ref="../CRD_common_v02/Beampipe_v01_07.xml"/>
+  <!--preliminary vertex and tracker, to update/-->
+  <!--include ref="../CRD_common_v02/VXD_StaggeredLadder_v02_01.xml"/-->
+  <include ref="../CRD_common_v02/VXD_Composite_v01_03.xml"/>
+  <!--include ref="../CRD_common_v02/FTD_SkewRing_v01_07.xml"/-->
+  <!--include ref="../CRD_common_v02/SIT_SimplePixel_v01_04.xml"/-->
+  <!--include ref="../CRD_common_v02/SIT_StaggeredStave_v02.xml"/-->
+  <include ref="../CRD_common_v02/ITK_StaggeredStave_v03_01.xml"/>
+  <include ref="../CRD_common_v02/ITK_EndCap_v01.xml"/>
+  <!--include ref="../CRD_common_v01/TPC_Simple_v10_02.xml"/-->
+  <!-- use 10 rows clustering version-->
+  <include ref="../CRD_common_v02/TPC_ModularEndcap_o1_v02_simplified.xml"/>
+  <!--include ref="../CRD_common_v01/SET_SimplePixel_v01_01.xml"/-->
+  <!--include ref="../CRD_common_v01/OTKBarrel_v01_01.xml"/-->
+  <include ref="../CRD_common_v01/OTKBarrel_v02.xml"/>
+  <!--include ref="../CRD_common_v01/OTKEndcap_v01_01.xml"/-->
+  <include ref="../CRD_common_v01/OTKEndcap_v02.xml"/>
+
+  <include ref="../CRD_common_v01/Ecal_Crystal_Barrel_v02_02.xml"/>
+  <include ref="../CRD_common_v01/Ecal_Crystal_Endcap_v02_01.xml"/>
+  <include ref="../CRD_common_v01/SHcalGlass_Barrel_v05.xml"/>
+  <include ref="../CRD_common_v01/SHcalGlass_Endcaps_v01.xml"/>
+
+  <!--Lumical to update-->
+  <include ref="../CRD_common_v01/Lumical_o1_v01.xml"/>
+  <!--preliminary Magnet, to update/-->
+  <include ref="../CRD_common_v02/Coil_Simple_v01_03.xml"/>
+  <!--preliminary Muon, obselete/-->
+  <!--include ref="../CRD_common_v02/Yoke_Polyhedra_Barrel_v01_01.xml"/>
+  <include ref="../CRD_common_v02/Yoke_Polyhedra_Endcaps_v01_01.xml"/-->
+ 
+  <!--muon detector-->
+  <include ref="../CRD_common_v01/Muon_Barrel_v01_04.xml"/>
+  <include ref="../CRD_common_v01/Muon_Endcap_v01_02.xml"/>
+  <include ref="../CRD_common_v01/ParaffinEndcap_v01_01.xml"/>
+  <!--include ref="../CRD_common_v01/ConcreteWall_v01_01.xml"/-->
+ 
+  <fields>
+    <field name="InnerSolenoid" type="solenoid"
+           inner_field="Field_nominal_value"
+           outer_field="0"
+           zmax="SolenoidCoil_half_length"
+           inner_radius="SolenoidCoil_center_radius"
+           outer_radius="Solenoid_outer_radius">
+    </field>
+    <!-- remove anti magnetic field in order to extrapolate to muon detector more easily-->
+    <!--field name="OuterSolenoid" type="solenoid"
+           inner_field="0"
+           outer_field="Field_outer_nominal_value"
+           zmax="SolenoidCoil_half_length"
+           inner_radius="Solenoid_outer_radius"
+           outer_radius="Yoke_barrel_inner_radius">
+    </field-->
+  </fields>
+
+</lccdd>
diff --git a/Detector/DetCRD/compact/TDR_o1_v01/TDR_o1_v01_wrap.xml b/Detector/DetCRD/compact/TDR_o1_v01/TDR_o1_v01_wrap.xml
new file mode 100644
index 00000000..b0bb96fc
--- /dev/null
+++ b/Detector/DetCRD/compact/TDR_o1_v01/TDR_o1_v01_wrap.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<lccdd xmlns:compact="http://www.lcsim.org/schemas/compact/1.0"
+       xmlns:xs="http://www.w3.org/2001/XMLSchema"
+       xs:noNamespaceSchemaLocation="http://www.lcsim.org/schemas/compact/1.0/compact.xsd">
+  <info name="TDR_o1_v01"
+        title="CepC reference detctor for TDR"
+        author=""
+        url="http://cepc.ihep.ac.cn"
+        status="developing"
+        version="v01">
+    <comment>CepC reference detector simulation models used for TDR </comment>
+  </info>
+  
+  <includes>
+    <gdmlFile  ref="${DD4hepINSTALL}/DDDetectors/compact/elements.xml"/>
+    <gdmlFile  ref="../CRD_common_v02/materials.xml"/>
+  </includes>
+  
+  <define>
+    <constant name="world_size" value="10*m"/>
+    <constant name="world_x" value="world_size"/>
+    <constant name="world_y" value="world_size"/>
+    <constant name="world_z" value="world_size"/>
+
+    <include ref="${DD4hepINSTALL}/DDDetectors/compact/detector_types.xml"/>
+  </define>
+
+  <include ref="./TDR_Dimensions_v01_01.xml"/>
+
+  <include ref="../CRD_common_v02/Beampipe_v01_07.xml"/>
+  <!--preliminary vertex and tracker, to update/-->
+  <!--include ref="../CRD_common_v02/VXD_StaggeredLadder_v02_01.xml"/-->
+  <include ref="../CRD_common_v02/VXD_Composite_v01_03.xml"/>
+  <!--include ref="../CRD_common_v02/FTD_SkewRing_v01_07.xml"/-->
+  <!--include ref="../CRD_common_v02/SIT_SimplePixel_v01_04.xml"/-->
+  <!--include ref="../CRD_common_v02/SIT_StaggeredStave_v02.xml"/-->
+  <include ref="../CRD_common_v02/ITK_StaggeredStave_v03_01.xml"/>
+  <include ref="../CRD_common_v02/ITK_EndCap_v01.xml"/>
+  <!--include ref="../CRD_common_v01/TPC_Simple_v10_02.xml"/-->
+  <!-- use 10 rows clustering version-->
+  <include ref="../CRD_common_v02/TPC_ModularEndcap_o1_v02_wrap.xml"/>
+  <!--include ref="../CRD_common_v01/SET_SimplePixel_v01_01.xml"/-->
+  <!--include ref="../CRD_common_v01/OTKBarrel_v01_01.xml"/-->
+  <include ref="../CRD_common_v01/OTKBarrel_v02.xml"/>
+  <!--include ref="../CRD_common_v01/OTKEndcap_v01_01.xml"/-->
+  <include ref="../CRD_common_v01/OTKEndcap_v02.xml"/>
+
+  <include ref="../CRD_common_v01/Ecal_Crystal_Barrel_v02_02.xml"/>
+  <include ref="../CRD_common_v01/Ecal_Crystal_Endcap_v02_01.xml"/>
+  <include ref="../CRD_common_v01/SHcalGlass_Barrel_v05.xml"/>
+  <include ref="../CRD_common_v01/SHcalGlass_Endcaps_v01.xml"/>
+
+  <!--Lumical to update-->
+  <include ref="../CRD_common_v01/Lumical_o1_v01.xml"/>
+  <!--preliminary Magnet, to update/-->
+  <include ref="../CRD_common_v02/Coil_Simple_v01_03.xml"/>
+  <!--preliminary Muon, obselete/-->
+  <!--include ref="../CRD_common_v02/Yoke_Polyhedra_Barrel_v01_01.xml"/>
+  <include ref="../CRD_common_v02/Yoke_Polyhedra_Endcaps_v01_01.xml"/-->
+ 
+  <!--muon detector-->
+  <include ref="../CRD_common_v01/Muon_Barrel_v01_04.xml"/>
+  <include ref="../CRD_common_v01/Muon_Endcap_v01_02.xml"/>
+  <include ref="../CRD_common_v01/ParaffinEndcap_v01_01.xml"/>
+  <!--include ref="../CRD_common_v01/ConcreteWall_v01_01.xml"/-->
+ 
+  <fields>
+    <field name="InnerSolenoid" type="solenoid"
+           inner_field="Field_nominal_value"
+           outer_field="0"
+           zmax="SolenoidCoil_half_length"
+           inner_radius="SolenoidCoil_center_radius"
+           outer_radius="Solenoid_outer_radius">
+    </field>
+    <!-- remove anti magnetic field in order to extrapolate to muon detector more easily-->
+    <!--field name="OuterSolenoid" type="solenoid"
+           inner_field="0"
+           outer_field="Field_outer_nominal_value"
+           zmax="SolenoidCoil_half_length"
+           inner_radius="Solenoid_outer_radius"
+           outer_radius="Yoke_barrel_inner_radius">
+    </field-->
+  </fields>
+
+</lccdd>
diff --git a/Detector/DetCRD/src/Tracker/TPC_ModularEndcap_o1_v01_simplified.cpp b/Detector/DetCRD/src/Tracker/TPC_ModularEndcap_o1_v01_simplified.cpp
new file mode 100644
index 00000000..c469e68b
--- /dev/null
+++ b/Detector/DetCRD/src/Tracker/TPC_ModularEndcap_o1_v01_simplified.cpp
@@ -0,0 +1,557 @@
+/*********************************************************************
+ * Author           : Lan-sx & origin author: F. Gaede, Desy
+ * Email            : shexin@ihep.ac.cn
+ * Last modified    : 2024-06-02 20:37
+ * Filename         : TPC_ModularEndcap_o1_v01.cpp
+ * Description      : 
+ * ******************************************************************/
+
+#include "DD4hep/DetFactoryHelper.h"
+#include "DD4hep/DD4hepUnits.h"
+#include "DD4hep/DetType.h"
+//#include "./include/LcgeoExceptions.h"
+//#include "./include/lcgeo.h"
+#include "DDRec/Surface.h"
+#include "DDRec/DetectorData.h"
+#include "XML/Utilities.h"
+//#include "XMLHandlerDB.h"
+
+#include <math.h>
+
+using namespace std;
+using namespace dd4hep;
+//using namespace lcgeo;
+
+using dd4hep::rec::Vector3D;
+using dd4hep::rec::VolCylinder;
+using dd4hep::rec::SurfaceType;
+using dd4hep::rec::volSurfaceList;
+using dd4hep::rec::VolPlane;
+using dd4hep::rec::FixedPadSizeTPCData;
+using dd4hep::rec::ConicalSupportData;
+
+/** Construction of TPC detector, ported from Mokka driver TPC10.cc
+ * Mokka History:
+ * - modified version of TPC driver by Ties Behnke
+ * - modified version of TPC02 as TPC03 with selectable chamber gas -- Adrian Vogel, 2005-06-09
+ * - modified version of TPC03 as TPC04 with limit of step length   -- Adrian Vogel, 2006-02-01
+ * - introduced self-scalability, no superdriver needed anymore     -- Adrian Vogel, 2006-03-11
+ * - modified version of TPC04 as TPC05 in order to have full MC
+ *   information both at entry and exit hits in the TPC ,
+ *   more realistic central electrode and endplate             -- Predrag Krstonosic, 2006-07-12
+ * - implemented new GEAR interface -- K. Harder, T. Pinto Jayawardena                2007-07-31
+ * - TPC10 implemented readout within the Gas volume and layered inner and outer wall -- SJA -- 2010-11-19
+ *
+ *  @author: F.Gaede, DESY, Nov 2013
+ *
+ * - Modular Endcap TPC Geo implemention for CEPC TDR TPC
+ *  @author: X.She, IHEP, May 2024
+ */
+
+static Ref_t create_element(Detector& theDetector, xml_h e, SensitiveDetector sens)  {
+
+
+    //------------------------------------------
+    //  See comments starting with '//**' for
+    //     hints on porting issues
+    //------------------------------------------
+    xml_det_t    x_det = e;
+    string       name  = x_det.nameStr();
+
+    DetElement   tpc(  name, x_det.id()  ) ;
+
+    // --- create an envelope volume and position it into the world ---------------------
+
+    Volume envelope = dd4hep::xml::createPlacedEnvelope( theDetector,  e , tpc ) ;
+
+    dd4hep::xml::setDetectorTypeFlag( e, tpc ) ;
+
+    if( theDetector.buildType() == BUILD_ENVELOPE ) return tpc ;
+
+    //-----------------------------------------------------------------------------------
+
+    PlacedVolume pv;  
+
+    if (x_det.hasChild(_U(sensitive))) {
+      xml_dim_t sd_typ = x_det.child(_U(sensitive));
+      sens.setType(sd_typ.typeStr());
+    }
+    else {
+      sens.setType("tracker");
+    }
+
+    std::cout << " **Lan Lan building TPC_ModularEndcap_TDR_o1_v01 construction" << std::endl ;
+
+    //######################################################################################################################################################################
+    const double phi1 =   0.0 ;
+    const double phi2 =  2*M_PI ;
+    const double dzTotal            = theDetector.constant<double>("TPC_half_length") * 2. ; 
+    const double rInner             = theDetector.constant<double>("TPC_inner_radius") ;
+    const double rOuter             = theDetector.constant<double>("TPC_outer_radius") ;
+    const double drInnerWall        = theDetector.constant<double>("TPC_dr_InnerWall");
+    const double drOuterWall        = theDetector.constant<double>("TPC_dr_OuterWall");
+    const double drInnerServiceArea = theDetector.constant<double>("TPC_dr_InnerServiceArea");
+    const double dz_Cathode         = theDetector.constant<double>("TPC_dz_Cathode");
+    const double dz_Endplate        = theDetector.constant<double>("TPC_dz_Endplate");
+    const double dz_Readout         = theDetector.constant<double>("TPC_dz_Readout");
+    const double tpcpadheight       = theDetector.constant<double>("TPC_pad_height");
+    const double tpcpadwidth        = theDetector.constant<double>("TPC_pad_width");
+    const int    tpcnumberOfPadRows = theDetector.constant<int>("TPC_numberOfPadrows");
+
+
+    std::cout<< "============ TPC_HoneyComb_TDR_o1_v01 mother Volume(Tube) (Dz,Ri,Ro) : (" << dzTotal/dd4hep::mm/2. << "\t" 
+                                                                    << rInner/dd4hep::mm  << "\t"
+                                                                    << rOuter/dd4hep::mm  <<" )"<<std::endl;
+    Material materialT2Kgas  = theDetector.material("T2KGas1");
+    Material materialAir     = theDetector.material("Air");
+    Material materialAlframe = theDetector.material("TPC_Alframe");
+    //-------------------------------------------------------------------------------------------------------//
+    //-------------------------------- TPC mother volume ----------------------------------------------------//
+    //------------ Volume for the whole TPC, Field Cage, Cathode, and Endplate and Sensitive ----------------//
+
+    Tube tpc_motherSolid(rInner ,rOuter ,dzTotal/2.0 , phi1 , phi1+phi2 ); 
+    Volume tpc_motherLog(  "TPCLog", tpc_motherSolid, materialT2Kgas);
+    pv = envelope.placeVolume( tpc_motherLog ) ;
+    tpc.setVisAttributes(theDetector,  "TPCMotherVis1" ,  tpc_motherLog ) ;
+
+    double gasRegion = ((rOuter-drOuterWall)-(rInner+drInnerWall))/dd4hep::mm;
+    std::cout << "================================================================"<< std::endl; 
+    std::cout << "TPC_HoneyComb_TDR_o1_v01: Total Gas material corresponds to " << ( ( gasRegion ) / (materialT2Kgas->GetMaterial()->GetRadLen() / dd4hep::mm ) * 100.0 ) 
+              << "% of a radiation length." << std::endl;
+    std::cout << "================================================================"<< std::endl; 
+    
+    //-------------------------------------------------------------------------------------------------------//
+    //Loop all sections
+    //-------------------------------------------------------------------------------------------------------//
+
+    for(xml_coll_t si(x_det, Unicode("component"));si;++si)
+    {
+        xml_comp_t x_section(si);
+        std::string types = x_section.attr<std::string>(_Unicode(type));
+        const std::string volName = x_section.nameStr();
+
+        //-------------------------------- inner/outer wall construction ----------------------------------------//
+        if(types == "TPCinnerWall" || types == "TPCouterWall")
+        {
+            double r_start = x_section.attr<double>(_Unicode(R_start));
+            double r_end =  x_section.attr<double>(_Unicode(R_end));
+            double z_fulllength = x_section.attr<double>(_Unicode(Z_fulllength));
+
+            //Create Inner/Outer Wall mother logic volume
+            std::string volNameLog = volName + "Log";
+            Tube WallSolid(r_start,r_end,z_fulllength/2., phi1 , phi1+phi2);
+            Volume WallLog(volNameLog,WallSolid,materialT2Kgas);
+            pv=tpc_motherLog.placeVolume(WallLog);
+            tpc.setVisAttributes(theDetector,"CyanVis",WallLog);
+            
+            Vector3D ocyl;
+            //SurfaceList data, same as TPC10(CEPCV4)
+            double dr_wall = r_end-r_start;
+            if(types == "TPCinnerWall")
+                ocyl.fill(r_start+0.5*dr_wall,0.,0.);
+            else
+                ocyl.fill(r_end-0.5*dr_wall,0.,0.);
+
+            //std::cout<<"======>   Vector3D cout : "<<ocyl.x()<<"\t"<<ocyl.y()<<"\t"<<ocyl.z()<<std::endl;
+            VolCylinder surfWall(WallLog,SurfaceType( SurfaceType::Helper ),0.5*dr_wall,0.5*dr_wall,ocyl);
+            volSurfaceList( tpc )->push_back( surfWall );
+
+            //Loop all layers of inner/outer wall
+            int ilayer =0;
+            double rCursor = r_start;
+            double fracRadLengthWall = 0.;
+            for(xml_coll_t li(x_section,_U(layer)); li;++li,++ilayer)
+            {
+                xml_comp_t x_layer(li);
+
+                double thickness = x_layer.thickness();
+                Material layerMaterial = theDetector.material( x_layer.materialStr() );
+                char suffix[20];
+                sprintf(suffix,"_%d",ilayer);
+                std::string layerName = volNameLog + suffix;
+
+                Tube layerSolid(rCursor,rCursor+thickness,z_fulllength/2.,phi1,phi1+phi2);
+                Volume layerLog(layerName,layerSolid,layerMaterial);
+                //layerLog.setVisAttributes(theDetector,x_layer.visStr());
+                pv=WallLog.placeVolume(layerLog);
+
+                rCursor += thickness; 
+                double layerRadLength = thickness/(layerMaterial->GetMaterial()->GetRadLen());
+                fracRadLengthWall += layerRadLength; 
+
+                std::cout<<"-> "<<volName<<"layer"<<ilayer<<" : "<< thickness/dd4hep::mm << "mm \t Materials: "<<layerMaterial.name()
+                         <<" X0= "<<layerMaterial->GetMaterial()->GetRadLen()/dd4hep::mm<<"mm \t"
+                         <<layerRadLength<<" X0"<<std::endl;
+            }
+            
+            double drSumThickness = rCursor - r_start;
+            if(drSumThickness > (r_end-r_start))
+            {
+                std::cout<<"Warning!  sum_{i}layerThickness_{i} > drWall !\n"<<std::endl;
+                throw "$!!! TPC_ModularEndcap_TDR_o1_v01: Overfull TPC Wall - check your xml file -component <TPCInnerWall/TPCOuterWall>";
+            }
+
+            std::cout << "================================================================"<< std::endl; 
+            std::cout<<"=====>$ "<<volName<<" material corresponds to "<< int(fracRadLengthWall*1000)/10. << "% of a radiation length."<<std::endl;
+            std::cout<<"=====>$ "<<volName<<" effective X0=  "<<std::setw(4)<< (r_end-r_start)/fracRadLengthWall <<" cm "<<std::endl; 
+            std::cout<<"=====>$ Sum of layer thickness = "<< drSumThickness/dd4hep::mm <<" mm "<<" \t Wall thickness = "<<(r_end-r_start)/dd4hep::mm <<" mm "<<std::endl;
+            std::cout << "================================================================"<< std::endl; 
+        }
+        //-------------------------------- TPCGrip construction ----------------------------------------//
+        if(types == "TPCGrip")
+        {
+            Material gripMaterial = theDetector.material(x_section.attr<std::string>(_Unicode(material))) ;
+            for(xml_coll_t li(x_section,_U(layer));li;++li)
+            {
+                xml_comp_t x_layer(li);
+
+                //std::string volNameLog = x_layer.nameStr()+"Log"; 
+                Tube gripSolid(x_layer.rmin(),x_layer.rmax(),x_layer.z_length()/2., phi1 , phi1+phi2);
+                Volume gripLog(x_layer.nameStr()+"Log",gripSolid,gripMaterial);
+                pv=tpc_motherLog.placeVolume(gripLog);
+                tpc.setVisAttributes(theDetector,x_layer.visStr(),gripLog);
+                std::cout << "================================================================"<< std::endl; 
+                std::cout<<"=====>$ "<<x_layer.nameStr()<<" Constructed ! "<<std::endl;
+                std::cout << "================================================================"<< std::endl; 
+            }
+        }
+        //-------------------------------- TPCCathode construction ----------------------------------------//
+        if(types == "TPCCathode")
+        {
+            for(xml_coll_t li(x_section,_U(layer));li;++li)
+            {
+                xml_comp_t x_layer(li);
+                Material cathodeMaterial = theDetector.material( x_layer.materialStr());
+                
+                Tube cathodeSolid(x_layer.rmin(),x_layer.rmax(),x_layer.z_length()/2,phi1,phi1+phi2);
+                Volume cathodeLog(x_layer.nameStr()+"Log",cathodeSolid,cathodeMaterial);
+
+                for(xml_coll_t pj(x_layer,_U(position));pj;++pj)
+                {
+                    xml_dim_t x_pos(pj);
+                    pv = tpc_motherLog.placeVolume(cathodeLog,Position(x_pos.x(),x_pos.y(),x_pos.z()));
+                    tpc.setVisAttributes(theDetector, x_layer.visStr(),cathodeLog);
+                    std::cout<<"============>Cathod Z Position: "<<x_pos.z() / dd4hep::mm <<" mm "<<std::endl;
+                }
+            }
+        }
+        //-------------------------------- TPC Sensitive Volume construction ----------------------------------------//
+        if(types == "TPCSensitiveVol")
+        {
+            //Material T2KgasMaterial = theDetector.material(x_section.attr<std::string>(_Unicode(material))) ;
+            std::cout<<"============>T2K gas RadLen= "<< materialT2Kgas->GetMaterial()->GetRadLen()/dd4hep::mm<<" mm"<<std::endl;
+
+            xml_dim_t dimSD = x_section.dimensions();
+            std::cout<<"============>rmin,rmax,dz "<< dimSD.rmin()<<"\t"<<dimSD.rmax()<<"\t"<<dimSD.z_length()<<std::endl; 
+
+            Tube sensitiveGasSolid(dimSD.rmin(),dimSD.rmax(),dimSD.z_length()/2.,phi1,phi1+phi2);
+            Volume sensitiveGasLog(volName+"Log",sensitiveGasSolid,materialT2Kgas);
+
+            DetElement sensGasDEfwd(tpc, "tpc_senGas_fwd",x_det.id());
+            DetElement sensGasDEbwd(tpc, "tpc_senGas_bwd",x_det.id());
+
+            pv = tpc_motherLog.placeVolume(sensitiveGasLog,Transform3D(RotationY(0.),Position(0,0,+(dz_Cathode/2+dimSD.z_length()/2.))));
+            pv.addPhysVolID("side",+1);
+            sensGasDEfwd.setPlacement(pv);
+
+            pv = tpc_motherLog.placeVolume(sensitiveGasLog,Transform3D(RotationY(pi),Position(0,0,-(dz_Cathode/2+dimSD.z_length()/2.))));
+            pv.addPhysVolID("side",-1);
+            sensGasDEbwd.setPlacement(pv);
+
+            tpc.setVisAttributes(theDetector, "gasVis", sensitiveGasLog);
+            
+            //Pad row doublets construction
+            xml_coll_t cc(x_section,_U(layer));
+            xml_comp_t x_layer = cc;
+            int numberPadRows = x_layer.repeat();
+            double padHeight  = x_layer.thickness();
+            std::cout<<"##################$$$$$$$$$$$$$$ Number of Pad Rows: > "<<numberPadRows<<"\t padHeight= "<<padHeight/dd4hep::mm<<" mm"<<std::endl;
+            
+            //Sensitive Volume construction  : readout pad layers
+        //     for(int ilayer=0; ilayer < numberPadRows; ++ilayer)
+        //     {
+        //         // create twice the number of rings as there are pads, producing an lower and upper part of the pad with the boundry between them the pad-ring centre
+        //         const double inner_lowerlayer_radius = dimSD.rmin()+ (ilayer * (padHeight));
+        //         const double outer_lowerlayer_radius = inner_lowerlayer_radius + (padHeight/2.0);
+
+        //         const double inner_upperlayer_radius = outer_lowerlayer_radius ;
+        //         const double outer_upperlayer_radius = inner_upperlayer_radius + (padHeight/2.0);
+
+        //         Tube lowerlayerSolid( inner_lowerlayer_radius, outer_lowerlayer_radius, dimSD.z_length() / 2.0, phi1, phi2);
+        //         Tube upperlayerSolid( inner_upperlayer_radius, outer_upperlayer_radius, dimSD.z_length() / 2.0, phi1, phi2);
+
+        //         Volume lowerlayerLog( _toString( ilayer ,"TPC_lowerlayer_log_%02d") ,lowerlayerSolid, materialT2Kgas );
+        //         Volume upperlayerLog( _toString( ilayer ,"TPC_upperlayer_log_%02d") ,upperlayerSolid, materialT2Kgas );
+
+        //         tpc.setVisAttributes(theDetector,  "Invisible" ,  lowerlayerLog) ;
+        //         tpc.setVisAttributes(theDetector,  "Invisible" ,  upperlayerLog) ;
+        //         //tpc.setVisAttributes(theDetector,  "RedVis" ,  lowerlayerLog) ;
+        //         //tpc.setVisAttributes(theDetector,  "RedVis" ,  upperlayerLog) ;
+
+        //         DetElement   layerDEfwd( sensGasDEfwd ,   _toString( ilayer, "tpc_row_fwd_%03d") , x_det.id() );
+        //         DetElement   layerDEbwd( sensGasDEbwd ,   _toString( ilayer, "tpc_row_bwd_%03d") , x_det.id() );
+
+        //         Vector3D o(  inner_upperlayer_radius + 1e-10  , 0. , 0. ) ;
+        //         // create an unbounded surface (i.e. an infinite cylinder) and assign it to the forward gaseous volume only
+        //         VolCylinder surf( upperlayerLog , SurfaceType(SurfaceType::Sensitive, SurfaceType::Invisible, SurfaceType::Unbounded ) ,  (padHeight/2.0) ,  (padHeight/2.0) ,o ) ;
+
+        //         volSurfaceList( layerDEfwd )->push_back( surf ) ;
+        //         // volSurfaceList( layerDEbwd )->push_back( surf ) ;
+
+        //         pv = sensitiveGasLog.placeVolume( lowerlayerLog ) ;
+        //         pv.addPhysVolID("layer", ilayer ).addPhysVolID( "module", 0 ).addPhysVolID("sensor", 1 ) ;
+
+        //         pv = sensitiveGasLog.placeVolume( upperlayerLog ) ;
+        //         pv.addPhysVolID("layer", ilayer ).addPhysVolID( "module", 0 ).addPhysVolID("sensor", 0 ) ;
+        //         layerDEfwd.setPlacement( pv ) ;
+        //         layerDEbwd.setPlacement( pv ) ;
+
+        //         lowerlayerLog.setSensitiveDetector(sens);
+        //         upperlayerLog.setSensitiveDetector(sens);
+		// if (x_det.hasAttr(_U(limits))) {
+		//   lowerlayerLog.setLimitSet(theDetector, x_det.limitsStr());
+		//   upperlayerLog.setLimitSet(theDetector, x_det.limitsStr());
+		// }
+        //     }
+
+        }
+        //-------------------------------- TPC Endplate and readout construction ----------------------------------------//
+        if(types == "TPCEndplate")
+        {
+            xml_dim_t dimEndCap = x_section.dimensions();
+            std::cout<<"============>(rmin,rmax,dz): "<< dimEndCap.rmin() / dd4hep::mm<<"mm "
+                                                      << dimEndCap.rmax() / dd4hep::mm<<" mm "
+                                                      << dimEndCap.z_length() / dd4hep::mm<< " mm" <<std::endl; 
+            //Create endcap Log volume
+            Tube endcapSolid(dimEndCap.rmin(),dimEndCap.rmax(),dimEndCap.z_length()/2.,phi1,phi1+phi2);
+            Volume endcapLog(volName+"Log",endcapSolid,materialAir);
+
+            DetElement endcapDEfwd(tpc, "tpc_endcap_fwd",x_det.id());
+            DetElement endcapDEbwd(tpc, "tpc_endcap_bwd",x_det.id());
+
+            //Vectors for endplate plane
+            Vector3D u(0.,1.,0.);
+            Vector3D v(1.,0.,0.);
+            Vector3D n(0.,0.,1.);
+
+            ////need to set the origin of the helper plane to be inside the material (otherwise it would pick up the vacuum at the origin)
+            double mid_r = 0.5*(rOuter + rInner);
+            Vector3D o(0., mid_r, 0.);
+
+            VolPlane surf( endcapLog, SurfaceType( SurfaceType::Helper ), (dz_Endplate+dz_Readout)/2.,dz_Endplate/2.,u,v,n,o);
+            volSurfaceList(endcapDEfwd) -> push_back(surf);
+            volSurfaceList(endcapDEbwd) -> push_back(surf);
+            
+            pv = tpc_motherLog.placeVolume(endcapLog,Transform3D(RotationY(0.),Position(0,0,+(dzTotal/2.-dz_Endplate/2.))));
+            endcapDEfwd.setPlacement(pv);
+
+            pv = tpc_motherLog.placeVolume(endcapLog,Transform3D(RotationY(pi),Position(0,0,-(dzTotal/2.-dz_Endplate/2.))));
+            endcapDEbwd.setPlacement(pv);
+            
+            tpc.setVisAttributes(theDetector, "transVis", endcapLog);
+
+            //================================================================================================
+            //Modular Endplate construction
+            //================================================================================================
+            double dz_Endpaltelength     = dimEndCap.z_length();
+            double r_start        = dimEndCap.rmin();  
+            double ds_reinforce   = x_section.attr<double>(_Unicode(s_frame));
+            double dz_Alframe     = x_section.attr<double>(_Unicode(z_frame));
+            double rCursor        = r_start;
+
+            //Loop all layers to construct frame-module
+            for(xml_coll_t ilayer(x_section,_U(layer)); ilayer; ++ilayer)
+            {
+                xml_comp_t x_layer(ilayer);
+
+                const std::string layerName = x_layer.nameStr();
+                const std::string layerType = x_layer.attr<std::string>(_Unicode(type));
+                double layerThickness       = x_layer.thickness();
+
+                double r_end = rCursor + layerThickness;
+                std::cout<<"===============>$ "<<layerName<<"\t"<<layerType
+                    <<" thickness = "<<layerThickness / dd4hep::mm << "mm "
+                    <<" inner radius = "<<rCursor / dd4hep::mm<<" mm"
+                    <<" outer radius = "<<r_end /dd4hep::mm<<" mm"
+                    <<std::endl;
+
+                //--------------------------------------
+                if(layerType == "Frame")
+                {
+                    double phi_start = 0.;
+                    double phi_end   = 2*M_PI;
+                    Tube   ringSolid(rCursor, r_end, dz_Alframe/2., phi_start,  phi_end) ;
+                    Volume ringLog( layerName+"Log", ringSolid, materialAlframe) ;
+                    pv = endcapLog.placeVolume( ringLog, Position(0., 0., -dz_Endpaltelength/2. + dz_Alframe/2.) ) ;
+                    tpc.setVisAttributes(theDetector,"GrayVis",ringLog);
+                }
+                if(layerType == "Module")
+                {
+                    int numberofModules = x_layer.repeat(); 
+                    double phi_start    = x_layer.phi0_offset();
+                    double phiCursor    = phi_start;
+                    double phiModule    = (2*M_PI*rCursor-numberofModules*ds_reinforce)/numberofModules/rCursor;
+                    double phiReinforce = ds_reinforce/rCursor;
+
+                    //Construct each module 
+                    for(int k=0; k<numberofModules;++k)
+                    {
+                        Tube moduleSolid1(rCursor,r_end,dz_Endpaltelength/2.,phiCursor,phiCursor+phiModule);
+                        std::string moduleLogName1 = layerName + _toString(k,"Log%00d");
+                        Volume moduleLog1(moduleLogName1,moduleSolid1,materialAir);
+
+                        double z_cursor = -dz_Endpaltelength/2.; 
+                        int    m_sli = 0;
+                        for(xml_coll_t sli(x_layer,_U(slice)); sli; ++sli,++m_sli)
+                        {
+                            xml_comp_t x_slice = sli; 
+                            double dz_modulepiece = sli.attr<double>(_Unicode(dz));
+                            std::string moduleSliceName = moduleLogName1 + _toString(m_sli,"slice%d");
+                            Material slice_mat = theDetector.material(x_slice.materialStr());
+
+                            Tube moduleSliceSolid(rCursor,r_end,dz_modulepiece/2.,phiCursor,phiCursor+phiModule);
+                            Volume moduleSliceLog(moduleSliceName,moduleSliceSolid,slice_mat);
+
+                            pv = moduleLog1.placeVolume(moduleSliceLog,Position(0.,0.,z_cursor+dz_modulepiece/2.));
+                            tpc.setVisAttributes(theDetector,  "Invisible" ,  moduleSliceLog) ;
+                            z_cursor += dz_modulepiece;
+
+                            if(z_cursor > dz_Endpaltelength/2.)
+                            {
+                                //std::cout<<" Warning ! TPC_ModularEndcap_TDR_o1_v01: Overfull TPC Module- check your xml file - section <Endpalte>." <<std::endl;
+                                throw " $!!! TPC_ModularEndcap_TDR_o1_v01: Overfull TPC Module- check your xml file - component <Endpalte>.";
+                            }
+                        }
+
+                        pv = endcapLog.placeVolume(moduleLog1);
+                        tpc.setVisAttributes(theDetector,"RedVis",moduleLog1);
+
+                        phiCursor = phiCursor+phiModule;
+
+                        //Construct the Al frame between each module 
+                        Tube moduleSolid2(rCursor,r_end,dz_Alframe/2.,phiCursor,phiCursor+phiReinforce);
+                        std::string moduleLogName2 = layerName + _toString(k,"Log_rein%00d");
+                        Volume moduleLog2(moduleLogName2,moduleSolid2,materialAlframe);
+                        pv = endcapLog.placeVolume(moduleLog2, Position(0., 0., -dz_Endpaltelength/2.+dz_Alframe/2.));
+                        tpc.setVisAttributes(theDetector,"GrayVis",moduleLog2);
+
+                        phiCursor = phiCursor+phiReinforce;
+                    }
+                }
+                rCursor = r_end;
+            }
+
+            double RadlenOfAl_Frame = materialAlframe->GetMaterial()->GetRadLen();
+            std::cout << "================================================================"<< std::endl; 
+            std::cout << "TPC_ModularEndcap_TDR_o1_v01: Endplate Al frame corresponds to " << (2. / RadlenOfAl_Frame*100)<< "% of a radiation length." << std::endl;
+            std::cout << "================================================================"<< std::endl; 
+        }
+        //-------------------------------- TPCreadout construction ----------------------------------------//
+        if(types == "TPCreadout")
+        {
+            xml_dim_t dimReadout = x_section.dimensions();
+            double dzReadout = dimReadout.z_length();
+            Tube readoutSolid(dimReadout.rmin(),dimReadout.rmax(),dimReadout.z_length()/2.,phi1,phi1+phi2);
+            Volume readoutLog(volName+"Log",readoutSolid, materialT2Kgas);
+
+            tpc.setVisAttributes(theDetector,"CyanVis",readoutLog);
+
+            xml_dim_t posReadout = x_section.position();
+            pv = tpc_motherLog.placeVolume(readoutLog,Transform3D(RotationY(0.),Position(0,0,posReadout.z())));  
+            pv = tpc_motherLog.placeVolume(readoutLog,Transform3D(RotationY(pi),Position(0,0,-posReadout.z())));  
+
+            std::cout<<"=========ReadOut dim: "<< dimReadout.rmin() / dd4hep::mm<<" mm "
+                                               << dimReadout.rmax() / dd4hep::mm<<" mm "
+                                               << dzReadout / dd4hep::mm <<" mm "<<std::endl;
+            std::cout<<"=========ReadOut Z_pos: "<<posReadout.z() / dd4hep::mm << " mm "<<std::endl;
+
+            int pieceCounter = 0;
+            double fracRadLengthReadout = 0;
+            double zCursor = -dzReadout/ 2;
+
+            for(xml_coll_t li(x_section,_U(layer)); li;++li)
+            {
+                xml_comp_t  x_layer( li );
+            
+                const double dzPiece = x_layer.attr<double>(_Unicode(dz)); 
+                Material pieceMaterial = theDetector.material( x_layer.materialStr() );
+                Tube pieceSolid( dimReadout.rmin(),dimReadout.rmax(), dzPiece / 2, phi1, phi2);
+                Volume pieceLog (  _toString( pieceCounter ,"TPCReadoutPieceLog_%02d"), pieceSolid, pieceMaterial ) ;
+
+                pieceLog.setVisAttributes(theDetector,x_layer.visStr());
+
+                pv = readoutLog.placeVolume( pieceLog  , Position(0, 0,  zCursor + dzPiece/2. ) ) ;
+
+                ++pieceCounter;
+                fracRadLengthReadout += dzPiece / pieceMaterial->GetMaterial()->GetRadLen();
+                zCursor += dzPiece;
+                
+                std::cout<<"==========> "<<dzPiece/dd4hep::mm<<" mm Material= "<<x_layer.materialStr()<<"\t"
+                         <<"X0= "<<pieceMaterial->GetMaterial()->GetRadLen()/dd4hep::mm<<"\t"
+                         <<dzPiece / pieceMaterial->GetMaterial()->GetRadLen()  <<" X0"<< std::endl;
+
+                if (zCursor > +dzReadout / 2) 
+                {
+                    //throw GeometryException(  "TPC11: Overfull TPC readout - check your xml file - section <readout>."   ) ;
+                    //std::cout<<" TPC_ModularEndcap_TDR_o1_v01: Overfull TPC readout - check your xml file - component <TPCReadout>." <<std::endl;
+                    throw " $!!! TPC_ModularEndcap_TDR_o1_v01: Overfull TPC readout - check your xml file - component <TPCReadout>.";
+                }
+            }
+            std::cout << "================================================================"<< std::endl; 
+            std::cout << "TPC_ModularEndcap_TDR_o1_v01: Readout material corresponds to " << int(fracRadLengthReadout * 1000) / 10.0 << "% of a radiation length." << std::endl;
+            std::cout << "================================================================"<< std::endl; 
+        }
+
+    }
+    
+    //TPC data
+    FixedPadSizeTPCData* tpcData = new FixedPadSizeTPCData();
+    tpcData->zHalf = dzTotal/2.;
+    tpcData->rMin  = rInner;
+    tpcData->rMax  = rOuter;
+    tpcData->innerWallThickness = drInnerWall;
+    tpcData->outerWallThickness = drOuterWall;
+    tpcData->rMinReadout = rInner + drInnerWall + drInnerServiceArea;
+    tpcData->rMaxReadout = rInner + drInnerWall + drInnerServiceArea + tpcnumberOfPadRows*tpcpadheight;
+    tpcData->maxRow = tpcnumberOfPadRows;
+    tpcData->padHeight = tpcpadheight;
+    tpcData->padWidth =  tpcpadwidth;
+    tpcData->driftLength = dzTotal/2.- dz_Endplate - dz_Readout - dz_Cathode/2.0; // SJA: cathode has to be added as the sensitive region does not start at 0.00    
+    tpcData->zMinReadout = dz_Cathode/2.0; 
+
+    ConicalSupportData* supportData = new ConicalSupportData;
+
+    ConicalSupportData::Section section0;
+    section0.rInner = rInner + drInnerWall; 
+    section0.rOuter = rOuter - drOuterWall;
+    section0.zPos   = dzTotal/2. - dz_Endplate - dz_Readout; 
+
+    ConicalSupportData::Section section1;
+    section1.rInner = rInner + drInnerWall; 
+    section1.rOuter = rOuter - drOuterWall;
+    section1.zPos   = dzTotal/2. - dz_Endplate; 
+
+    ConicalSupportData::Section section2;
+    section2.rInner = rInner ; 
+    section2.rOuter = rOuter ;
+    section2.zPos   = dzTotal/2.; 
+
+    supportData->sections.push_back(section0);
+    supportData->sections.push_back(section1);
+    supportData->sections.push_back(section2);
+
+
+    tpc.addExtension< FixedPadSizeTPCData >(tpcData);
+    tpc.addExtension< ConicalSupportData >(supportData);
+
+    //tpc.setVisAttributes( theDetector, x_det.visStr(), envelope );
+    tpc.setVisAttributes( theDetector, "TPCMotherVis1", envelope );
+    //  if( tpc.isValid() ) 
+    // tpc.setPlacement(pv);
+    std::cout << "================================================================"<< std::endl; 
+    std::cout << "TPC_ModularEndcap_TDR_o1_v01 Constructed!"<< std::endl; 
+    std::cout << "================================================================"<< std::endl; 
+
+    return tpc;
+}
+DECLARE_DETELEMENT(TPC_ModularEndcap_o1_v01_simplified,create_element)
diff --git a/Detector/DetCRD/src/Tracker/TPC_ModularEndcap_o1_v01_wrapvolume.cpp b/Detector/DetCRD/src/Tracker/TPC_ModularEndcap_o1_v01_wrapvolume.cpp
new file mode 100644
index 00000000..4702cee0
--- /dev/null
+++ b/Detector/DetCRD/src/Tracker/TPC_ModularEndcap_o1_v01_wrapvolume.cpp
@@ -0,0 +1,582 @@
+/*********************************************************************
+ * Author           : Lan-sx & origin author: F. Gaede, Desy
+ * Email            : shexin@ihep.ac.cn
+ * Last modified    : 2024-06-02 20:37
+ * Filename         : TPC_ModularEndcap_o1_v01.cpp
+ * Description      : 
+ * ******************************************************************/
+
+#include "DD4hep/DetFactoryHelper.h"
+#include "DD4hep/DD4hepUnits.h"
+#include "DD4hep/DetType.h"
+//#include "./include/LcgeoExceptions.h"
+//#include "./include/lcgeo.h"
+#include "DDRec/Surface.h"
+#include "DDRec/DetectorData.h"
+#include "XML/Utilities.h"
+//#include "XMLHandlerDB.h"
+
+#include <math.h>
+
+using namespace std;
+using namespace dd4hep;
+//using namespace lcgeo;
+
+using dd4hep::rec::Vector3D;
+using dd4hep::rec::VolCylinder;
+using dd4hep::rec::SurfaceType;
+using dd4hep::rec::volSurfaceList;
+using dd4hep::rec::VolPlane;
+using dd4hep::rec::FixedPadSizeTPCData;
+using dd4hep::rec::ConicalSupportData;
+
+/** Construction of TPC detector, ported from Mokka driver TPC10.cc
+ * Mokka History:
+ * - modified version of TPC driver by Ties Behnke
+ * - modified version of TPC02 as TPC03 with selectable chamber gas -- Adrian Vogel, 2005-06-09
+ * - modified version of TPC03 as TPC04 with limit of step length   -- Adrian Vogel, 2006-02-01
+ * - introduced self-scalability, no superdriver needed anymore     -- Adrian Vogel, 2006-03-11
+ * - modified version of TPC04 as TPC05 in order to have full MC
+ *   information both at entry and exit hits in the TPC ,
+ *   more realistic central electrode and endplate             -- Predrag Krstonosic, 2006-07-12
+ * - implemented new GEAR interface -- K. Harder, T. Pinto Jayawardena                2007-07-31
+ * - TPC10 implemented readout within the Gas volume and layered inner and outer wall -- SJA -- 2010-11-19
+ *
+ *  @author: F.Gaede, DESY, Nov 2013
+ *
+ * - Modular Endcap TPC Geo implemention for CEPC TDR TPC
+ *  @author: X.She, IHEP, May 2024
+ */
+
+static Ref_t create_element(Detector& theDetector, xml_h e, SensitiveDetector sens)  {
+
+
+    //------------------------------------------
+    //  See comments starting with '//**' for
+    //     hints on porting issues
+    //------------------------------------------
+    xml_det_t    x_det = e;
+    string       name  = x_det.nameStr();
+
+    DetElement   tpc(  name, x_det.id()  ) ;
+
+    // --- create an envelope volume and position it into the world ---------------------
+
+    Volume envelope = dd4hep::xml::createPlacedEnvelope( theDetector,  e , tpc ) ;
+
+    dd4hep::xml::setDetectorTypeFlag( e, tpc ) ;
+
+    if( theDetector.buildType() == BUILD_ENVELOPE ) return tpc ;
+
+    //-----------------------------------------------------------------------------------
+
+    PlacedVolume pv;  
+
+    if (x_det.hasChild(_U(sensitive))) {
+      xml_dim_t sd_typ = x_det.child(_U(sensitive));
+      sens.setType(sd_typ.typeStr());
+    }
+    else {
+      sens.setType("tracker");
+    }
+
+    std::cout << " **Lan Lan building TPC_ModularEndcap_TDR_o1_v01 construction" << std::endl ;
+
+    //######################################################################################################################################################################
+    const double phi1 =   0.0 ;
+    const double phi2 =  2*M_PI ;
+    const double dzTotal            = theDetector.constant<double>("TPC_half_length") * 2. ; 
+    const double rInner             = theDetector.constant<double>("TPC_inner_radius") ;
+    const double rOuter             = theDetector.constant<double>("TPC_outer_radius") ;
+    const double drInnerWall        = theDetector.constant<double>("TPC_dr_InnerWall");
+    const double drOuterWall        = theDetector.constant<double>("TPC_dr_OuterWall");
+    const double drInnerServiceArea = theDetector.constant<double>("TPC_dr_InnerServiceArea");
+    const double dz_Cathode         = theDetector.constant<double>("TPC_dz_Cathode");
+    const double dz_Endplate        = theDetector.constant<double>("TPC_dz_Endplate");
+    const double dz_Readout         = theDetector.constant<double>("TPC_dz_Readout");
+    const double tpcpadheight       = theDetector.constant<double>("TPC_pad_height");
+    const double tpcpadwidth        = theDetector.constant<double>("TPC_pad_width");
+    const int    tpcnumberOfPadRows = theDetector.constant<int>("TPC_numberOfPadrows");
+
+
+    std::cout<< "============ TPC_HoneyComb_TDR_o1_v01 mother Volume(Tube) (Dz,Ri,Ro) : (" << dzTotal/dd4hep::mm/2. << "\t" 
+                                                                    << rInner/dd4hep::mm  << "\t"
+                                                                    << rOuter/dd4hep::mm  <<" )"<<std::endl;
+    Material materialT2Kgas  = theDetector.material("T2KGas1");
+    Material materialAir     = theDetector.material("Air");
+    Material materialAlframe = theDetector.material("TPC_Alframe");
+    //-------------------------------------------------------------------------------------------------------//
+    //-------------------------------- TPC mother volume ----------------------------------------------------//
+    //------------ Volume for the whole TPC, Field Cage, Cathode, and Endplate and Sensitive ----------------//
+
+    Tube tpc_motherSolid(rInner ,rOuter ,dzTotal/2.0 , phi1 , phi1+phi2 ); 
+    Volume tpc_motherLog(  "TPCLog", tpc_motherSolid, materialT2Kgas);
+    pv = envelope.placeVolume( tpc_motherLog ) ;
+    tpc.setVisAttributes(theDetector,  "TPCMotherVis1" ,  tpc_motherLog ) ;
+
+    double gasRegion = ((rOuter-drOuterWall)-(rInner+drInnerWall))/dd4hep::mm;
+    std::cout << "================================================================"<< std::endl; 
+    std::cout << "TPC_HoneyComb_TDR_o1_v01: Total Gas material corresponds to " << ( ( gasRegion ) / (materialT2Kgas->GetMaterial()->GetRadLen() / dd4hep::mm ) * 100.0 ) 
+              << "% of a radiation length." << std::endl;
+    std::cout << "================================================================"<< std::endl; 
+    
+    //-------------------------------------------------------------------------------------------------------//
+    //Loop all sections
+    //-------------------------------------------------------------------------------------------------------//
+
+    for(xml_coll_t si(x_det, Unicode("component"));si;++si)
+    {
+        xml_comp_t x_section(si);
+        std::string types = x_section.attr<std::string>(_Unicode(type));
+        const std::string volName = x_section.nameStr();
+
+        //-------------------------------- inner/outer wall construction ----------------------------------------//
+        if(types == "TPCinnerWall" || types == "TPCouterWall")
+        {
+            double r_start = x_section.attr<double>(_Unicode(R_start));
+            double r_end =  x_section.attr<double>(_Unicode(R_end));
+            double z_fulllength = x_section.attr<double>(_Unicode(Z_fulllength));
+
+            //Create Inner/Outer Wall mother logic volume
+            std::string volNameLog = volName + "Log";
+            Tube WallSolid(r_start,r_end,z_fulllength/2., phi1 , phi1+phi2);
+            Volume WallLog(volNameLog,WallSolid,materialT2Kgas);
+            pv=tpc_motherLog.placeVolume(WallLog);
+            tpc.setVisAttributes(theDetector,"CyanVis",WallLog);
+            
+            Vector3D ocyl;
+            //SurfaceList data, same as TPC10(CEPCV4)
+            double dr_wall = r_end-r_start;
+            if(types == "TPCinnerWall")
+                ocyl.fill(r_start+0.5*dr_wall,0.,0.);
+            else
+                ocyl.fill(r_end-0.5*dr_wall,0.,0.);
+
+            //std::cout<<"======>   Vector3D cout : "<<ocyl.x()<<"\t"<<ocyl.y()<<"\t"<<ocyl.z()<<std::endl;
+            VolCylinder surfWall(WallLog,SurfaceType( SurfaceType::Helper ),0.5*dr_wall,0.5*dr_wall,ocyl);
+            volSurfaceList( tpc )->push_back( surfWall );
+
+            //Loop all layers of inner/outer wall
+            int ilayer =0;
+            double rCursor = r_start;
+            double fracRadLengthWall = 0.;
+            for(xml_coll_t li(x_section,_U(layer)); li;++li,++ilayer)
+            {
+                xml_comp_t x_layer(li);
+
+                double thickness = x_layer.thickness();
+                Material layerMaterial = theDetector.material( x_layer.materialStr() );
+                char suffix[20];
+                sprintf(suffix,"_%d",ilayer);
+                std::string layerName = volNameLog + suffix;
+
+                Tube layerSolid(rCursor,rCursor+thickness,z_fulllength/2.,phi1,phi1+phi2);
+                Volume layerLog(layerName,layerSolid,layerMaterial);
+                //layerLog.setVisAttributes(theDetector,x_layer.visStr());
+                pv=WallLog.placeVolume(layerLog);
+
+                rCursor += thickness; 
+                double layerRadLength = thickness/(layerMaterial->GetMaterial()->GetRadLen());
+                fracRadLengthWall += layerRadLength; 
+
+                std::cout<<"-> "<<volName<<"layer"<<ilayer<<" : "<< thickness/dd4hep::mm << "mm \t Materials: "<<layerMaterial.name()
+                         <<" X0= "<<layerMaterial->GetMaterial()->GetRadLen()/dd4hep::mm<<"mm \t"
+                         <<layerRadLength<<" X0"<<std::endl;
+            }
+            
+            double drSumThickness = rCursor - r_start;
+            if(drSumThickness > (r_end-r_start))
+            {
+                std::cout<<"Warning!  sum_{i}layerThickness_{i} > drWall !\n"<<std::endl;
+                throw "$!!! TPC_ModularEndcap_TDR_o1_v01: Overfull TPC Wall - check your xml file -component <TPCInnerWall/TPCOuterWall>";
+            }
+
+            std::cout << "================================================================"<< std::endl; 
+            std::cout<<"=====>$ "<<volName<<" material corresponds to "<< int(fracRadLengthWall*1000)/10. << "% of a radiation length."<<std::endl;
+            std::cout<<"=====>$ "<<volName<<" effective X0=  "<<std::setw(4)<< (r_end-r_start)/fracRadLengthWall <<" cm "<<std::endl; 
+            std::cout<<"=====>$ Sum of layer thickness = "<< drSumThickness/dd4hep::mm <<" mm "<<" \t Wall thickness = "<<(r_end-r_start)/dd4hep::mm <<" mm "<<std::endl;
+            std::cout << "================================================================"<< std::endl; 
+        }
+        //-------------------------------- TPCGrip construction ----------------------------------------//
+        if(types == "TPCGrip")
+        {
+            Material gripMaterial = theDetector.material(x_section.attr<std::string>(_Unicode(material))) ;
+            for(xml_coll_t li(x_section,_U(layer));li;++li)
+            {
+                xml_comp_t x_layer(li);
+
+                //std::string volNameLog = x_layer.nameStr()+"Log"; 
+                Tube gripSolid(x_layer.rmin(),x_layer.rmax(),x_layer.z_length()/2., phi1 , phi1+phi2);
+                Volume gripLog(x_layer.nameStr()+"Log",gripSolid,gripMaterial);
+                pv=tpc_motherLog.placeVolume(gripLog);
+                tpc.setVisAttributes(theDetector,x_layer.visStr(),gripLog);
+                std::cout << "================================================================"<< std::endl; 
+                std::cout<<"=====>$ "<<x_layer.nameStr()<<" Constructed ! "<<std::endl;
+                std::cout << "================================================================"<< std::endl; 
+            }
+        }
+        //-------------------------------- TPCCathode construction ----------------------------------------//
+        if(types == "TPCCathode")
+        {
+            for(xml_coll_t li(x_section,_U(layer));li;++li)
+            {
+                xml_comp_t x_layer(li);
+                Material cathodeMaterial = theDetector.material( x_layer.materialStr());
+                
+                Tube cathodeSolid(x_layer.rmin(),x_layer.rmax(),x_layer.z_length()/2,phi1,phi1+phi2);
+                Volume cathodeLog(x_layer.nameStr()+"Log",cathodeSolid,cathodeMaterial);
+
+                for(xml_coll_t pj(x_layer,_U(position));pj;++pj)
+                {
+                    xml_dim_t x_pos(pj);
+                    pv = tpc_motherLog.placeVolume(cathodeLog,Position(x_pos.x(),x_pos.y(),x_pos.z()));
+                    tpc.setVisAttributes(theDetector, x_layer.visStr(),cathodeLog);
+                    std::cout<<"============>Cathod Z Position: "<<x_pos.z() / dd4hep::mm <<" mm "<<std::endl;
+                }
+            }
+        }
+        //-------------------------------- TPC Sensitive Volume construction ----------------------------------------//
+        if(types == "TPCSensitiveVol")
+        {
+            //Material T2KgasMaterial = theDetector.material(x_section.attr<std::string>(_Unicode(material))) ;
+            std::cout<<"============>T2K gas RadLen= "<< materialT2Kgas->GetMaterial()->GetRadLen()/dd4hep::mm<<" mm"<<std::endl;
+
+            xml_dim_t dimSD = x_section.dimensions();
+            std::cout<<"============>rmin,rmax,dz "<< dimSD.rmin()<<"\t"<<dimSD.rmax()<<"\t"<<dimSD.z_length()<<std::endl; 
+
+            Tube sensitiveGasSolid(dimSD.rmin(),dimSD.rmax(),dimSD.z_length()/2.,phi1,phi1+phi2);
+            Volume sensitiveGasLog(volName+"Log",sensitiveGasSolid,materialT2Kgas);
+
+            DetElement sensGasDEfwd(tpc, "tpc_senGas_fwd",x_det.id());
+            DetElement sensGasDEbwd(tpc, "tpc_senGas_bwd",x_det.id());
+
+            pv = tpc_motherLog.placeVolume(sensitiveGasLog,Transform3D(RotationY(0.),Position(0,0,+(dz_Cathode/2+dimSD.z_length()/2.))));
+            pv.addPhysVolID("side",+1);
+            sensGasDEfwd.setPlacement(pv);
+
+            pv = tpc_motherLog.placeVolume(sensitiveGasLog,Transform3D(RotationY(pi),Position(0,0,-(dz_Cathode/2+dimSD.z_length()/2.))));
+            pv.addPhysVolID("side",-1);
+            sensGasDEbwd.setPlacement(pv);
+
+            tpc.setVisAttributes(theDetector, "gasVis", sensitiveGasLog);
+            
+            //Pad row doublets construction
+            xml_coll_t cc(x_section,_U(layer));
+            xml_comp_t x_layer = cc;
+            int numberPadRows = x_layer.repeat();
+            double padHeight  = x_layer.thickness();
+            std::cout<<"##################$$$$$$$$$$$$$$ Number of Pad Rows: > "<<numberPadRows<<"\t padHeight= "<<padHeight/dd4hep::mm<<" mm"<<std::endl;
+            
+            const int layersPerGroup = 10;
+            std::vector<Volume> groupVolumes;
+            for (int grp = 0; grp < numberPadRows; grp += layersPerGroup) {
+              // Determine span for this group
+              const int count = std::min(layersPerGroup, numberPadRows - grp);
+              const double r_inner = dimSD.rmin() + grp * padHeight;
+              const double r_outer = r_inner + count * padHeight;
+
+              Tube groupSolid(r_inner, r_outer, dimSD.z_length() / 2.0, phi1,
+                              phi2);
+              Volume groupLog(_toString(grp / layersPerGroup, "TPC_group_%02d"),
+                              groupSolid, materialT2Kgas);
+
+              // Optionally set visualization
+            //   tpc.setVisAttributes(theDetector, "BlueVisSemiTransparent",
+            //                        groupLog);
+
+              // Place group under the sensitive gas mother volume
+              sensitiveGasLog.placeVolume(groupLog);
+              groupVolumes.push_back(groupLog);
+            }
+
+            //Sensitive Volume construction  : readout pad layers
+            for(int ilayer=0; ilayer < numberPadRows; ++ilayer)
+            {
+                int groupIndex = ilayer / layersPerGroup;
+                Volume &parentGroup = groupVolumes[groupIndex];
+
+                // create twice the number of rings as there are pads, producing an lower and upper part of the pad with the boundry between them the pad-ring centre
+                const double inner_lowerlayer_radius = dimSD.rmin()+ (ilayer * (padHeight));
+                const double outer_lowerlayer_radius = inner_lowerlayer_radius + (padHeight/2.0);
+
+                const double inner_upperlayer_radius = outer_lowerlayer_radius ;
+                const double outer_upperlayer_radius = inner_upperlayer_radius + (padHeight/2.0);
+
+                Tube lowerlayerSolid( inner_lowerlayer_radius, outer_lowerlayer_radius, dimSD.z_length() / 2.0, phi1, phi2);
+                Tube upperlayerSolid( inner_upperlayer_radius, outer_upperlayer_radius, dimSD.z_length() / 2.0, phi1, phi2);
+
+                Volume lowerlayerLog( _toString( ilayer ,"TPC_lowerlayer_log_%02d") ,lowerlayerSolid, materialT2Kgas );
+                Volume upperlayerLog( _toString( ilayer ,"TPC_upperlayer_log_%02d") ,upperlayerSolid, materialT2Kgas );
+
+                tpc.setVisAttributes(theDetector,  "Invisible" ,  lowerlayerLog) ;
+                tpc.setVisAttributes(theDetector,  "Invisible" ,  upperlayerLog) ;
+                //tpc.setVisAttributes(theDetector,  "RedVis" ,  lowerlayerLog) ;
+                //tpc.setVisAttributes(theDetector,  "RedVis" ,  upperlayerLog) ;
+
+                DetElement   layerDEfwd( sensGasDEfwd ,   _toString( ilayer, "tpc_row_fwd_%03d") , x_det.id() );
+                // DetElement   layerDEbwd( sensGasDEbwd ,   _toString( ilayer, "tpc_row_bwd_%03d") , x_det.id() );
+
+                Vector3D o(  inner_upperlayer_radius + 1e-10  , 0. , 0. ) ;
+                // create an unbounded surface (i.e. an infinite cylinder) and assign it to the forward gaseous volume only
+                VolCylinder surf( upperlayerLog , SurfaceType(SurfaceType::Sensitive, SurfaceType::Invisible, SurfaceType::Unbounded ) ,  (padHeight/2.0) ,  (padHeight/2.0) ,o ) ;
+
+                volSurfaceList( layerDEfwd )->push_back( surf ) ;
+                // volSurfaceList( layerDEbwd )->push_back( surf ) ;
+
+                pv = parentGroup.placeVolume( lowerlayerLog ) ;
+                pv.addPhysVolID("layer", ilayer ).addPhysVolID( "module", 0 ).addPhysVolID("sensor", 1 ) ;
+
+                pv = parentGroup.placeVolume( upperlayerLog ) ;
+                pv.addPhysVolID("layer", ilayer ).addPhysVolID( "module", 0 ).addPhysVolID("sensor", 0 ) ;
+                layerDEfwd.setPlacement( pv ) ;
+                // layerDEbwd.setPlacement( pv ) ;
+
+                lowerlayerLog.setSensitiveDetector(sens);
+                upperlayerLog.setSensitiveDetector(sens);
+		if (x_det.hasAttr(_U(limits))) {
+		  lowerlayerLog.setLimitSet(theDetector, x_det.limitsStr());
+		  upperlayerLog.setLimitSet(theDetector, x_det.limitsStr());
+		}
+            }
+
+        }
+        //-------------------------------- TPC Endplate and readout construction ----------------------------------------//
+        if(types == "TPCEndplate")
+        {
+            xml_dim_t dimEndCap = x_section.dimensions();
+            std::cout<<"============>(rmin,rmax,dz): "<< dimEndCap.rmin() / dd4hep::mm<<"mm "
+                                                      << dimEndCap.rmax() / dd4hep::mm<<" mm "
+                                                      << dimEndCap.z_length() / dd4hep::mm<< " mm" <<std::endl; 
+            //Create endcap Log volume
+            Tube endcapSolid(dimEndCap.rmin(),dimEndCap.rmax(),dimEndCap.z_length()/2.,phi1,phi1+phi2);
+            Volume endcapLog(volName+"Log",endcapSolid,materialAir);
+
+            DetElement endcapDEfwd(tpc, "tpc_endcap_fwd",x_det.id());
+            DetElement endcapDEbwd(tpc, "tpc_endcap_bwd",x_det.id());
+
+            //Vectors for endplate plane
+            Vector3D u(0.,1.,0.);
+            Vector3D v(1.,0.,0.);
+            Vector3D n(0.,0.,1.);
+
+            ////need to set the origin of the helper plane to be inside the material (otherwise it would pick up the vacuum at the origin)
+            double mid_r = 0.5*(rOuter + rInner);
+            Vector3D o(0., mid_r, 0.);
+
+            VolPlane surf( endcapLog, SurfaceType( SurfaceType::Helper ), (dz_Endplate+dz_Readout)/2.,dz_Endplate/2.,u,v,n,o);
+            volSurfaceList(endcapDEfwd) -> push_back(surf);
+            volSurfaceList(endcapDEbwd) -> push_back(surf);
+            
+            pv = tpc_motherLog.placeVolume(endcapLog,Transform3D(RotationY(0.),Position(0,0,+(dzTotal/2.-dz_Endplate/2.))));
+            endcapDEfwd.setPlacement(pv);
+
+            pv = tpc_motherLog.placeVolume(endcapLog,Transform3D(RotationY(pi),Position(0,0,-(dzTotal/2.-dz_Endplate/2.))));
+            endcapDEbwd.setPlacement(pv);
+            
+            tpc.setVisAttributes(theDetector, "transVis", endcapLog);
+
+            //================================================================================================
+            //Modular Endplate construction
+            //================================================================================================
+            double dz_Endpaltelength     = dimEndCap.z_length();
+            double r_start        = dimEndCap.rmin();  
+            double ds_reinforce   = x_section.attr<double>(_Unicode(s_frame));
+            double dz_Alframe     = x_section.attr<double>(_Unicode(z_frame));
+            double rCursor        = r_start;
+
+            //Loop all layers to construct frame-module
+            for(xml_coll_t ilayer(x_section,_U(layer)); ilayer; ++ilayer)
+            {
+                xml_comp_t x_layer(ilayer);
+
+                const std::string layerName = x_layer.nameStr();
+                const std::string layerType = x_layer.attr<std::string>(_Unicode(type));
+                double layerThickness       = x_layer.thickness();
+
+                double r_end = rCursor + layerThickness;
+                std::cout<<"===============>$ "<<layerName<<"\t"<<layerType
+                    <<" thickness = "<<layerThickness / dd4hep::mm << "mm "
+                    <<" inner radius = "<<rCursor / dd4hep::mm<<" mm"
+                    <<" outer radius = "<<r_end /dd4hep::mm<<" mm"
+                    <<std::endl;
+
+                //--------------------------------------
+                if(layerType == "Frame")
+                {
+                    double phi_start = 0.;
+                    double phi_end   = 2*M_PI;
+                    Tube   ringSolid(rCursor, r_end, dz_Alframe/2., phi_start,  phi_end) ;
+                    Volume ringLog( layerName+"Log", ringSolid, materialAlframe) ;
+                    pv = endcapLog.placeVolume( ringLog, Position(0., 0., -dz_Endpaltelength/2. + dz_Alframe/2.) ) ;
+                    tpc.setVisAttributes(theDetector,"GrayVis",ringLog);
+                }
+                if(layerType == "Module")
+                {
+                    int numberofModules = x_layer.repeat(); 
+                    double phi_start    = x_layer.phi0_offset();
+                    double phiCursor    = phi_start;
+                    double phiModule    = (2*M_PI*rCursor-numberofModules*ds_reinforce)/numberofModules/rCursor;
+                    double phiReinforce = ds_reinforce/rCursor;
+
+                    //Construct each module 
+                    for(int k=0; k<numberofModules;++k)
+                    {
+                        Tube moduleSolid1(rCursor,r_end,dz_Endpaltelength/2.,phiCursor,phiCursor+phiModule);
+                        std::string moduleLogName1 = layerName + _toString(k,"Log%00d");
+                        Volume moduleLog1(moduleLogName1,moduleSolid1,materialAir);
+
+                        double z_cursor = -dz_Endpaltelength/2.; 
+                        int    m_sli = 0;
+                        for(xml_coll_t sli(x_layer,_U(slice)); sli; ++sli,++m_sli)
+                        {
+                            xml_comp_t x_slice = sli; 
+                            double dz_modulepiece = sli.attr<double>(_Unicode(dz));
+                            std::string moduleSliceName = moduleLogName1 + _toString(m_sli,"slice%d");
+                            Material slice_mat = theDetector.material(x_slice.materialStr());
+
+                            Tube moduleSliceSolid(rCursor,r_end,dz_modulepiece/2.,phiCursor,phiCursor+phiModule);
+                            Volume moduleSliceLog(moduleSliceName,moduleSliceSolid,slice_mat);
+
+                            pv = moduleLog1.placeVolume(moduleSliceLog,Position(0.,0.,z_cursor+dz_modulepiece/2.));
+                            tpc.setVisAttributes(theDetector,  "Invisible" ,  moduleSliceLog) ;
+                            z_cursor += dz_modulepiece;
+
+                            if(z_cursor > dz_Endpaltelength/2.)
+                            {
+                                //std::cout<<" Warning ! TPC_ModularEndcap_TDR_o1_v01: Overfull TPC Module- check your xml file - section <Endpalte>." <<std::endl;
+                                throw " $!!! TPC_ModularEndcap_TDR_o1_v01: Overfull TPC Module- check your xml file - component <Endpalte>.";
+                            }
+                        }
+
+                        pv = endcapLog.placeVolume(moduleLog1);
+                        tpc.setVisAttributes(theDetector,"RedVis",moduleLog1);
+
+                        phiCursor = phiCursor+phiModule;
+
+                        //Construct the Al frame between each module 
+                        Tube moduleSolid2(rCursor,r_end,dz_Alframe/2.,phiCursor,phiCursor+phiReinforce);
+                        std::string moduleLogName2 = layerName + _toString(k,"Log_rein%00d");
+                        Volume moduleLog2(moduleLogName2,moduleSolid2,materialAlframe);
+                        pv = endcapLog.placeVolume(moduleLog2, Position(0., 0., -dz_Endpaltelength/2.+dz_Alframe/2.));
+                        tpc.setVisAttributes(theDetector,"GrayVis",moduleLog2);
+
+                        phiCursor = phiCursor+phiReinforce;
+                    }
+                }
+                rCursor = r_end;
+            }
+
+            double RadlenOfAl_Frame = materialAlframe->GetMaterial()->GetRadLen();
+            std::cout << "================================================================"<< std::endl; 
+            std::cout << "TPC_ModularEndcap_TDR_o1_v01: Endplate Al frame corresponds to " << (2. / RadlenOfAl_Frame*100)<< "% of a radiation length." << std::endl;
+            std::cout << "================================================================"<< std::endl; 
+        }
+        //-------------------------------- TPCreadout construction ----------------------------------------//
+        if(types == "TPCreadout")
+        {
+            xml_dim_t dimReadout = x_section.dimensions();
+            double dzReadout = dimReadout.z_length();
+            Tube readoutSolid(dimReadout.rmin(),dimReadout.rmax(),dimReadout.z_length()/2.,phi1,phi1+phi2);
+            Volume readoutLog(volName+"Log",readoutSolid, materialT2Kgas);
+
+            tpc.setVisAttributes(theDetector,"CyanVis",readoutLog);
+
+            xml_dim_t posReadout = x_section.position();
+            pv = tpc_motherLog.placeVolume(readoutLog,Transform3D(RotationY(0.),Position(0,0,posReadout.z())));  
+            pv = tpc_motherLog.placeVolume(readoutLog,Transform3D(RotationY(pi),Position(0,0,-posReadout.z())));  
+
+            std::cout<<"=========ReadOut dim: "<< dimReadout.rmin() / dd4hep::mm<<" mm "
+                                               << dimReadout.rmax() / dd4hep::mm<<" mm "
+                                               << dzReadout / dd4hep::mm <<" mm "<<std::endl;
+            std::cout<<"=========ReadOut Z_pos: "<<posReadout.z() / dd4hep::mm << " mm "<<std::endl;
+
+            int pieceCounter = 0;
+            double fracRadLengthReadout = 0;
+            double zCursor = -dzReadout/ 2;
+
+            for(xml_coll_t li(x_section,_U(layer)); li;++li)
+            {
+                xml_comp_t  x_layer( li );
+            
+                const double dzPiece = x_layer.attr<double>(_Unicode(dz)); 
+                Material pieceMaterial = theDetector.material( x_layer.materialStr() );
+                Tube pieceSolid( dimReadout.rmin(),dimReadout.rmax(), dzPiece / 2, phi1, phi2);
+                Volume pieceLog (  _toString( pieceCounter ,"TPCReadoutPieceLog_%02d"), pieceSolid, pieceMaterial ) ;
+
+                pieceLog.setVisAttributes(theDetector,x_layer.visStr());
+
+                pv = readoutLog.placeVolume( pieceLog  , Position(0, 0,  zCursor + dzPiece/2. ) ) ;
+
+                ++pieceCounter;
+                fracRadLengthReadout += dzPiece / pieceMaterial->GetMaterial()->GetRadLen();
+                zCursor += dzPiece;
+                
+                std::cout<<"==========> "<<dzPiece/dd4hep::mm<<" mm Material= "<<x_layer.materialStr()<<"\t"
+                         <<"X0= "<<pieceMaterial->GetMaterial()->GetRadLen()/dd4hep::mm<<"\t"
+                         <<dzPiece / pieceMaterial->GetMaterial()->GetRadLen()  <<" X0"<< std::endl;
+
+                if (zCursor > +dzReadout / 2) 
+                {
+                    //throw GeometryException(  "TPC11: Overfull TPC readout - check your xml file - section <readout>."   ) ;
+                    //std::cout<<" TPC_ModularEndcap_TDR_o1_v01: Overfull TPC readout - check your xml file - component <TPCReadout>." <<std::endl;
+                    throw " $!!! TPC_ModularEndcap_TDR_o1_v01: Overfull TPC readout - check your xml file - component <TPCReadout>.";
+                }
+            }
+            std::cout << "================================================================"<< std::endl; 
+            std::cout << "TPC_ModularEndcap_TDR_o1_v01: Readout material corresponds to " << int(fracRadLengthReadout * 1000) / 10.0 << "% of a radiation length." << std::endl;
+            std::cout << "================================================================"<< std::endl; 
+        }
+
+    }
+    
+    //TPC data
+    FixedPadSizeTPCData* tpcData = new FixedPadSizeTPCData();
+    tpcData->zHalf = dzTotal/2.;
+    tpcData->rMin  = rInner;
+    tpcData->rMax  = rOuter;
+    tpcData->innerWallThickness = drInnerWall;
+    tpcData->outerWallThickness = drOuterWall;
+    tpcData->rMinReadout = rInner + drInnerWall + drInnerServiceArea;
+    tpcData->rMaxReadout = rInner + drInnerWall + drInnerServiceArea + tpcnumberOfPadRows*tpcpadheight;
+    tpcData->maxRow = tpcnumberOfPadRows;
+    tpcData->padHeight = tpcpadheight;
+    tpcData->padWidth =  tpcpadwidth;
+    tpcData->driftLength = dzTotal/2.- dz_Endplate - dz_Readout - dz_Cathode/2.0; // SJA: cathode has to be added as the sensitive region does not start at 0.00    
+    tpcData->zMinReadout = dz_Cathode/2.0; 
+
+    ConicalSupportData* supportData = new ConicalSupportData;
+
+    ConicalSupportData::Section section0;
+    section0.rInner = rInner + drInnerWall; 
+    section0.rOuter = rOuter - drOuterWall;
+    section0.zPos   = dzTotal/2. - dz_Endplate - dz_Readout; 
+
+    ConicalSupportData::Section section1;
+    section1.rInner = rInner + drInnerWall; 
+    section1.rOuter = rOuter - drOuterWall;
+    section1.zPos   = dzTotal/2. - dz_Endplate; 
+
+    ConicalSupportData::Section section2;
+    section2.rInner = rInner ; 
+    section2.rOuter = rOuter ;
+    section2.zPos   = dzTotal/2.; 
+
+    supportData->sections.push_back(section0);
+    supportData->sections.push_back(section1);
+    supportData->sections.push_back(section2);
+
+
+    tpc.addExtension< FixedPadSizeTPCData >(tpcData);
+    tpc.addExtension< ConicalSupportData >(supportData);
+
+    //tpc.setVisAttributes( theDetector, x_det.visStr(), envelope );
+    tpc.setVisAttributes( theDetector, "TPCMotherVis1", envelope );
+    //  if( tpc.isValid() ) 
+    // tpc.setPlacement(pv);
+    std::cout << "================================================================"<< std::endl; 
+    std::cout << "TPC_ModularEndcap_TDR_o1_v01 Constructed!"<< std::endl; 
+    std::cout << "================================================================"<< std::endl; 
+
+    return tpc;
+}
+DECLARE_DETELEMENT(TPC_ModularEndcap_o1_v01_wrapvolume,create_element)
-- 
GitLab


From bab6dd8399ccae5f50f38c1b0fed69716f1de681 Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Mon, 23 Jun 2025 00:13:16 +0800
Subject: [PATCH 73/74] fix: always init track info

---
 Simulation/DetSimAna/src/Edm4hepWriterAnaElemTool.cpp | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/Simulation/DetSimAna/src/Edm4hepWriterAnaElemTool.cpp b/Simulation/DetSimAna/src/Edm4hepWriterAnaElemTool.cpp
index 19046c54..ee4900df 100644
--- a/Simulation/DetSimAna/src/Edm4hepWriterAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/Edm4hepWriterAnaElemTool.cpp
@@ -362,6 +362,13 @@ Edm4hepWriterAnaElemTool::PreUserTrackingAction(const G4Track* track) {
     int curtrkid = track->GetTrackID();
     int curparid = track->GetParentID();
     int pritrkid = curparid;
+    auto trackInfo =
+      static_cast<CommonUserTrackInfo *>(track->GetUserInformation());
+    if (!trackInfo) {
+        // 不影响,edm4heptool设置trackinfo是secondary,secondary的pre不会比primary的post早
+        trackInfo = new CommonUserTrackInfo();
+        track->SetUserInformation(trackInfo);
+    }
 
     // if it is ancestor mode, then we need to check whether current track is associated
     // with MCParticle or not.
-- 
GitLab


From 4e12f99bb58f33bed209ea0b9642e78cf378dba7 Mon Sep 17 00:00:00 2001
From: Li Zhihao <lizhihao@ihep.ac.cn>
Date: Wed, 23 Jul 2025 17:22:20 +0800
Subject: [PATCH 74/74] feat: add ecal entry info

---
 .../DetSimAna/src/ChronoAnaElemTool.cpp       | 55 +++++++++++++++++--
 Simulation/DetSimAna/src/ChronoAnaElemTool.h  |  7 +++
 2 files changed, 56 insertions(+), 6 deletions(-)

diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
index 3481a186..2872f4c8 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.cpp
@@ -105,12 +105,15 @@ StatusCode ChronoAnaElemTool::initNtuple() {
               .isSuccess();
       suc &= m_ntuple->addItem("TrackNum", m_trackIdx, 0U, MAX_TRACK_NUM)
                  .isSuccess();
-      suc &= m_ntuple->addItem("TrackPx", m_trackIdx, m_trackPx);
-      suc &= m_ntuple->addItem("TrackPy", m_trackIdx, m_trackPy);
-      suc &= m_ntuple->addItem("TrackPz", m_trackIdx, m_trackPz);
-      suc &= m_ntuple->addItem("TrackX", m_trackIdx, m_trackX);
-      suc &= m_ntuple->addItem("TrackY", m_trackIdx, m_trackY);
-      suc &= m_ntuple->addItem("TrackZ", m_trackIdx, m_trackZ);
+      suc &= m_ntuple->addIndexedItem("TrackID", m_trackIdx, m_trackId);
+      suc &= m_ntuple->addIndexedItem("TrackParentID", m_trackIdx,
+                                      m_trackParentId);
+      suc &= m_ntuple->addIndexedItem("TrackPx", m_trackIdx, m_trackPx);
+      suc &= m_ntuple->addIndexedItem("TrackPy", m_trackIdx, m_trackPy);
+      suc &= m_ntuple->addIndexedItem("TrackPz", m_trackIdx, m_trackPz);
+      suc &= m_ntuple->addIndexedItem("TrackX", m_trackIdx, m_trackX);
+      suc &= m_ntuple->addIndexedItem("TrackY", m_trackIdx, m_trackY);
+      suc &= m_ntuple->addIndexedItem("TrackZ", m_trackIdx, m_trackZ);
       suc &= m_ntuple->addIndexedItem("TrackLengths", m_trackIdx, m_trackLength)
                  .isSuccess();
       suc &= m_ntuple->addIndexedItem("TrackTimes", m_trackIdx, m_trackTime)
@@ -153,6 +156,24 @@ StatusCode ChronoAnaElemTool::initNtuple() {
               ->addIndexedItem("TrackSubdetSequence", m_trackIdx,
                                MAX_SUBDET_SEQUENCE_NUM, m_trackSubdetSequence)
               .isSuccess();
+      // ecal enter point
+      suc &= m_ntuple->addIndexedItem("EcalEntryPx", m_trackIdx, m_EcalEntryPx);
+      suc &= m_ntuple->addIndexedItem("EcalEntryPy", m_trackIdx, m_EcalEntryPy);
+      suc &= m_ntuple->addIndexedItem("EcalEntryPz", m_trackIdx, m_EcalEntryPz);
+      suc &= m_ntuple->addIndexedItem("EcalEntryX", m_trackIdx, m_EcalEntryX);
+      suc &= m_ntuple->addIndexedItem("EcalEntryY", m_trackIdx, m_EcalEntryY);
+      suc &= m_ntuple->addIndexedItem("EcalEntryZ", m_trackIdx, m_EcalEntryZ);
+      suc &=
+          m_ntuple->addIndexedItem("EcalEntryEnergy", m_trackIdx, m_EcalEntryE)
+              .isSuccess();
+      suc &= m_ntuple
+                 ->addIndexedItem("EcalBarrelEntryTimes", m_trackIdx,
+                                  m_trackEcalBarrelEntryTimes)
+                 .isSuccess();
+      suc &= m_ntuple
+                 ->addIndexedItem("EcalEndcapEntryTimes", m_trackIdx,
+                                  m_trackEcalEndcapEntryTimes)
+                 .isSuccess();
     } else {
       error() << "Cannot book N-tuple." << endmsg;
       return StatusCode::FAILURE;
@@ -229,6 +250,8 @@ void ChronoAnaElemTool::PreUserTrackingAction(const G4Track *track) {
     trackInfo->SetChronoTrackIdx(chronoTrackIdx);
     const auto &mom = track->GetMomentum();
     const auto &pos = track->GetPosition();
+    m_trackId[chronoTrackIdx] = track->GetTrackID();
+    m_trackParentId[chronoTrackIdx] = track->GetParentID();
     m_trackPx[chronoTrackIdx] = mom.x();
     m_trackPy[chronoTrackIdx] = mom.y();
     m_trackPz[chronoTrackIdx] = mom.z();
@@ -310,6 +333,26 @@ void ChronoAnaElemTool::UserSteppingAction(const G4Step *step) {
       trackInfo->SetTrackLength(currentTrackLength);
       trackInfo->SetStepNum(currentStep);
       trackInfo->SetTrackSubdetChangeTime(stop);
+      // if changed into ECAL
+      if (newSubdet == EcalBarrelIndex || newSubdet == EcalEndcapIndex) {
+        auto track_idx = trackInfo->GetChronoTrackIdx();
+        if ((m_trackEcalBarrelEntryTimes[track_idx] == 0) &&
+            (m_trackEcalEndcapEntryTimes[track_idx] == 0)) {
+          auto entry_pos = step->GetPostStepPoint()->GetPosition();
+          auto entry_mom = step->GetPostStepPoint()->GetMomentum();
+          m_EcalEntryX[track_idx] = entry_pos.x();
+          m_EcalEntryY[track_idx] = entry_pos.y();
+          m_EcalEntryZ[track_idx] = entry_pos.z();
+          m_EcalEntryPx[track_idx] = entry_mom.x();
+          m_EcalEntryPy[track_idx] = entry_mom.y();
+          m_EcalEntryPz[track_idx] = entry_mom.z();
+          m_EcalEntryE[track_idx] = step->GetPostStepPoint()->GetTotalEnergy();
+        }
+        auto &entryTimes = (newSubdet == EcalBarrelIndex)
+                               ? m_trackEcalBarrelEntryTimes
+                               : m_trackEcalEndcapEntryTimes;
+        entryTimes[track_idx]++;
+      }
     }
   }
 }
diff --git a/Simulation/DetSimAna/src/ChronoAnaElemTool.h b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
index 1ae63488..7a402a9f 100644
--- a/Simulation/DetSimAna/src/ChronoAnaElemTool.h
+++ b/Simulation/DetSimAna/src/ChronoAnaElemTool.h
@@ -60,6 +60,8 @@ private:
   StatusCode initSubdetVolumes();
   static constexpr uint8_t UnknownIndex = 0;
   static constexpr uint8_t PWorldIndex = 1;
+  static constexpr uint8_t EcalBarrelIndex = 13;
+  static constexpr uint8_t EcalEndcapIndex = 14;
   std::vector<std::string> m_subdetNames;
   std::unordered_map<G4LogicalVolume *, uint8_t> m_logicalVolumePtrToSubdetIdx;
 
@@ -70,12 +72,17 @@ private:
   NanoClock::time_point m_track_start;
 
   NTuple::Item<uint> m_trackIdx;
+  NTuple::Array<int> m_trackId, m_trackParentId;
   NTuple::Array<double> m_trackPx, m_trackPy, m_trackPz;
   NTuple::Array<double> m_trackX, m_trackY, m_trackZ;
   NTuple::Array<double> m_trackLength, m_trackE, m_trackdE;
   NTuple::Array<int> m_trackPDGId, m_trackStepNum;
   NTuple::Array<time_t> m_trackTime;
   NTuple::Array<uint8_t> m_trackStartSubdet, m_trackEndSubdet;
+  // ECAL Enter point
+  NTuple::Array<double> m_EcalEntryPx, m_EcalEntryPy, m_EcalEntryPz, m_EcalEntryE;
+  NTuple::Array<double> m_EcalEntryX, m_EcalEntryY, m_EcalEntryZ;
+  NTuple::Array<int> m_trackEcalBarrelEntryTimes, m_trackEcalEndcapEntryTimes;
 
   static constexpr uint MAX_SUBDET_NUM = 27;
   NTuple::Matrix<time_t> m_trackTimePerSubdet;
-- 
GitLab