From 9bddeb0ed00a162dbffc78c7c471d75311404102 Mon Sep 17 00:00:00 2001
From: Guang Zhao <zhaog@ihep.ac.cn>
Date: Mon, 27 May 2024 21:12:53 +0800
Subject: [PATCH 01/10] Implementation of dN/dx for gaseous detectors

---
 Reconstruction/CMakeLists.txt                 |   1 +
 Reconstruction/ParticleID/CMakeLists.txt      |  28 +++
 Reconstruction/ParticleID/src/DCHDndxAlg.cpp  | 182 ++++++++++++++
 Reconstruction/ParticleID/src/DCHDndxAlg.h    |  40 ++++
 Reconstruction/ParticleID/src/TPCDndxAlg.cpp  | 147 ++++++++++++
 Reconstruction/ParticleID/src/TPCDndxAlg.h    |  36 +++
 Service/CMakeLists.txt                        |   1 +
 Service/SimplePIDSvc/CMakeLists.txt           |  24 ++
 .../include/SimplePIDSvc/ISimplePIDSvc.h      |  26 ++
 Service/SimplePIDSvc/src/SimplePIDSvc.cpp     | 224 ++++++++++++++++++
 Service/SimplePIDSvc/src/SimplePIDSvc.h       |  39 +++
 11 files changed, 748 insertions(+)
 create mode 100644 Reconstruction/ParticleID/CMakeLists.txt
 create mode 100644 Reconstruction/ParticleID/src/DCHDndxAlg.cpp
 create mode 100644 Reconstruction/ParticleID/src/DCHDndxAlg.h
 create mode 100644 Reconstruction/ParticleID/src/TPCDndxAlg.cpp
 create mode 100644 Reconstruction/ParticleID/src/TPCDndxAlg.h
 create mode 100644 Service/SimplePIDSvc/CMakeLists.txt
 create mode 100644 Service/SimplePIDSvc/include/SimplePIDSvc/ISimplePIDSvc.h
 create mode 100644 Service/SimplePIDSvc/src/SimplePIDSvc.cpp
 create mode 100644 Service/SimplePIDSvc/src/SimplePIDSvc.h

diff --git a/Reconstruction/CMakeLists.txt b/Reconstruction/CMakeLists.txt
index 6bb53e06..793d8828 100644
--- a/Reconstruction/CMakeLists.txt
+++ b/Reconstruction/CMakeLists.txt
@@ -5,3 +5,4 @@ add_subdirectory(SiliconTracking)
 add_subdirectory(Tracking)
 add_subdirectory(RecGenfitAlg)
 add_subdirectory(RecAssociationMaker)
+add_subdirectory(ParticleID)
diff --git a/Reconstruction/ParticleID/CMakeLists.txt b/Reconstruction/ParticleID/CMakeLists.txt
new file mode 100644
index 00000000..6530b72f
--- /dev/null
+++ b/Reconstruction/ParticleID/CMakeLists.txt
@@ -0,0 +1,28 @@
+# gaudi_add_header_only_library(ParticleIDLib)
+
+# Modules
+gaudi_add_module(ParticleID
+                 SOURCES src/TPCDndxAlg.cpp
+                         src/DCHDndxAlg.cpp
+                 LINK SimplePIDSvc
+                      Gaudi::GaudiAlgLib
+                      Gaudi::GaudiKernel
+                      DataHelperLib
+                      DetSegmentation
+                      DetInterface
+                      ${GSL_LIBRARIES}
+                      ${GEAR_LIBRARIES}
+                      ${LCIO_LIBRARIES}
+                      EDM4HEP::edm4hep EDM4HEP::edm4hepDict
+                      k4FWCore::k4FWCore
+)
+
+target_include_directories(ParticleID PUBLIC
+  $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}>/include
+  $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
+
+install(TARGETS ParticleID
+  EXPORT CEPCSWTargets
+  RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT bin
+  LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT shlib
+  COMPONENT dev)
diff --git a/Reconstruction/ParticleID/src/DCHDndxAlg.cpp b/Reconstruction/ParticleID/src/DCHDndxAlg.cpp
new file mode 100644
index 00000000..7b66d17e
--- /dev/null
+++ b/Reconstruction/ParticleID/src/DCHDndxAlg.cpp
@@ -0,0 +1,182 @@
+#include "DCHDndxAlg.h"
+#include "DataHelper/HelixClass.h"
+#include "DataHelper/Navigation.h"
+#include "DetInterface/IGeomSvc.h"
+#include "UTIL/ILDConf.h"
+
+#include "DD4hep/Detector.h"
+#include "edm4hep/Hypothesis.h"
+#include "edm4hep/MCParticle.h"
+#include "edm4hep/Quantity.h"
+#include "edm4hep/Vector3d.h"
+#include "lcio.h"
+
+#include <cmath>
+
+using namespace edm4hep;
+
+DECLARE_COMPONENT(DCHDndxAlg)
+
+DCHDndxAlg::DCHDndxAlg(const std::string& name, ISvcLocator* svcLoc) : GaudiAlgorithm(name, svcLoc) {
+    // Input
+    declareProperty("SDTRecTrackCollection", _trackCol, "handler of the input track collection");
+    declareProperty("DCHitAssociationCollection", _hitassCol, "handler of the input hit association collection");
+
+    // Output
+    declareProperty("DndxTracks", _dndxCol, "handler of the collection of dN/dx tracks");
+}
+
+StatusCode DCHDndxAlg::initialize() {
+    info() << "Initilize DndxAlg ..." << endmsg;
+
+    if (m_method == "Simple") {
+        m_pid_svc = service("SimplePIDSvc");
+    }
+    else {
+        m_pid_svc = nullptr;
+    }
+
+    m_geom_svc = service("GeomSvc");
+
+    return GaudiAlgorithm::initialize();
+}
+
+StatusCode DCHDndxAlg::execute() {
+    info() << "Dndx reconstruction started" << endmsg;
+
+    const edm4hep::TrackCollection* trkcol = nullptr;
+    const edm4hep::MCRecoTrackerAssociationCollection* hitasscol = nullptr;
+    try {
+        trkcol = _trackCol.get();
+        hitasscol = _hitassCol.get();
+    }
+    catch(...) {
+        //
+    }
+
+    if (trkcol == nullptr || hitasscol == nullptr) {
+        return StatusCode::SUCCESS;
+    }
+
+    RecDqdxCollection* outCol = _dndxCol.createAndPut();
+
+    //Navigation nav;
+    //nav.Initialize();
+    for (std::size_t i = 0; i < trkcol->size(); i++) {
+        Track trk(trkcol->at(i));
+        
+        /// MC truth information
+        int pdgid = -999;
+        double p_truth = -999.;
+        bool found_mc = false;
+
+        podio::RelationRange<edm4hep::TrackerHit> hitcol = trk.getTrackerHits();
+        for (auto hit : hitcol) {
+            for (auto ha : *hitasscol) {
+                if (ha.getRec() == hit) {
+                    pdgid = ha.getSim().getMCParticle().getPDG();
+                    double px = ha.getSim().getMCParticle().getMomentum()[0];
+                    double py = ha.getSim().getMCParticle().getMomentum()[1];
+                    double pz = ha.getSim().getMCParticle().getMomentum()[2];
+                    p_truth = sqrt(px*px + py*py + pz*pz);
+                    // std::cout << "pdgid: " << pdgid << " p_truth: " << p_truth << std::endl;
+                    found_mc = true;
+                    break;
+                }
+            }
+            if (found_mc) break;
+        }
+        if (!found_mc) continue;
+
+        /// Track parameters
+        if (hitcol.size() == 0) return StatusCode::SUCCESS;
+        bool first_dc_hit = true;
+        int ihit_first, ihit_last;
+        double zmin, zmax;
+        for (size_t ihit = 0; ihit < hitcol.size(); ihit++) {
+            auto hit = hitcol[ihit];
+            if (!isCDCHit(&hit)) {
+                continue;
+            }
+            if (first_dc_hit) {
+                zmin = hit.getPosition()[2];
+                zmax = hit.getPosition()[2];
+                first_dc_hit = false;
+            }
+            else {
+                if (hit.getPosition()[2] < zmin) {
+                    zmin = hit.getPosition()[2];
+                    ihit_first = ihit;
+                }
+                if (hit.getPosition()[2] > zmax) {
+                    zmax = hit.getPosition()[2];
+                    ihit_last = ihit;
+                }
+            }
+        }
+
+        TrackerHit first_hit = hitcol[ihit_first];
+        TrackerHit last_hit = hitcol[ihit_last];
+        double len, p, cos;
+        m_pid_svc->getTrackPars(first_hit, last_hit, trk, 0, len, p, cos);
+        len = len * 10.; // convert from cm to mm
+
+        /// dN/dx reconstruction
+        if (m_method.value() == "Simple") { // Track level implementation
+            int particle_type = m_pid_svc->getParticleType(pdgid);
+            double bg = m_pid_svc->getBetaGamma(p_truth, particle_type);
+            double dndx_mean = m_pid_svc->getDndxMean(bg, cos);
+            double dndx_sigma = m_pid_svc->getDndxSigma(bg, cos, len);
+            double dndx_meas = m_pid_svc->getDndx(dndx_mean, dndx_sigma);
+            // std::cout << "pdgid: " << pdgid << " bg: " << bg << " dndx_mean: " << dndx_mean << " dndx_sigma: " << dndx_sigma << " dndx_meas: " << dndx_meas << std::endl;
+
+            std::array<Hypothesis, 5> hypotheses;
+            for (int pid = 0; pid < 5; pid++) {
+                bg = m_pid_svc->getBetaGamma(p_truth, pid);
+                dndx_mean = m_pid_svc->getDndxMean(bg, cos);
+                dndx_sigma = m_pid_svc->getDndxSigma(bg, cos, len);
+
+                Hypothesis h;
+                h.chi2 = m_pid_svc->getChi2(dndx_meas, dndx_mean, dndx_sigma);
+                h.expected = dndx_mean;
+                h.sigma = dndx_sigma;
+
+                hypotheses[pid] = h;
+            }
+
+            Quantity q;
+            q.value = dndx_meas;
+            q.error = dndx_sigma;
+
+            MutableRecDqdx dndx_track(q, pdgid, 0, hypotheses);
+            dndx_track.setTrack(trk);
+            outCol->push_back(dndx_track);
+        }
+        else if (m_method.value() == "Full") {
+            // Hit level implementation, loop over hits ...
+        }
+        else {
+            return StatusCode::FAILURE;
+        }
+    }
+
+
+    return StatusCode::SUCCESS;
+
+}
+
+StatusCode DCHDndxAlg::finalize() {
+
+    return StatusCode::SUCCESS;
+}
+
+int DCHDndxAlg::getDetTypeID(unsigned long long cellID) const {
+    UTIL::BitField64 encoder(lcio::ILDCellID0::encoder_string);
+    encoder.setValue(cellID);
+    return encoder[lcio::ILDCellID0::subdet];
+}
+
+bool DCHDndxAlg::isCDCHit(edm4hep::TrackerHit* hit){
+    return m_geom_svc->lcdd()->constant<int>("DetID_DC")==
+        getDetTypeID(hit->getCellID());
+}
\ No newline at end of file
diff --git a/Reconstruction/ParticleID/src/DCHDndxAlg.h b/Reconstruction/ParticleID/src/DCHDndxAlg.h
new file mode 100644
index 00000000..56840ca8
--- /dev/null
+++ b/Reconstruction/ParticleID/src/DCHDndxAlg.h
@@ -0,0 +1,40 @@
+#ifndef DCHDndxAlg_h
+#define DCHDndxAlg_h 1
+
+#include "k4FWCore/DataHandle.h"
+
+#include "edm4hep/TrackerHit.h"
+#include "edm4hep/TrackCollection.h"
+#include "edm4hep/TrackerHitCollection.h"
+#include "edm4hep/MCRecoTrackerAssociationCollection.h"
+#include "edm4hep/RecDqdx.h"
+#include "edm4hep/RecDqdxCollection.h"
+
+#include "GaudiAlg/GaudiAlgorithm.h"
+
+#include "SimplePIDSvc/ISimplePIDSvc.h"
+#include "DetInterface/IGeomSvc.h"
+
+class DCHDndxAlg : public GaudiAlgorithm {
+public:
+    DCHDndxAlg(const std::string& name, ISvcLocator* svcLoc);
+
+    virtual StatusCode initialize();
+    virtual StatusCode execute();
+    virtual StatusCode finalize();
+
+protected:
+    Gaudi::Property<std::string> m_method{this, "Method", "Simple"};
+
+    DataHandle<edm4hep::TrackCollection> _trackCol{"SDTRecTrackCollection", Gaudi::DataHandle::Reader, this};
+    DataHandle<edm4hep::MCRecoTrackerAssociationCollection> _hitassCol{"DCHitAssociationCollection", Gaudi::DataHandle::Reader, this};
+    DataHandle<edm4hep::RecDqdxCollection> _dndxCol{"DndxTracks", Gaudi::DataHandle::Writer, this};
+
+private:
+    SmartIF<ISimplePIDSvc> m_pid_svc;
+    SmartIF<IGeomSvc> m_geom_svc;
+    int getDetTypeID(unsigned long long cellID) const;
+    bool isCDCHit(edm4hep::TrackerHit* hit);
+};
+
+#endif
diff --git a/Reconstruction/ParticleID/src/TPCDndxAlg.cpp b/Reconstruction/ParticleID/src/TPCDndxAlg.cpp
new file mode 100644
index 00000000..801bd52b
--- /dev/null
+++ b/Reconstruction/ParticleID/src/TPCDndxAlg.cpp
@@ -0,0 +1,147 @@
+#include "TPCDndxAlg.h"
+#include "DataHelper/HelixClass.h"
+#include "DataHelper/Navigation.h"
+
+#include "edm4hep/Hypothesis.h"
+#include "edm4hep/MCParticle.h"
+#include "edm4hep/Quantity.h"
+#include "edm4hep/Vector3d.h"
+#include "lcio.h"
+
+#include <cmath>
+
+using namespace edm4hep;
+
+DECLARE_COMPONENT(TPCDndxAlg)
+
+TPCDndxAlg::TPCDndxAlg(const std::string& name, ISvcLocator* svcLoc) : GaudiAlgorithm(name, svcLoc) {
+    // Input
+    declareProperty("ClupatraTracks", _trackCol, "handler of the input track collection");
+    declareProperty("TPCTrackerHitAss", _hitassCol, "handler of the input hit association collection");
+
+    // Output
+    declareProperty("DndxTracks", _dndxCol, "handler of the collection of dN/dx tracks");
+}
+
+StatusCode TPCDndxAlg::initialize() {
+    info() << "Initilize DndxAlg ..." << endmsg;
+
+    if (m_method == "Simple") {
+        m_pid_svc = service("SimplePIDSvc");
+    }
+    else {
+        m_pid_svc = nullptr;
+    }
+
+    return GaudiAlgorithm::initialize();
+}
+
+StatusCode TPCDndxAlg::execute() {
+    info() << "Dndx reconstruction started" << endmsg;
+
+    const edm4hep::TrackCollection* trkcol = nullptr;
+    const edm4hep::MCRecoTrackerAssociationCollection* hitasscol = nullptr;
+    try {
+        trkcol = _trackCol.get();
+        hitasscol = _hitassCol.get();
+    }
+    catch(...) {
+        //
+    }
+
+    if (trkcol == nullptr || hitasscol == nullptr) {
+        return StatusCode::SUCCESS;
+    }
+
+    RecDqdxCollection* outCol = _dndxCol.createAndPut();
+
+
+    Navigation nav;
+    nav.Initialize();
+    for (std::size_t i = 0; i < trkcol->size(); i++) {
+        Track trk(trkcol->at(i));
+        
+        /// MC truth information
+        int pdgid = -999;
+        double p_truth = -999.;
+
+        podio::RelationRange<edm4hep::TrackerHit> hitcol = trk.getTrackerHits();
+        for (auto hit : hitcol) {
+            std::vector<SimTrackerHit> simhits = nav.GetRelatedTrackerHit(hit, hitasscol);
+            if (simhits.size() > 0) {
+                pdgid = simhits[0].getMCParticle().getPDG();
+                double px = simhits[0].getMCParticle().getMomentum()[0];
+                double py = simhits[0].getMCParticle().getMomentum()[1];
+                double pz = simhits[0].getMCParticle().getMomentum()[2];
+                p_truth = sqrt(px*px + py*py + pz*pz);
+                break;
+            }
+
+            //std::cout << hit.getCellID() << std::endl;
+            //double x = hit.getPosition()[0];
+            //double y = hit.getPosition()[1];
+            //double z = hit.getPosition()[2];
+            //double r = sqrt(x*x + y*y);
+            //std::cout << "r, z = " << r << ", " << z << std::endl;
+            //std::cout << "# of sim hits = " << simhits.size() << std::endl;
+            //for (auto s : simhits) {
+            //    std::cout << s.getMCParticle().getPDG() << std::endl;
+            //}
+        }
+        if (p_truth < 0) continue;
+
+        /// Track parameters
+        int nhits = hitcol.size();
+        if (nhits == 0) return StatusCode::SUCCESS;
+        TrackerHit first_hit = hitcol[0];
+        TrackerHit last_hit = hitcol[nhits - 1];
+        double len, p, cos;
+        m_pid_svc->getTrackPars(first_hit, last_hit, trk, TrackState::AtFirstHit, len, p, cos);
+
+        /// dN/dx reconstruction
+        if (m_method.value() == "Simple") { // Track level implementation
+            int particle_type = m_pid_svc->getParticleType(pdgid);
+            double bg = m_pid_svc->getBetaGamma(p_truth, particle_type);
+            double dndx_mean = m_pid_svc->getDndxMean(bg, cos);
+            double dndx_sigma = m_pid_svc->getDndxSigma(bg, cos, len);
+            double dndx_meas = m_pid_svc->getDndx(dndx_mean, dndx_sigma);
+
+            std::array<Hypothesis, 5> hypotheses;
+            for (int pid = 0; pid < 5; pid++) {
+                bg = m_pid_svc->getBetaGamma(p_truth, pid);
+                dndx_mean = m_pid_svc->getDndxMean(bg, cos);
+                dndx_sigma = m_pid_svc->getDndxSigma(bg, cos, len);
+
+                Hypothesis h;
+                h.chi2 = m_pid_svc->getChi2(dndx_meas, dndx_mean, dndx_sigma);
+                h.expected = dndx_mean;
+                h.sigma = dndx_sigma;
+
+                hypotheses[pid] = h;
+            }
+
+            Quantity q;
+            q.value = dndx_meas;
+            q.error = dndx_sigma;
+
+            MutableRecDqdx dndx_track(q, pdgid, 0, hypotheses);
+            dndx_track.setTrack(trk);
+            outCol->push_back(dndx_track);
+        }
+        else if (m_method.value() == "Full") {
+            // Hit level implementation, loop over hits ...
+        }
+        else {
+            return StatusCode::FAILURE;
+        }
+    }
+
+
+    return StatusCode::SUCCESS;
+
+}
+
+StatusCode TPCDndxAlg::finalize() {
+
+    return StatusCode::SUCCESS;
+}
diff --git a/Reconstruction/ParticleID/src/TPCDndxAlg.h b/Reconstruction/ParticleID/src/TPCDndxAlg.h
new file mode 100644
index 00000000..225ce161
--- /dev/null
+++ b/Reconstruction/ParticleID/src/TPCDndxAlg.h
@@ -0,0 +1,36 @@
+#ifndef TPCDndxAlg_h
+#define TPCDndxAlg_h 1
+
+#include "k4FWCore/DataHandle.h"
+
+#include "edm4hep/TrackerHit.h"
+#include "edm4hep/TrackCollection.h"
+#include "edm4hep/TrackerHitCollection.h"
+#include "edm4hep/MCRecoTrackerAssociationCollection.h"
+#include "edm4hep/RecDqdx.h"
+#include "edm4hep/RecDqdxCollection.h"
+
+#include "GaudiAlg/GaudiAlgorithm.h"
+
+#include "SimplePIDSvc/ISimplePIDSvc.h"
+
+class TPCDndxAlg : public GaudiAlgorithm {
+public:
+    TPCDndxAlg(const std::string& name, ISvcLocator* svcLoc);
+
+    virtual StatusCode initialize();
+    virtual StatusCode execute();
+    virtual StatusCode finalize();
+
+protected:
+    Gaudi::Property<std::string> m_method{this, "Method", "Simple"};
+
+    DataHandle<edm4hep::TrackCollection> _trackCol{"ClupatraTracks", Gaudi::DataHandle::Reader, this};
+    DataHandle<edm4hep::MCRecoTrackerAssociationCollection> _hitassCol{"TPCTrackerHitAss", Gaudi::DataHandle::Reader, this};
+    DataHandle<edm4hep::RecDqdxCollection> _dndxCol{"DndxTracks", Gaudi::DataHandle::Writer, this};
+
+private:
+    SmartIF<ISimplePIDSvc> m_pid_svc;
+};
+
+#endif
diff --git a/Service/CMakeLists.txt b/Service/CMakeLists.txt
index 24ff600b..14c79538 100644
--- a/Service/CMakeLists.txt
+++ b/Service/CMakeLists.txt
@@ -1,3 +1,4 @@
 add_subdirectory(EventSeeder)
 add_subdirectory(GearSvc)
 add_subdirectory(TrackSystemSvc)
+add_subdirectory(SimplePIDSvc)
diff --git a/Service/SimplePIDSvc/CMakeLists.txt b/Service/SimplePIDSvc/CMakeLists.txt
new file mode 100644
index 00000000..6e8d14b9
--- /dev/null
+++ b/Service/SimplePIDSvc/CMakeLists.txt
@@ -0,0 +1,24 @@
+
+gaudi_add_header_only_library(SimplePIDSvc)
+
+gaudi_add_module(SimplePIDSvcPlugins
+                 SOURCES src/SimplePIDSvc.cpp
+                 LINK SimplePIDSvc
+                      GearSvc
+                      Gaudi::GaudiKernel
+                      DataHelperLib
+                      ${DD4hep_COMPONENT_LIBRARIES}
+                      ${ROOT_LIBRARIES}
+                      ${GEAR_LIBRARIES}
+)
+
+#target_include_directories(SimplePIDSvc PUBLIC
+#  $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}>/include
+#  $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
+
+install(TARGETS SimplePIDSvc SimplePIDSvcPlugins
+  EXPORT CEPCSWTargets
+  RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT bin
+  LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT shlib
+  COMPONENT dev)
+
diff --git a/Service/SimplePIDSvc/include/SimplePIDSvc/ISimplePIDSvc.h b/Service/SimplePIDSvc/include/SimplePIDSvc/ISimplePIDSvc.h
new file mode 100644
index 00000000..dcf1a80b
--- /dev/null
+++ b/Service/SimplePIDSvc/include/SimplePIDSvc/ISimplePIDSvc.h
@@ -0,0 +1,26 @@
+#ifndef I_SimplePID_SVC_H
+#define I_SimplePID_SVC_H
+
+#include "GaudiKernel/IService.h"
+#include "edm4hep/Vector3d.h"
+#include "edm4hep/TrackerHit.h"
+#include "edm4hep/Track.h"
+
+class ISimplePIDSvc: virtual public IService {
+public:
+    DeclareInterfaceID(ISimplePIDSvc, 0, 1); // major/minor version
+    
+    virtual ~ISimplePIDSvc() = default;
+
+    virtual double getDndx(double mean, double sigma) = 0;
+    virtual double getDndxMean(double bg, double cos) = 0;
+    virtual double getDndxSigma(double bg, double cos, double len) = 0;
+    virtual double getChi2(double dndx_meas, double dndx_exp, double dndx_sigma) = 0;
+    virtual void getTrackPars(const edm4hep::TrackerHit& hit1, const edm4hep::TrackerHit& hit2, const edm4hep::Track& trk, int location,
+                              double& len, double& p, double& cos) = 0;
+    virtual double getBetaGamma(double p, int pid_type) = 0; // e, mu, pi, K, p: 0, 1, 2, 3, 4
+    virtual int getParticleType(int pdgid) = 0;
+};
+
+
+#endif
diff --git a/Service/SimplePIDSvc/src/SimplePIDSvc.cpp b/Service/SimplePIDSvc/src/SimplePIDSvc.cpp
new file mode 100644
index 00000000..e8890d80
--- /dev/null
+++ b/Service/SimplePIDSvc/src/SimplePIDSvc.cpp
@@ -0,0 +1,224 @@
+#include "SimplePIDSvc.h"
+#include "DataHelper/HelixClass.h"
+#include "GearSvc/IGearSvc.h"
+#include "gear/BField.h"
+#include "TFile.h"
+#include "TH2D.h"
+#include "TRandom.h"
+#include <iostream>
+#include <cmath>
+
+DECLARE_COMPONENT(SimplePIDSvc)
+
+using namespace edm4hep;
+
+SimplePIDSvc::SimplePIDSvc(const std::string& name, ISvcLocator* svc)
+    : base_class(name, svc)
+{
+    m_rootFile = nullptr;
+}
+
+SimplePIDSvc::~SimplePIDSvc()
+{
+    if (m_rootFile != nullptr) delete m_rootFile;
+}
+
+double SimplePIDSvc::getDndx(double mean, double sigma) {
+    return gRandom->Gaus(mean, sigma);
+}
+
+double SimplePIDSvc::getDndxMean(double bg, double cos)
+{
+    return interpolate(m_dndxMean, bg, cos);
+}
+
+double SimplePIDSvc::getDndxSigma(double bg, double cos, double len)
+{
+    return interpolate(m_dndxSigma, bg, cos)/sqrt(len*0.1); // len in mm, need to convert to cm
+}
+
+double SimplePIDSvc::getChi2(double dndx_meas, double dndx_exp, double dndx_sigma) {
+    double chi = (dndx_meas - dndx_exp)/dndx_sigma;
+    return chi*chi;
+}
+
+void SimplePIDSvc::getTrackPars(const edm4hep::TrackerHit& hit1, const edm4hep::TrackerHit& hit2, const edm4hep::Track& trk, int location,
+                                double& length, double& p, double& costheta) 
+{
+    Vector3d first_hit_pos = hit1.getPosition();
+    Vector3d last_hit_pos = hit2.getPosition();
+    TrackState trk_par;
+    for (auto it = trk.trackStates_begin(); it != trk.trackStates_end(); it++) {
+        if (it->location == location) {
+            trk_par = *it;
+            break;
+        }
+    }
+	auto _gear = service<IGearSvc>("GearSvc");
+	double bfield = _gear->getGearMgr()->getBField().at(gear::Vector3D(0.,0.0,0.)).z();
+    HelixClass hc = HelixClass();
+    hc.Initialize_Canonical(trk_par.phi, trk_par.D0, trk_par.Z0, trk_par.omega, trk_par.tanLambda, bfield); // WARNING: hard-coded magnetic field
+    double R = hc.getRadius();
+    double phi1 = atan2(first_hit_pos[1] - hc.getYC(), first_hit_pos[0] - hc.getXC());
+    double phi2 = atan2(last_hit_pos[1] - hc.getYC(), last_hit_pos[0] - hc.getXC());
+    double z1 = first_hit_pos[2];
+    double z2 = last_hit_pos[2];
+
+    // if (fabs(z2 - z1) > fabs(2*M_PI*R/cos(atan(trk_par.tanLambda)))) { // length is larger than one cycle
+    //     length = fabs((z1 - z2)/sin(atan(trk_par.tanLambda)));
+    //     //std::cout << "The trajectory longer than a cycle: " << z1 << ", " << z2 << std::endl;
+    // }
+    // else { // otherwise use rphi, because rphi is more accurate
+    //     length = fabs((phi1 - phi2)*R/cos(atan(trk_par.tanLambda)));
+    // }
+    // std::cout << "phi1: " << phi1 << ", phi2: " << phi2 << ", z1: " << z1 << ", z2: " << z2 << std::endl;
+    // std::cout << "length (rphi): " << length << std::endl;
+    // std::cout << "length (z): " << fabs((z1 - z2)/sin(atan(trk_par.tanLambda))) << std::endl;
+
+    length = fabs((z1 - z2)/sin(atan(trk_par.tanLambda)));
+
+    double pt = hc.getPXY();
+    double pz = pt * hc.getTanLambda();
+    p = sqrt(pt*pt + pz*pz);
+    costheta = cos(M_PI/2 - atan(hc.getTanLambda()));
+}
+
+double SimplePIDSvc::getBetaGamma(double p, int pid_type) { // p: GeV/c
+    double mass;
+    switch (pid_type)
+    {
+    case 0:
+        mass = 0.511;
+        break;
+    case 1:
+        mass = 105.658;
+        break;
+    case 2:
+        mass = 139.570;
+        break;
+    case 3:
+        mass = 493.677;
+        break;
+    case 4:
+        mass = 938.272;
+        break;
+    
+    default:
+        mass = 105.658; // default is muon
+        break;
+    }
+    return p/mass*1000.;
+}
+
+int SimplePIDSvc::getParticleType(int pdgid) {
+    int type;
+    switch (abs(pdgid))
+    {
+    case 11:
+        type = 0;
+        break;
+    case 13:
+        type = 1;
+        break;
+    case 211:
+        type = 2;
+        break;
+    case 321:
+        type = 3;
+        break;
+    case 2212:
+        type = 4;
+        break;
+    
+    default:
+        type = -1;
+        break;
+    }
+    return type;
+}
+
+StatusCode SimplePIDSvc::initialize()
+{
+    m_rootFile = new TFile(m_parFile.value().c_str());
+    m_dndxMean = (TH2D*)m_rootFile->Get("dndx_mean");
+    m_dndxSigma = (TH2D*)m_rootFile->Get("dndx_sigma");
+    return StatusCode::SUCCESS;
+}
+
+StatusCode SimplePIDSvc::finalize()
+{
+    if (m_rootFile != nullptr) delete m_rootFile;
+    return StatusCode::SUCCESS;
+}
+
+double SimplePIDSvc::interpolate(TH2D* h, double x, double y) {
+    int nx = h->GetNbinsX();
+    int ny = h->GetNbinsY();
+    double xmax = h->GetXaxis()->GetBinCenter(nx);
+    double xmin = h->GetXaxis()->GetBinCenter(1);
+    double ymax = h->GetYaxis()->GetBinCenter(ny);
+    double ymin = h->GetYaxis()->GetBinCenter(1);
+    double dlogx = (log10(xmax) - log10(xmin)) * 0.01;
+    double dy = (ymax - ymin) * 0.01;
+
+    if (xmin <= x && x <= xmax && ymin <= y && y <= ymax) {
+        return h->Interpolate(x, y);
+    }
+
+    int xloc, yloc;
+    if (x > xmax) xloc = 1;
+    else if (x < xmin) xloc = -1;
+    else xloc = 0;
+    if (y > ymax) yloc = 1;
+    else if (y < ymin) yloc = -1;
+    else yloc = 0;
+
+    double x1, x2, y1, y2, z1, z2;
+    if (xloc == 1) {
+        x1 = pow(10, log10(xmax)-dlogx);
+        x2 = xmax;
+    }
+    else if (xloc == -1) {
+        x1 = xmin;
+        x2 = pow(10, log10(xmin) + dlogx);
+    }
+    if (yloc == 1) {
+        y1 = ymax - dy;
+        y2 = ymax;
+    }
+    else if (yloc == -1) {
+        y1 = ymin;
+        y2 = ymin + dy;
+    }
+
+    if (xloc != 0 && yloc == 0) {
+        z1 = h->Interpolate(x1, y);
+        z2 = h->Interpolate(x2, y);
+        return linear_interpolate(x1, x2, z1, z2, x);
+    }
+    else if (xloc == 0 && yloc != 0) {
+        z1 = h->Interpolate(x, y1);
+        z2 = h->Interpolate(x, y2);
+        return linear_interpolate(y1, y2, z1, z2, y);
+    }
+    else if (xloc != 0 && yloc != 0) {
+        double y_boundary = yloc > 0 ? ymax : ymin;
+        z1 = h->Interpolate(x1, y_boundary);
+        z2 = h->Interpolate(x2, y_boundary);
+        double z_tmp = linear_interpolate(x1, x2, z1, z2, x);
+
+        double x_boundary = xloc > 0 ? xmax : xmin;
+        z1 = h->Interpolate(x_boundary, y1);
+        z2 = h->Interpolate(x_boundary, y2);
+        double z_tmp2 = linear_interpolate(y1, y2, z1, z2, y);
+
+        return z_tmp + z_tmp2 - h->Interpolate(x_boundary, y_boundary);
+    }
+    else {
+        return 0.;
+    }
+}
+
+double SimplePIDSvc::linear_interpolate(double x1, double x2, double y1, double y2, double x) {
+    return (y2-y1)/(x2-x1)*(x-x1) + y1;
+}
diff --git a/Service/SimplePIDSvc/src/SimplePIDSvc.h b/Service/SimplePIDSvc/src/SimplePIDSvc.h
new file mode 100644
index 00000000..12438bcb
--- /dev/null
+++ b/Service/SimplePIDSvc/src/SimplePIDSvc.h
@@ -0,0 +1,39 @@
+#ifndef SIMPLEPID_SVC_H
+#define SIMPLEPID_SVC_H
+
+#include "SimplePIDSvc/ISimplePIDSvc.h"
+#include <GaudiKernel/Service.h>
+#include "DD4hep/Detector.h"
+#include "gear/GearMgr.h"
+
+class TFile;
+class TH2D;
+
+class SimplePIDSvc : public extends<Service, ISimplePIDSvc>
+{
+    public:
+        SimplePIDSvc(const std::string& name, ISvcLocator* svc);
+        virtual ~SimplePIDSvc();
+
+        double getDndx(double mean, double sigma) override;
+        double getDndxMean(double bg, double cos) override;
+        double getDndxSigma(double bg, double cos, double len) override;
+        double getChi2(double dndx_meas, double dndx_exp, double dndx_sigma) override;
+        void getTrackPars(const edm4hep::TrackerHit& hit1, const edm4hep::TrackerHit& hit2, const edm4hep::Track& trk, int location,
+                          double& len, double& p, double& cos) override;
+        double getBetaGamma(double p, int pid_type) override;
+        int getParticleType(int pdgid) override;
+
+        StatusCode initialize() override;
+        StatusCode finalize() override;
+
+    private:
+        Gaudi::Property<std::string> m_parFile{this, "ParFile", "/cefs/higgs/zhaog/dndx_files/dNdx_DCH.root"};
+        TFile* m_rootFile;
+        TH2D* m_dndxMean;
+        TH2D* m_dndxSigma;
+        double interpolate(TH2D* h, double bg, double cos);
+        double linear_interpolate(double x1, double x2, double y1, double y2, double x);
+};
+
+#endif
-- 
GitLab


From e3f5e2cead332231df33f7d6a3268d833089e892 Mon Sep 17 00:00:00 2001
From: Guang Zhao <zhaog@ihep.ac.cn>
Date: Tue, 28 May 2024 22:03:03 +0800
Subject: [PATCH 02/10] updata mc track association

---
 Reconstruction/ParticleID/src/DCHDndxAlg.cpp | 38 +++++++++++----
 Reconstruction/ParticleID/src/DCHDndxAlg.h   |  4 +-
 Reconstruction/ParticleID/src/TPCDndxAlg.cpp | 51 +++++++++-----------
 Reconstruction/ParticleID/src/TPCDndxAlg.h   |  3 +-
 4 files changed, 57 insertions(+), 39 deletions(-)

diff --git a/Reconstruction/ParticleID/src/DCHDndxAlg.cpp b/Reconstruction/ParticleID/src/DCHDndxAlg.cpp
index 7b66d17e..ea0b5d54 100644
--- a/Reconstruction/ParticleID/src/DCHDndxAlg.cpp
+++ b/Reconstruction/ParticleID/src/DCHDndxAlg.cpp
@@ -20,7 +20,7 @@ DECLARE_COMPONENT(DCHDndxAlg)
 DCHDndxAlg::DCHDndxAlg(const std::string& name, ISvcLocator* svcLoc) : GaudiAlgorithm(name, svcLoc) {
     // Input
     declareProperty("SDTRecTrackCollection", _trackCol, "handler of the input track collection");
-    declareProperty("DCHitAssociationCollection", _hitassCol, "handler of the input hit association collection");
+    declareProperty("SDTRecTrackCollectionParticleAssociation", _trkParAssCol, "handler of the input track particle association collection");
 
     // Output
     declareProperty("DndxTracks", _dndxCol, "handler of the collection of dN/dx tracks");
@@ -45,16 +45,16 @@ StatusCode DCHDndxAlg::execute() {
     info() << "Dndx reconstruction started" << endmsg;
 
     const edm4hep::TrackCollection* trkcol = nullptr;
-    const edm4hep::MCRecoTrackerAssociationCollection* hitasscol = nullptr;
+    const edm4hep::MCRecoTrackParticleAssociationCollection* trkparasscol = nullptr;
     try {
         trkcol = _trackCol.get();
-        hitasscol = _hitassCol.get();
+        trkparasscol = _trkParAssCol.get();
     }
     catch(...) {
         //
     }
 
-    if (trkcol == nullptr || hitasscol == nullptr) {
+    if (trkcol == nullptr || trkparasscol == nullptr) {
         return StatusCode::SUCCESS;
     }
 
@@ -68,9 +68,28 @@ StatusCode DCHDndxAlg::execute() {
         /// MC truth information
         int pdgid = -999;
         double p_truth = -999.;
-        bool found_mc = false;
-
-        podio::RelationRange<edm4hep::TrackerHit> hitcol = trk.getTrackerHits();
+        
+        float max_weight = -999.;
+        int max_weight_idx = -1;
+        int ass_idx = 0;
+        for (auto ass : *trkparasscol) {
+            if (ass.getRec() == trk) {
+                float weight = ass.getWeight();
+                if (weight > max_weight) {
+                    max_weight = weight;
+                    max_weight_idx = ass_idx;
+                }
+            }
+            ass_idx++;
+        }
+        if (max_weight_idx < 0) continue;
+        pdgid = trkparasscol->at(max_weight_idx).getSim().getPDG();
+        double px = trkparasscol->at(max_weight_idx).getSim().getMomentum()[0];
+        double py = trkparasscol->at(max_weight_idx).getSim().getMomentum()[1];
+        double pz = trkparasscol->at(max_weight_idx).getSim().getMomentum()[2];
+        p_truth = sqrt(px*px + py*py + pz*pz);
+
+        /*bool found_mc = false;
         for (auto hit : hitcol) {
             for (auto ha : *hitasscol) {
                 if (ha.getRec() == hit) {
@@ -86,9 +105,10 @@ StatusCode DCHDndxAlg::execute() {
             }
             if (found_mc) break;
         }
-        if (!found_mc) continue;
+        if (!found_mc) continue;*/
 
         /// Track parameters
+        podio::RelationRange<edm4hep::TrackerHit> hitcol = trk.getTrackerHits();
         if (hitcol.size() == 0) return StatusCode::SUCCESS;
         bool first_dc_hit = true;
         int ihit_first, ihit_last;
@@ -148,7 +168,7 @@ StatusCode DCHDndxAlg::execute() {
             q.value = dndx_meas;
             q.error = dndx_sigma;
 
-            MutableRecDqdx dndx_track(q, pdgid, 0, hypotheses);
+            MutableRecDqdx dndx_track(q, particle_type, 0, hypotheses);
             dndx_track.setTrack(trk);
             outCol->push_back(dndx_track);
         }
diff --git a/Reconstruction/ParticleID/src/DCHDndxAlg.h b/Reconstruction/ParticleID/src/DCHDndxAlg.h
index 56840ca8..bd6f01d8 100644
--- a/Reconstruction/ParticleID/src/DCHDndxAlg.h
+++ b/Reconstruction/ParticleID/src/DCHDndxAlg.h
@@ -7,6 +7,7 @@
 #include "edm4hep/TrackCollection.h"
 #include "edm4hep/TrackerHitCollection.h"
 #include "edm4hep/MCRecoTrackerAssociationCollection.h"
+#include "edm4hep/MCRecoTrackParticleAssociationCollection.h"
 #include "edm4hep/RecDqdx.h"
 #include "edm4hep/RecDqdxCollection.h"
 
@@ -27,7 +28,8 @@ protected:
     Gaudi::Property<std::string> m_method{this, "Method", "Simple"};
 
     DataHandle<edm4hep::TrackCollection> _trackCol{"SDTRecTrackCollection", Gaudi::DataHandle::Reader, this};
-    DataHandle<edm4hep::MCRecoTrackerAssociationCollection> _hitassCol{"DCHitAssociationCollection", Gaudi::DataHandle::Reader, this};
+    //DataHandle<edm4hep::MCRecoTrackerAssociationCollection> _hitassCol{"DCHitAssociationCollection", Gaudi::DataHandle::Reader, this};
+    DataHandle<edm4hep::MCRecoTrackParticleAssociationCollection> _trkParAssCol{"SDTRecTrackCollectionParticleAssociation", Gaudi::DataHandle::Reader, this};
     DataHandle<edm4hep::RecDqdxCollection> _dndxCol{"DndxTracks", Gaudi::DataHandle::Writer, this};
 
 private:
diff --git a/Reconstruction/ParticleID/src/TPCDndxAlg.cpp b/Reconstruction/ParticleID/src/TPCDndxAlg.cpp
index 801bd52b..42db714f 100644
--- a/Reconstruction/ParticleID/src/TPCDndxAlg.cpp
+++ b/Reconstruction/ParticleID/src/TPCDndxAlg.cpp
@@ -17,7 +17,7 @@ DECLARE_COMPONENT(TPCDndxAlg)
 TPCDndxAlg::TPCDndxAlg(const std::string& name, ISvcLocator* svcLoc) : GaudiAlgorithm(name, svcLoc) {
     // Input
     declareProperty("ClupatraTracks", _trackCol, "handler of the input track collection");
-    declareProperty("TPCTrackerHitAss", _hitassCol, "handler of the input hit association collection");
+    declareProperty("ClupatraTracksParticleAssociation", _trkParAssCol, "handler of the input track particle association collection");
 
     // Output
     declareProperty("DndxTracks", _dndxCol, "handler of the collection of dN/dx tracks");
@@ -40,22 +40,21 @@ StatusCode TPCDndxAlg::execute() {
     info() << "Dndx reconstruction started" << endmsg;
 
     const edm4hep::TrackCollection* trkcol = nullptr;
-    const edm4hep::MCRecoTrackerAssociationCollection* hitasscol = nullptr;
+    const edm4hep::MCRecoTrackParticleAssociationCollection* trkparasscol = nullptr;
     try {
         trkcol = _trackCol.get();
-        hitasscol = _hitassCol.get();
+        trkparasscol = _trkParAssCol.get();
     }
     catch(...) {
         //
     }
 
-    if (trkcol == nullptr || hitasscol == nullptr) {
+    if (trkcol == nullptr || trkparasscol == nullptr) {
         return StatusCode::SUCCESS;
     }
 
     RecDqdxCollection* outCol = _dndxCol.createAndPut();
 
-
     Navigation nav;
     nav.Initialize();
     for (std::size_t i = 0; i < trkcol->size(); i++) {
@@ -65,32 +64,28 @@ StatusCode TPCDndxAlg::execute() {
         int pdgid = -999;
         double p_truth = -999.;
 
-        podio::RelationRange<edm4hep::TrackerHit> hitcol = trk.getTrackerHits();
-        for (auto hit : hitcol) {
-            std::vector<SimTrackerHit> simhits = nav.GetRelatedTrackerHit(hit, hitasscol);
-            if (simhits.size() > 0) {
-                pdgid = simhits[0].getMCParticle().getPDG();
-                double px = simhits[0].getMCParticle().getMomentum()[0];
-                double py = simhits[0].getMCParticle().getMomentum()[1];
-                double pz = simhits[0].getMCParticle().getMomentum()[2];
-                p_truth = sqrt(px*px + py*py + pz*pz);
-                break;
+        float max_weight = -999.;
+        int max_weight_idx = -1;
+        int ass_idx = 0;
+        for (auto ass : *trkparasscol) {
+            if (ass.getRec() == trk) {
+                float weight = ass.getWeight();
+                if (weight > max_weight) {
+                    max_weight = weight;
+                    max_weight_idx = ass_idx;
+                }
             }
-
-            //std::cout << hit.getCellID() << std::endl;
-            //double x = hit.getPosition()[0];
-            //double y = hit.getPosition()[1];
-            //double z = hit.getPosition()[2];
-            //double r = sqrt(x*x + y*y);
-            //std::cout << "r, z = " << r << ", " << z << std::endl;
-            //std::cout << "# of sim hits = " << simhits.size() << std::endl;
-            //for (auto s : simhits) {
-            //    std::cout << s.getMCParticle().getPDG() << std::endl;
-            //}
+            ass_idx++;
         }
-        if (p_truth < 0) continue;
+        if (max_weight_idx < 0) continue;
+        pdgid = trkparasscol->at(max_weight_idx).getSim().getPDG();
+        double px = trkparasscol->at(max_weight_idx).getSim().getMomentum()[0];
+        double py = trkparasscol->at(max_weight_idx).getSim().getMomentum()[1];
+        double pz = trkparasscol->at(max_weight_idx).getSim().getMomentum()[2];
+        p_truth = sqrt(px*px + py*py + pz*pz);
 
         /// Track parameters
+        podio::RelationRange<edm4hep::TrackerHit> hitcol = trk.getTrackerHits();
         int nhits = hitcol.size();
         if (nhits == 0) return StatusCode::SUCCESS;
         TrackerHit first_hit = hitcol[0];
@@ -124,7 +119,7 @@ StatusCode TPCDndxAlg::execute() {
             q.value = dndx_meas;
             q.error = dndx_sigma;
 
-            MutableRecDqdx dndx_track(q, pdgid, 0, hypotheses);
+            MutableRecDqdx dndx_track(q, particle_type, 0, hypotheses);
             dndx_track.setTrack(trk);
             outCol->push_back(dndx_track);
         }
diff --git a/Reconstruction/ParticleID/src/TPCDndxAlg.h b/Reconstruction/ParticleID/src/TPCDndxAlg.h
index 225ce161..76c95a84 100644
--- a/Reconstruction/ParticleID/src/TPCDndxAlg.h
+++ b/Reconstruction/ParticleID/src/TPCDndxAlg.h
@@ -7,6 +7,7 @@
 #include "edm4hep/TrackCollection.h"
 #include "edm4hep/TrackerHitCollection.h"
 #include "edm4hep/MCRecoTrackerAssociationCollection.h"
+#include "edm4hep/MCRecoTrackParticleAssociationCollection.h"
 #include "edm4hep/RecDqdx.h"
 #include "edm4hep/RecDqdxCollection.h"
 
@@ -26,7 +27,7 @@ protected:
     Gaudi::Property<std::string> m_method{this, "Method", "Simple"};
 
     DataHandle<edm4hep::TrackCollection> _trackCol{"ClupatraTracks", Gaudi::DataHandle::Reader, this};
-    DataHandle<edm4hep::MCRecoTrackerAssociationCollection> _hitassCol{"TPCTrackerHitAss", Gaudi::DataHandle::Reader, this};
+    DataHandle<edm4hep::MCRecoTrackParticleAssociationCollection> _trkParAssCol{"ClupatraTracksParticleAssociation", Gaudi::DataHandle::Reader, this};
     DataHandle<edm4hep::RecDqdxCollection> _dndxCol{"DndxTracks", Gaudi::DataHandle::Writer, this};
 
 private:
-- 
GitLab


From 9110abbddd6f2fdf8cff05661efc26f45759bb8c Mon Sep 17 00:00:00 2001
From: Guang Zhao <zhaog@ihep.ac.cn>
Date: Wed, 29 May 2024 09:44:27 +0800
Subject: [PATCH 03/10] Minor updates on comments and default parameters

---
 Reconstruction/ParticleID/src/DCHDndxAlg.h                | 6 ++++++
 Reconstruction/ParticleID/src/TPCDndxAlg.h                | 6 ++++++
 Service/SimplePIDSvc/include/SimplePIDSvc/ISimplePIDSvc.h | 6 ++++++
 Service/SimplePIDSvc/src/SimplePIDSvc.h                   | 8 +++++++-
 4 files changed, 25 insertions(+), 1 deletion(-)

diff --git a/Reconstruction/ParticleID/src/DCHDndxAlg.h b/Reconstruction/ParticleID/src/DCHDndxAlg.h
index bd6f01d8..8301662f 100644
--- a/Reconstruction/ParticleID/src/DCHDndxAlg.h
+++ b/Reconstruction/ParticleID/src/DCHDndxAlg.h
@@ -16,6 +16,12 @@
 #include "SimplePIDSvc/ISimplePIDSvc.h"
 #include "DetInterface/IGeomSvc.h"
 
+/**
+ * @class DCHDndxAlg
+ * @brief Algorithm to calculate dN/dx for DCH
+ * @author Guang Zhao (zhaog@ihep.ac.cn)
+*/
+
 class DCHDndxAlg : public GaudiAlgorithm {
 public:
     DCHDndxAlg(const std::string& name, ISvcLocator* svcLoc);
diff --git a/Reconstruction/ParticleID/src/TPCDndxAlg.h b/Reconstruction/ParticleID/src/TPCDndxAlg.h
index 76c95a84..9c508e0b 100644
--- a/Reconstruction/ParticleID/src/TPCDndxAlg.h
+++ b/Reconstruction/ParticleID/src/TPCDndxAlg.h
@@ -15,6 +15,12 @@
 
 #include "SimplePIDSvc/ISimplePIDSvc.h"
 
+/**
+ * @class TPCDndxAlg
+ * @brief Algorithm to calculate dN/dx for TPC
+ * @author Guang Zhao (zhaog@ihep.ac.cn)
+*/
+
 class TPCDndxAlg : public GaudiAlgorithm {
 public:
     TPCDndxAlg(const std::string& name, ISvcLocator* svcLoc);
diff --git a/Service/SimplePIDSvc/include/SimplePIDSvc/ISimplePIDSvc.h b/Service/SimplePIDSvc/include/SimplePIDSvc/ISimplePIDSvc.h
index dcf1a80b..5e048a03 100644
--- a/Service/SimplePIDSvc/include/SimplePIDSvc/ISimplePIDSvc.h
+++ b/Service/SimplePIDSvc/include/SimplePIDSvc/ISimplePIDSvc.h
@@ -6,6 +6,12 @@
 #include "edm4hep/TrackerHit.h"
 #include "edm4hep/Track.h"
 
+/**
+ * @class ISimplePIDSvc
+ * @brief Interface for Simple PID
+ * @author Guang Zhao (zhaog@ihep.ac.cn)
+*/
+
 class ISimplePIDSvc: virtual public IService {
 public:
     DeclareInterfaceID(ISimplePIDSvc, 0, 1); // major/minor version
diff --git a/Service/SimplePIDSvc/src/SimplePIDSvc.h b/Service/SimplePIDSvc/src/SimplePIDSvc.h
index 12438bcb..aec3da99 100644
--- a/Service/SimplePIDSvc/src/SimplePIDSvc.h
+++ b/Service/SimplePIDSvc/src/SimplePIDSvc.h
@@ -9,6 +9,12 @@
 class TFile;
 class TH2D;
 
+/**
+ * @class SimplePIDSvc
+ * @brief Simple PID service
+ * @author Guang Zhao (zhaog@ihep.ac.cn)
+*/
+
 class SimplePIDSvc : public extends<Service, ISimplePIDSvc>
 {
     public:
@@ -28,7 +34,7 @@ class SimplePIDSvc : public extends<Service, ISimplePIDSvc>
         StatusCode finalize() override;
 
     private:
-        Gaudi::Property<std::string> m_parFile{this, "ParFile", "/cefs/higgs/zhaog/dndx_files/dNdx_DCH.root"};
+        Gaudi::Property<std::string> m_parFile{this, "ParFile", "dNdx_TPC.root"};
         TFile* m_rootFile;
         TH2D* m_dndxMean;
         TH2D* m_dndxSigma;
-- 
GitLab


From 00de4c5adcf3ca888837e1a08d8d0f38970ef91f Mon Sep 17 00:00:00 2001
From: Guang Zhao <zhaog@ihep.ac.cn>
Date: Wed, 29 May 2024 16:06:31 +0800
Subject: [PATCH 04/10] update the script and bug fix

---
 Detector/DetCRD/scripts/TDR_o1_v01/tracking.py | 15 ++++++++++++---
 Reconstruction/ParticleID/src/DCHDndxAlg.cpp   |  8 ++++----
 Reconstruction/ParticleID/src/TPCDndxAlg.cpp   |  8 ++++----
 3 files changed, 20 insertions(+), 11 deletions(-)

diff --git a/Detector/DetCRD/scripts/TDR_o1_v01/tracking.py b/Detector/DetCRD/scripts/TDR_o1_v01/tracking.py
index 6c7a4e89..5ce28166 100644
--- a/Detector/DetCRD/scripts/TDR_o1_v01/tracking.py
+++ b/Detector/DetCRD/scripts/TDR_o1_v01/tracking.py
@@ -39,6 +39,10 @@ gearsvc = GearSvc("GearSvc")
 from Configurables import TrackSystemSvc
 tracksystemsvc = TrackSystemSvc("TrackSystemSvc")
 
+from Configurables import SimplePIDSvc
+pidsvc = SimplePIDSvc("SimplePIDSvc")
+pidsvc.ParFile = "/cefs/higgs/zhaog/dndx_files/dNdx_TPC.root"
+
 from Configurables import PodioInput
 podioinput = PodioInput("PodioReader", collections=[
 #    "EventHeader",
@@ -207,10 +211,15 @@ full.SETHitToTrackDistance = 5.
 full.MinChi2ProbForSiliconTracks = 0
 #full.OutputLevel = DEBUG
 
+from Configurables import TPCDndxAlg
+tpc_dndx = TPCDndxAlg("TPCDndxAlg")
+tpc_dndx.Method = "Simple"
+
 from Configurables import TrackParticleRelationAlg
 tpr = TrackParticleRelationAlg("Track2Particle")
 tpr.MCParticleCollection = "MCParticle"
-tpr.TrackList = ["CompleteTracks"]
+tpr.TrackList = ["CompleteTracks", "ClupatraTracks"]
+tpr.TrackerAssociationList = ["VXDTrackerHitAssociation", "SITTrackerHitAssociation", "SETTrackerHitAssociation", "FTDTrackerHitAssociation", "TPCTrackerHitAss"]
 #tpr.OutputLevel = DEBUG
 
 # output
@@ -222,10 +231,10 @@ out.outputCommands = ["keep *"]
 # ApplicationMgr
 from Configurables import ApplicationMgr
 mgr = ApplicationMgr(
-    TopAlg = [podioinput, digiVXD, digiSIT, digiSET, digiFTD, digiTPC, tracking, forward, subset, clupatra, full, tpr, out],
+    TopAlg = [podioinput, digiVXD, digiSIT, digiSET, digiFTD, digiTPC, tracking, forward, subset, clupatra, full, tpr, tpc_dndx, out],
     EvtSel = 'NONE',
     EvtMax = 5,
-    ExtSvc = [rndmengine, rndmgensvc, dsvc, evtseeder, geosvc, gearsvc, tracksystemsvc],
+    ExtSvc = [rndmengine, rndmgensvc, dsvc, evtseeder, geosvc, gearsvc, tracksystemsvc, pidsvc],
     HistogramPersistency = 'ROOT',
     OutputLevel = ERROR
 )
diff --git a/Reconstruction/ParticleID/src/DCHDndxAlg.cpp b/Reconstruction/ParticleID/src/DCHDndxAlg.cpp
index ea0b5d54..afd8cadf 100644
--- a/Reconstruction/ParticleID/src/DCHDndxAlg.cpp
+++ b/Reconstruction/ParticleID/src/DCHDndxAlg.cpp
@@ -150,6 +150,10 @@ StatusCode DCHDndxAlg::execute() {
             double dndx_meas = m_pid_svc->getDndx(dndx_mean, dndx_sigma);
             // std::cout << "pdgid: " << pdgid << " bg: " << bg << " dndx_mean: " << dndx_mean << " dndx_sigma: " << dndx_sigma << " dndx_meas: " << dndx_meas << std::endl;
 
+            Quantity q;
+            q.value = dndx_meas;
+            q.error = dndx_sigma;
+
             std::array<Hypothesis, 5> hypotheses;
             for (int pid = 0; pid < 5; pid++) {
                 bg = m_pid_svc->getBetaGamma(p_truth, pid);
@@ -164,10 +168,6 @@ StatusCode DCHDndxAlg::execute() {
                 hypotheses[pid] = h;
             }
 
-            Quantity q;
-            q.value = dndx_meas;
-            q.error = dndx_sigma;
-
             MutableRecDqdx dndx_track(q, particle_type, 0, hypotheses);
             dndx_track.setTrack(trk);
             outCol->push_back(dndx_track);
diff --git a/Reconstruction/ParticleID/src/TPCDndxAlg.cpp b/Reconstruction/ParticleID/src/TPCDndxAlg.cpp
index 42db714f..454c4754 100644
--- a/Reconstruction/ParticleID/src/TPCDndxAlg.cpp
+++ b/Reconstruction/ParticleID/src/TPCDndxAlg.cpp
@@ -101,6 +101,10 @@ StatusCode TPCDndxAlg::execute() {
             double dndx_sigma = m_pid_svc->getDndxSigma(bg, cos, len);
             double dndx_meas = m_pid_svc->getDndx(dndx_mean, dndx_sigma);
 
+            Quantity q;
+            q.value = dndx_meas;
+            q.error = dndx_sigma;
+
             std::array<Hypothesis, 5> hypotheses;
             for (int pid = 0; pid < 5; pid++) {
                 bg = m_pid_svc->getBetaGamma(p_truth, pid);
@@ -115,10 +119,6 @@ StatusCode TPCDndxAlg::execute() {
                 hypotheses[pid] = h;
             }
 
-            Quantity q;
-            q.value = dndx_meas;
-            q.error = dndx_sigma;
-
             MutableRecDqdx dndx_track(q, particle_type, 0, hypotheses);
             dndx_track.setTrack(trk);
             outCol->push_back(dndx_track);
-- 
GitLab


From be0e3fcbf526f60fc0a9b98456182f23af96607d Mon Sep 17 00:00:00 2001
From: Guang Zhao <zhaog@ihep.ac.cn>
Date: Thu, 30 May 2024 00:30:45 +0800
Subject: [PATCH 05/10] Update track length calculation; fix DCH unit
 conversion

---
 Reconstruction/ParticleID/src/DCHDndxAlg.cpp  |  9 ++--
 Reconstruction/ParticleID/src/TPCDndxAlg.cpp  |  4 +-
 .../include/SimplePIDSvc/ISimplePIDSvc.h      |  3 +-
 Service/SimplePIDSvc/src/SimplePIDSvc.cpp     | 45 ++++++++++++-------
 Service/SimplePIDSvc/src/SimplePIDSvc.h       |  3 +-
 5 files changed, 41 insertions(+), 23 deletions(-)

diff --git a/Reconstruction/ParticleID/src/DCHDndxAlg.cpp b/Reconstruction/ParticleID/src/DCHDndxAlg.cpp
index afd8cadf..f7b26c3b 100644
--- a/Reconstruction/ParticleID/src/DCHDndxAlg.cpp
+++ b/Reconstruction/ParticleID/src/DCHDndxAlg.cpp
@@ -135,11 +135,12 @@ StatusCode DCHDndxAlg::execute() {
             }
         }
 
-        TrackerHit first_hit = hitcol[ihit_first];
-        TrackerHit last_hit = hitcol[ihit_last];
+        // convert the position from cm to mm
+        Vector3d first_hit_pos = Vector3d(hitcol[ihit_first].getPosition().x*10.0, hitcol[ihit_first].getPosition().y*10.0, hitcol[ihit_first].getPosition().z*10.0);
+        Vector3d last_hit_pos = Vector3d(hitcol[ihit_last].getPosition().x*10.0, hitcol[ihit_last].getPosition().y*10.0, hitcol[ihit_last].getPosition().z*10.0);
         double len, p, cos;
-        m_pid_svc->getTrackPars(first_hit, last_hit, trk, 0, len, p, cos);
-        len = len * 10.; // convert from cm to mm
+        m_pid_svc->getTrackPars(first_hit_pos, last_hit_pos, trk, 0, len, p, cos);
+        //len = len * 10.; // convert from cm to mm
 
         /// dN/dx reconstruction
         if (m_method.value() == "Simple") { // Track level implementation
diff --git a/Reconstruction/ParticleID/src/TPCDndxAlg.cpp b/Reconstruction/ParticleID/src/TPCDndxAlg.cpp
index 454c4754..3d5c38fb 100644
--- a/Reconstruction/ParticleID/src/TPCDndxAlg.cpp
+++ b/Reconstruction/ParticleID/src/TPCDndxAlg.cpp
@@ -88,8 +88,8 @@ StatusCode TPCDndxAlg::execute() {
         podio::RelationRange<edm4hep::TrackerHit> hitcol = trk.getTrackerHits();
         int nhits = hitcol.size();
         if (nhits == 0) return StatusCode::SUCCESS;
-        TrackerHit first_hit = hitcol[0];
-        TrackerHit last_hit = hitcol[nhits - 1];
+        const Vector3d& first_hit = hitcol[0].getPosition();
+        const Vector3d& last_hit = hitcol[nhits - 1].getPosition();
         double len, p, cos;
         m_pid_svc->getTrackPars(first_hit, last_hit, trk, TrackState::AtFirstHit, len, p, cos);
 
diff --git a/Service/SimplePIDSvc/include/SimplePIDSvc/ISimplePIDSvc.h b/Service/SimplePIDSvc/include/SimplePIDSvc/ISimplePIDSvc.h
index 5e048a03..5a543229 100644
--- a/Service/SimplePIDSvc/include/SimplePIDSvc/ISimplePIDSvc.h
+++ b/Service/SimplePIDSvc/include/SimplePIDSvc/ISimplePIDSvc.h
@@ -5,6 +5,7 @@
 #include "edm4hep/Vector3d.h"
 #include "edm4hep/TrackerHit.h"
 #include "edm4hep/Track.h"
+#include "edm4hep/Vector3d.h"
 
 /**
  * @class ISimplePIDSvc
@@ -22,7 +23,7 @@ public:
     virtual double getDndxMean(double bg, double cos) = 0;
     virtual double getDndxSigma(double bg, double cos, double len) = 0;
     virtual double getChi2(double dndx_meas, double dndx_exp, double dndx_sigma) = 0;
-    virtual void getTrackPars(const edm4hep::TrackerHit& hit1, const edm4hep::TrackerHit& hit2, const edm4hep::Track& trk, int location,
+    virtual void getTrackPars(const edm4hep::Vector3d& hit1, const edm4hep::Vector3d& hit2, const edm4hep::Track& trk, int location,
                               double& len, double& p, double& cos) = 0;
     virtual double getBetaGamma(double p, int pid_type) = 0; // e, mu, pi, K, p: 0, 1, 2, 3, 4
     virtual int getParticleType(int pdgid) = 0;
diff --git a/Service/SimplePIDSvc/src/SimplePIDSvc.cpp b/Service/SimplePIDSvc/src/SimplePIDSvc.cpp
index e8890d80..5ccece5b 100644
--- a/Service/SimplePIDSvc/src/SimplePIDSvc.cpp
+++ b/Service/SimplePIDSvc/src/SimplePIDSvc.cpp
@@ -42,11 +42,9 @@ double SimplePIDSvc::getChi2(double dndx_meas, double dndx_exp, double dndx_sigm
     return chi*chi;
 }
 
-void SimplePIDSvc::getTrackPars(const edm4hep::TrackerHit& hit1, const edm4hep::TrackerHit& hit2, const edm4hep::Track& trk, int location,
+void SimplePIDSvc::getTrackPars(const edm4hep::Vector3d& first_hit_pos, const edm4hep::Vector3d& last_hit_pos, const edm4hep::Track& trk, int location,
                                 double& length, double& p, double& costheta) 
 {
-    Vector3d first_hit_pos = hit1.getPosition();
-    Vector3d last_hit_pos = hit2.getPosition();
     TrackState trk_par;
     for (auto it = trk.trackStates_begin(); it != trk.trackStates_end(); it++) {
         if (it->location == location) {
@@ -57,25 +55,42 @@ void SimplePIDSvc::getTrackPars(const edm4hep::TrackerHit& hit1, const edm4hep::
 	auto _gear = service<IGearSvc>("GearSvc");
 	double bfield = _gear->getGearMgr()->getBField().at(gear::Vector3D(0.,0.0,0.)).z();
     HelixClass hc = HelixClass();
-    hc.Initialize_Canonical(trk_par.phi, trk_par.D0, trk_par.Z0, trk_par.omega, trk_par.tanLambda, bfield); // WARNING: hard-coded magnetic field
+    hc.Initialize_Canonical(trk_par.phi, trk_par.D0, trk_par.Z0, trk_par.omega, trk_par.tanLambda, bfield);
     double R = hc.getRadius();
     double phi1 = atan2(first_hit_pos[1] - hc.getYC(), first_hit_pos[0] - hc.getXC());
     double phi2 = atan2(last_hit_pos[1] - hc.getYC(), last_hit_pos[0] - hc.getXC());
     double z1 = first_hit_pos[2];
     double z2 = last_hit_pos[2];
 
+
+    // std::cout << "xc, yc = " << hc.getXC() << ", " << hc.getYC() << std::endl;
+    // std::cout << "r = " << R << std::endl;
+    // std::cout << "x1, y1 = " << first_hit_pos[0] << ", " << first_hit_pos[1] << std::endl;
+    // std::cout << "x2, y2 = " << last_hit_pos[0] << ", " << last_hit_pos[1] << std::endl;
+    // std::cout << "phi1, phi2 = " << phi1 << ", " << phi2 << std::endl;
+    // std::cout << "tanLambda = " << trk_par.tanLambda << std::endl;
+    // std::cout << "z1, z2 = " << z1 << ", " << z2 << std::endl;
+    // const float* p3 = hc.getMomentum();
+    // std::cout << "momentum = " << sqrt(p3[0]*p3[0] + p3[1]*p3[1] + p3[2]*p3[2]) << std::endl;
+    // std::cout << "charge = " << trk_par.omega << std::endl;
+
     // if (fabs(z2 - z1) > fabs(2*M_PI*R/cos(atan(trk_par.tanLambda)))) { // length is larger than one cycle
-    //     length = fabs((z1 - z2)/sin(atan(trk_par.tanLambda)));
-    //     //std::cout << "The trajectory longer than a cycle: " << z1 << ", " << z2 << std::endl;
-    // }
-    // else { // otherwise use rphi, because rphi is more accurate
-    //     length = fabs((phi1 - phi2)*R/cos(atan(trk_par.tanLambda)));
-    // }
-    // std::cout << "phi1: " << phi1 << ", phi2: " << phi2 << ", z1: " << z1 << ", z2: " << z2 << std::endl;
-    // std::cout << "length (rphi): " << length << std::endl;
-    // std::cout << "length (z): " << fabs((z1 - z2)/sin(atan(trk_par.tanLambda))) << std::endl;
-
-    length = fabs((z1 - z2)/sin(atan(trk_par.tanLambda)));
+    if (fabs(trk_par.tanLambda) > 0.1) { // if the track is not vertical, use z
+        length = fabs((z1 - z2)/sin(atan(trk_par.tanLambda)));
+    }
+    else {
+        int outward_direction = int(fabs(z1) < fabs(z2));
+        int positive_charge = int(trk_par.omega > 0.);
+        if (outward_direction * positive_charge > 0) {
+            length = phi1 > phi2 ? (phi1 - phi2)*R/cos(atan(trk_par.tanLambda)) : (phi1 - phi2 + 2*M_PI)*R/cos(atan(trk_par.tanLambda));
+        }
+        else {
+            length = phi1 < phi2 ? (phi2 - phi1)*R/cos(atan(trk_par.tanLambda)) : (phi2 - phi1 + 2*M_PI)*R/cos(atan(trk_par.tanLambda));
+        }
+    }
+    // std::cout << "length (rphi): " << fabs((phi1 - phi2)*R/cos(atan(trk_par.tanLambda))) << std::endl;
+    // std::cout << "length (z): " << fabs((z1 - z2)/sin(atan(trk_par.tanLambda))) << std::endl << std::endl;
+    // std::cout << "length: " << length << std::endl;
 
     double pt = hc.getPXY();
     double pz = pt * hc.getTanLambda();
diff --git a/Service/SimplePIDSvc/src/SimplePIDSvc.h b/Service/SimplePIDSvc/src/SimplePIDSvc.h
index aec3da99..661accb7 100644
--- a/Service/SimplePIDSvc/src/SimplePIDSvc.h
+++ b/Service/SimplePIDSvc/src/SimplePIDSvc.h
@@ -5,6 +5,7 @@
 #include <GaudiKernel/Service.h>
 #include "DD4hep/Detector.h"
 #include "gear/GearMgr.h"
+#include "edm4hep/Vector3d.h"
 
 class TFile;
 class TH2D;
@@ -25,7 +26,7 @@ class SimplePIDSvc : public extends<Service, ISimplePIDSvc>
         double getDndxMean(double bg, double cos) override;
         double getDndxSigma(double bg, double cos, double len) override;
         double getChi2(double dndx_meas, double dndx_exp, double dndx_sigma) override;
-        void getTrackPars(const edm4hep::TrackerHit& hit1, const edm4hep::TrackerHit& hit2, const edm4hep::Track& trk, int location,
+        void getTrackPars(const edm4hep::Vector3d& hit1, const edm4hep::Vector3d& hit2, const edm4hep::Track& trk, int location,
                           double& len, double& p, double& cos) override;
         double getBetaGamma(double p, int pid_type) override;
         int getParticleType(int pdgid) override;
-- 
GitLab


From adf9646e02470cca186b30d57e3d72d08b5035c8 Mon Sep 17 00:00:00 2001
From: Guang Zhao <zhaog@ihep.ac.cn>
Date: Thu, 30 May 2024 21:12:46 +0800
Subject: [PATCH 06/10] update track length calculation and bug fixes

---
 Reconstruction/ParticleID/src/DCHDndxAlg.cpp | 111 ++++++++++++-------
 Reconstruction/ParticleID/src/DCHDndxAlg.h   |   2 +
 Reconstruction/ParticleID/src/TPCDndxAlg.cpp |  47 +++++++-
 Reconstruction/ParticleID/src/TPCDndxAlg.h   |   1 +
 4 files changed, 118 insertions(+), 43 deletions(-)

diff --git a/Reconstruction/ParticleID/src/DCHDndxAlg.cpp b/Reconstruction/ParticleID/src/DCHDndxAlg.cpp
index f7b26c3b..29110f87 100644
--- a/Reconstruction/ParticleID/src/DCHDndxAlg.cpp
+++ b/Reconstruction/ParticleID/src/DCHDndxAlg.cpp
@@ -12,6 +12,7 @@
 #include "lcio.h"
 
 #include <cmath>
+#include <fstream>
 
 using namespace edm4hep;
 
@@ -43,6 +44,7 @@ StatusCode DCHDndxAlg::initialize() {
 
 StatusCode DCHDndxAlg::execute() {
     info() << "Dndx reconstruction started" << endmsg;
+    static std::ofstream check_file("log.txt");
 
     const edm4hep::TrackCollection* trkcol = nullptr;
     const edm4hep::MCRecoTrackParticleAssociationCollection* trkparasscol = nullptr;
@@ -63,6 +65,7 @@ StatusCode DCHDndxAlg::execute() {
     //Navigation nav;
     //nav.Initialize();
     for (std::size_t i = 0; i < trkcol->size(); i++) {
+        //check_file << "Track " << i << std::endl;
         Track trk(trkcol->at(i));
         
         /// MC truth information
@@ -89,50 +92,16 @@ StatusCode DCHDndxAlg::execute() {
         double pz = trkparasscol->at(max_weight_idx).getSim().getMomentum()[2];
         p_truth = sqrt(px*px + py*py + pz*pz);
 
-        /*bool found_mc = false;
-        for (auto hit : hitcol) {
-            for (auto ha : *hitasscol) {
-                if (ha.getRec() == hit) {
-                    pdgid = ha.getSim().getMCParticle().getPDG();
-                    double px = ha.getSim().getMCParticle().getMomentum()[0];
-                    double py = ha.getSim().getMCParticle().getMomentum()[1];
-                    double pz = ha.getSim().getMCParticle().getMomentum()[2];
-                    p_truth = sqrt(px*px + py*py + pz*pz);
-                    // std::cout << "pdgid: " << pdgid << " p_truth: " << p_truth << std::endl;
-                    found_mc = true;
-                    break;
-                }
-            }
-            if (found_mc) break;
-        }
-        if (!found_mc) continue;*/
-
         /// Track parameters
         podio::RelationRange<edm4hep::TrackerHit> hitcol = trk.getTrackerHits();
         if (hitcol.size() == 0) return StatusCode::SUCCESS;
-        bool first_dc_hit = true;
+
         int ihit_first, ihit_last;
-        double zmin, zmax;
-        for (size_t ihit = 0; ihit < hitcol.size(); ihit++) {
-            auto hit = hitcol[ihit];
-            if (!isCDCHit(&hit)) {
-                continue;
-            }
-            if (first_dc_hit) {
-                zmin = hit.getPosition()[2];
-                zmax = hit.getPosition()[2];
-                first_dc_hit = false;
-            }
-            else {
-                if (hit.getPosition()[2] < zmin) {
-                    zmin = hit.getPosition()[2];
-                    ihit_first = ihit;
-                }
-                if (hit.getPosition()[2] > zmax) {
-                    zmax = hit.getPosition()[2];
-                    ihit_last = ihit;
-                }
-            }
+        if (trk.getTrackStates(0).tanLambda > 0.1) {
+            getFirstAndLastHitsByZ(hitcol, ihit_first, ihit_last);
+        }
+        else {
+            getFirstAndLastHitsByRadius(hitcol, ihit_first, ihit_last);
         }
 
         // convert the position from cm to mm
@@ -155,6 +124,7 @@ StatusCode DCHDndxAlg::execute() {
             q.value = dndx_meas;
             q.error = dndx_sigma;
 
+            //check_file << "before hypotheses loop" << std::endl;
             std::array<Hypothesis, 5> hypotheses;
             for (int pid = 0; pid < 5; pid++) {
                 bg = m_pid_svc->getBetaGamma(p_truth, pid);
@@ -168,6 +138,7 @@ StatusCode DCHDndxAlg::execute() {
 
                 hypotheses[pid] = h;
             }
+            //check_file << "after hypotheses loop" << std::endl;
 
             MutableRecDqdx dndx_track(q, particle_type, 0, hypotheses);
             dndx_track.setTrack(trk);
@@ -191,6 +162,66 @@ StatusCode DCHDndxAlg::finalize() {
     return StatusCode::SUCCESS;
 }
 
+void DCHDndxAlg::getFirstAndLastHitsByZ(const podio::RelationRange<edm4hep::TrackerHit>& hitcol, int& first, int& last) {
+    bool first_dc_hit = true;
+    double zmin, zmax;
+    for (size_t ihit = 0; ihit < hitcol.size(); ihit++) {
+        auto hit = hitcol[ihit];
+        double z = hit.getPosition()[2];
+        if (!isCDCHit(&hit)) {
+            continue;
+        }
+        if (first_dc_hit) {
+            zmin = z;
+            zmax = z;
+            first = ihit;
+            last = ihit;
+            first_dc_hit = false;
+        }
+        else {
+            if (z < zmin) {
+                zmin = z;
+                first = ihit;
+            }
+            if (z > zmax) {
+                zmax = z;
+                last = ihit;
+            }
+        }
+    }
+}
+
+void DCHDndxAlg::getFirstAndLastHitsByRadius(const podio::RelationRange<edm4hep::TrackerHit>& hitcol, int& first, int& last) {
+    bool first_dc_hit = true;
+    double rmin, rmax;
+    for (size_t ihit = 0; ihit < hitcol.size(); ihit++) {
+        auto hit = hitcol[ihit];
+        double x = hit.getPosition()[0];
+        double y = hit.getPosition()[1];
+        double r = sqrt(x*x + y*y);
+        if (!isCDCHit(&hit)) {
+            continue;
+        }
+        if (first_dc_hit) {
+            rmin = r;
+            rmax = r;
+            first = ihit;
+            last = ihit;
+            first_dc_hit = false;
+        }
+        else {
+            if (r < rmin) {
+                rmin = r;
+                first = ihit;
+            }
+            if (r > rmax) {
+                rmax = r;
+                last = ihit;
+            }
+        }
+    }
+}
+
 int DCHDndxAlg::getDetTypeID(unsigned long long cellID) const {
     UTIL::BitField64 encoder(lcio::ILDCellID0::encoder_string);
     encoder.setValue(cellID);
diff --git a/Reconstruction/ParticleID/src/DCHDndxAlg.h b/Reconstruction/ParticleID/src/DCHDndxAlg.h
index 8301662f..3601d755 100644
--- a/Reconstruction/ParticleID/src/DCHDndxAlg.h
+++ b/Reconstruction/ParticleID/src/DCHDndxAlg.h
@@ -41,6 +41,8 @@ protected:
 private:
     SmartIF<ISimplePIDSvc> m_pid_svc;
     SmartIF<IGeomSvc> m_geom_svc;
+    void getFirstAndLastHitsByZ(const podio::RelationRange<edm4hep::TrackerHit>& hitcol, int& first, int& last);
+    void getFirstAndLastHitsByRadius(const podio::RelationRange<edm4hep::TrackerHit>& hitcol, int& first, int& last);
     int getDetTypeID(unsigned long long cellID) const;
     bool isCDCHit(edm4hep::TrackerHit* hit);
 };
diff --git a/Reconstruction/ParticleID/src/TPCDndxAlg.cpp b/Reconstruction/ParticleID/src/TPCDndxAlg.cpp
index 3d5c38fb..64e7a088 100644
--- a/Reconstruction/ParticleID/src/TPCDndxAlg.cpp
+++ b/Reconstruction/ParticleID/src/TPCDndxAlg.cpp
@@ -88,10 +88,20 @@ StatusCode TPCDndxAlg::execute() {
         podio::RelationRange<edm4hep::TrackerHit> hitcol = trk.getTrackerHits();
         int nhits = hitcol.size();
         if (nhits == 0) return StatusCode::SUCCESS;
-        const Vector3d& first_hit = hitcol[0].getPosition();
-        const Vector3d& last_hit = hitcol[nhits - 1].getPosition();
+
+        int ihit_first, ihit_last;
+        if (trk.getTrackStates(0).tanLambda > 0.1) {
+            ihit_first = 0;
+            ihit_last = nhits - 1;
+        }
+        else {
+            getFirstAndLastHitsByRadius(hitcol, ihit_first, ihit_last);
+        }
+    
+        const Vector3d& first_hit_pos = hitcol[ihit_first].getPosition();
+        const Vector3d& last_hit_pos = hitcol[ihit_last].getPosition();
         double len, p, cos;
-        m_pid_svc->getTrackPars(first_hit, last_hit, trk, TrackState::AtFirstHit, len, p, cos);
+        m_pid_svc->getTrackPars(first_hit_pos, last_hit_pos, trk, TrackState::AtFirstHit, len, p, cos);
 
         /// dN/dx reconstruction
         if (m_method.value() == "Simple") { // Track level implementation
@@ -140,3 +150,34 @@ StatusCode TPCDndxAlg::finalize() {
 
     return StatusCode::SUCCESS;
 }
+
+void TPCDndxAlg::getFirstAndLastHitsByRadius(const podio::RelationRange<edm4hep::TrackerHit>& hitcol, int& first, int& last) {
+    bool first_hit = true;
+    double rmin, rmax;
+    for (size_t ihit = 0; ihit < hitcol.size(); ihit++) {
+        auto hit = hitcol[ihit];
+        double x = hit.getPosition()[0];
+        double y = hit.getPosition()[1];
+        double r = sqrt(x*x + y*y);
+        // if (!isCDCHit(&hit)) {
+        //     continue;
+        // }
+        if (first_hit) {
+            rmin = r;
+            rmax = r;
+            first = ihit;
+            last = ihit;
+            first_hit = false;
+        }
+        else {
+            if (r < rmin) {
+                rmin = r;
+                first = ihit;
+            }
+            if (r > rmax) {
+                rmax = r;
+                last = ihit;
+            }
+        }
+    }
+}
diff --git a/Reconstruction/ParticleID/src/TPCDndxAlg.h b/Reconstruction/ParticleID/src/TPCDndxAlg.h
index 9c508e0b..c8cd699e 100644
--- a/Reconstruction/ParticleID/src/TPCDndxAlg.h
+++ b/Reconstruction/ParticleID/src/TPCDndxAlg.h
@@ -38,6 +38,7 @@ protected:
 
 private:
     SmartIF<ISimplePIDSvc> m_pid_svc;
+    void getFirstAndLastHitsByRadius(const podio::RelationRange<edm4hep::TrackerHit>& hitcol, int& first, int& last);
 };
 
 #endif
-- 
GitLab


From 4259ef8fd0cee0441ff3538a10d843687fccb431 Mon Sep 17 00:00:00 2001
From: Guang Zhao <zhaog@ihep.ac.cn>
Date: Fri, 31 May 2024 15:46:30 +0800
Subject: [PATCH 07/10] bug fixes

---
 Reconstruction/ParticleID/src/DCHDndxAlg.cpp | 17 +++++++++++++++--
 Reconstruction/ParticleID/src/TPCDndxAlg.cpp |  4 +++-
 2 files changed, 18 insertions(+), 3 deletions(-)

diff --git a/Reconstruction/ParticleID/src/DCHDndxAlg.cpp b/Reconstruction/ParticleID/src/DCHDndxAlg.cpp
index 29110f87..b5703b12 100644
--- a/Reconstruction/ParticleID/src/DCHDndxAlg.cpp
+++ b/Reconstruction/ParticleID/src/DCHDndxAlg.cpp
@@ -45,6 +45,9 @@ StatusCode DCHDndxAlg::initialize() {
 StatusCode DCHDndxAlg::execute() {
     info() << "Dndx reconstruction started" << endmsg;
     static std::ofstream check_file("log.txt");
+    static int ievent = 0;
+    check_file << "--------                 Event " << ievent++ << " --------" << std::endl;
+    //if (ievent <= 262) return StatusCode::SUCCESS;
 
     const edm4hep::TrackCollection* trkcol = nullptr;
     const edm4hep::MCRecoTrackParticleAssociationCollection* trkparasscol = nullptr;
@@ -62,10 +65,11 @@ StatusCode DCHDndxAlg::execute() {
 
     RecDqdxCollection* outCol = _dndxCol.createAndPut();
 
+    check_file << "before track loop" << std::endl;
     //Navigation nav;
     //nav.Initialize();
     for (std::size_t i = 0; i < trkcol->size(); i++) {
-        //check_file << "Track " << i << std::endl;
+        check_file << "  Track " << i << std::endl;
         Track trk(trkcol->at(i));
         
         /// MC truth information
@@ -92,18 +96,25 @@ StatusCode DCHDndxAlg::execute() {
         double pz = trkparasscol->at(max_weight_idx).getSim().getMomentum()[2];
         p_truth = sqrt(px*px + py*py + pz*pz);
 
+        check_file << "before track par" << std::endl;
+
         /// Track parameters
         podio::RelationRange<edm4hep::TrackerHit> hitcol = trk.getTrackerHits();
+        check_file << "hitcol size = " << hitcol.size() << std::endl;
         if (hitcol.size() == 0) return StatusCode::SUCCESS;
 
-        int ihit_first, ihit_last;
+        check_file << "before get track state" << std::endl;
+        int ihit_first = -1;
+        int ihit_last = -1;
         if (trk.getTrackStates(0).tanLambda > 0.1) {
             getFirstAndLastHitsByZ(hitcol, ihit_first, ihit_last);
         }
         else {
             getFirstAndLastHitsByRadius(hitcol, ihit_first, ihit_last);
         }
+        if (ihit_first < 0 || ihit_last < 0 || ihit_first == ihit_last) continue;
 
+        check_file << "before conversion unit. ihit_first, ihit_last = " << ihit_first << ", " << ihit_last << std::endl;
         // convert the position from cm to mm
         Vector3d first_hit_pos = Vector3d(hitcol[ihit_first].getPosition().x*10.0, hitcol[ihit_first].getPosition().y*10.0, hitcol[ihit_first].getPosition().z*10.0);
         Vector3d last_hit_pos = Vector3d(hitcol[ihit_last].getPosition().x*10.0, hitcol[ihit_last].getPosition().y*10.0, hitcol[ihit_last].getPosition().z*10.0);
@@ -111,6 +122,8 @@ StatusCode DCHDndxAlg::execute() {
         m_pid_svc->getTrackPars(first_hit_pos, last_hit_pos, trk, 0, len, p, cos);
         //len = len * 10.; // convert from cm to mm
 
+        check_file << "before dndx calc" << std::endl;
+
         /// dN/dx reconstruction
         if (m_method.value() == "Simple") { // Track level implementation
             int particle_type = m_pid_svc->getParticleType(pdgid);
diff --git a/Reconstruction/ParticleID/src/TPCDndxAlg.cpp b/Reconstruction/ParticleID/src/TPCDndxAlg.cpp
index 64e7a088..ca43923d 100644
--- a/Reconstruction/ParticleID/src/TPCDndxAlg.cpp
+++ b/Reconstruction/ParticleID/src/TPCDndxAlg.cpp
@@ -89,7 +89,8 @@ StatusCode TPCDndxAlg::execute() {
         int nhits = hitcol.size();
         if (nhits == 0) return StatusCode::SUCCESS;
 
-        int ihit_first, ihit_last;
+        int ihit_first = -1;
+        int ihit_last = -1;
         if (trk.getTrackStates(0).tanLambda > 0.1) {
             ihit_first = 0;
             ihit_last = nhits - 1;
@@ -97,6 +98,7 @@ StatusCode TPCDndxAlg::execute() {
         else {
             getFirstAndLastHitsByRadius(hitcol, ihit_first, ihit_last);
         }
+        if (ihit_first < 0 || ihit_last < 0 || ihit_first == ihit_last) continue;
     
         const Vector3d& first_hit_pos = hitcol[ihit_first].getPosition();
         const Vector3d& last_hit_pos = hitcol[ihit_last].getPosition();
-- 
GitLab


From 3c187cd61c9055a1dd9f81b71d5e8bac732042d8 Mon Sep 17 00:00:00 2001
From: Guang Zhao <zhaog@ihep.ac.cn>
Date: Fri, 31 May 2024 16:43:21 +0800
Subject: [PATCH 08/10] bug fix

---
 Reconstruction/ParticleID/src/DCHDndxAlg.cpp | 11 ++++++++++-
 Reconstruction/ParticleID/src/TPCDndxAlg.cpp |  9 ++++++++-
 2 files changed, 18 insertions(+), 2 deletions(-)

diff --git a/Reconstruction/ParticleID/src/DCHDndxAlg.cpp b/Reconstruction/ParticleID/src/DCHDndxAlg.cpp
index b5703b12..0b1660cd 100644
--- a/Reconstruction/ParticleID/src/DCHDndxAlg.cpp
+++ b/Reconstruction/ParticleID/src/DCHDndxAlg.cpp
@@ -106,11 +106,20 @@ StatusCode DCHDndxAlg::execute() {
         check_file << "before get track state" << std::endl;
         int ihit_first = -1;
         int ihit_last = -1;
-        if (trk.getTrackStates(0).tanLambda > 0.1) {
+        TrackState trk_par;
+        for (auto it = trk.trackStates_begin(); it != trk.trackStates_end(); it++) {
+            if (it->location == 0) {
+                trk_par = *it;
+                break;
+            }
+        }
+        if (trk_par.tanLambda > 0.1) {
             getFirstAndLastHitsByZ(hitcol, ihit_first, ihit_last);
         }
         else {
+            check_file << "before get first and last by r" << std::endl;
             getFirstAndLastHitsByRadius(hitcol, ihit_first, ihit_last);
+            check_file << "after get first and last by r" << std::endl;
         }
         if (ihit_first < 0 || ihit_last < 0 || ihit_first == ihit_last) continue;
 
diff --git a/Reconstruction/ParticleID/src/TPCDndxAlg.cpp b/Reconstruction/ParticleID/src/TPCDndxAlg.cpp
index ca43923d..2bd63ab5 100644
--- a/Reconstruction/ParticleID/src/TPCDndxAlg.cpp
+++ b/Reconstruction/ParticleID/src/TPCDndxAlg.cpp
@@ -91,7 +91,14 @@ StatusCode TPCDndxAlg::execute() {
 
         int ihit_first = -1;
         int ihit_last = -1;
-        if (trk.getTrackStates(0).tanLambda > 0.1) {
+        TrackState trk_par;
+        for (auto it = trk.trackStates_begin(); it != trk.trackStates_end(); it++) {
+            if (it->location == TrackState::AtFirstHit) {
+                trk_par = *it;
+                break;
+            }
+        }
+        if (trk_par.tanLambda > 0.1) {
             ihit_first = 0;
             ihit_last = nhits - 1;
         }
-- 
GitLab


From 77b33c493554239f483727ef9f2c285e801c3e4d Mon Sep 17 00:00:00 2001
From: Guang Zhao <zhaog@ihep.ac.cn>
Date: Fri, 31 May 2024 23:41:20 +0800
Subject: [PATCH 09/10] remove comments

---
 Reconstruction/ParticleID/src/DCHDndxAlg.cpp | 28 +++++++++-----------
 1 file changed, 12 insertions(+), 16 deletions(-)

diff --git a/Reconstruction/ParticleID/src/DCHDndxAlg.cpp b/Reconstruction/ParticleID/src/DCHDndxAlg.cpp
index 0b1660cd..ed9cf4af 100644
--- a/Reconstruction/ParticleID/src/DCHDndxAlg.cpp
+++ b/Reconstruction/ParticleID/src/DCHDndxAlg.cpp
@@ -44,10 +44,9 @@ StatusCode DCHDndxAlg::initialize() {
 
 StatusCode DCHDndxAlg::execute() {
     info() << "Dndx reconstruction started" << endmsg;
-    static std::ofstream check_file("log.txt");
-    static int ievent = 0;
-    check_file << "--------                 Event " << ievent++ << " --------" << std::endl;
-    //if (ievent <= 262) return StatusCode::SUCCESS;
+    // static std::ofstream check_file("log.txt");
+    // static int ievent = 0;
+    // check_file << "--------                 Event " << ievent++ << " --------" << std::endl;
 
     const edm4hep::TrackCollection* trkcol = nullptr;
     const edm4hep::MCRecoTrackParticleAssociationCollection* trkparasscol = nullptr;
@@ -65,11 +64,10 @@ StatusCode DCHDndxAlg::execute() {
 
     RecDqdxCollection* outCol = _dndxCol.createAndPut();
 
-    check_file << "before track loop" << std::endl;
-    //Navigation nav;
-    //nav.Initialize();
+    // check_file << "before track loop" << std::endl;
+
     for (std::size_t i = 0; i < trkcol->size(); i++) {
-        check_file << "  Track " << i << std::endl;
+        // check_file << "  Track " << i << std::endl;
         Track trk(trkcol->at(i));
         
         /// MC truth information
@@ -96,14 +94,13 @@ StatusCode DCHDndxAlg::execute() {
         double pz = trkparasscol->at(max_weight_idx).getSim().getMomentum()[2];
         p_truth = sqrt(px*px + py*py + pz*pz);
 
-        check_file << "before track par" << std::endl;
+        // check_file << "before track par" << std::endl;
 
         /// Track parameters
         podio::RelationRange<edm4hep::TrackerHit> hitcol = trk.getTrackerHits();
-        check_file << "hitcol size = " << hitcol.size() << std::endl;
         if (hitcol.size() == 0) return StatusCode::SUCCESS;
 
-        check_file << "before get track state" << std::endl;
+        // check_file << "before get track state" << std::endl;
         int ihit_first = -1;
         int ihit_last = -1;
         TrackState trk_par;
@@ -117,21 +114,20 @@ StatusCode DCHDndxAlg::execute() {
             getFirstAndLastHitsByZ(hitcol, ihit_first, ihit_last);
         }
         else {
-            check_file << "before get first and last by r" << std::endl;
+            // check_file << "before get first and last by r" << std::endl;
             getFirstAndLastHitsByRadius(hitcol, ihit_first, ihit_last);
-            check_file << "after get first and last by r" << std::endl;
         }
         if (ihit_first < 0 || ihit_last < 0 || ihit_first == ihit_last) continue;
 
-        check_file << "before conversion unit. ihit_first, ihit_last = " << ihit_first << ", " << ihit_last << std::endl;
+        // check_file << "before conversion unit. ihit_first, ihit_last = " << ihit_first << ", " << ihit_last << std::endl;
+        
         // convert the position from cm to mm
         Vector3d first_hit_pos = Vector3d(hitcol[ihit_first].getPosition().x*10.0, hitcol[ihit_first].getPosition().y*10.0, hitcol[ihit_first].getPosition().z*10.0);
         Vector3d last_hit_pos = Vector3d(hitcol[ihit_last].getPosition().x*10.0, hitcol[ihit_last].getPosition().y*10.0, hitcol[ihit_last].getPosition().z*10.0);
         double len, p, cos;
         m_pid_svc->getTrackPars(first_hit_pos, last_hit_pos, trk, 0, len, p, cos);
-        //len = len * 10.; // convert from cm to mm
 
-        check_file << "before dndx calc" << std::endl;
+        // check_file << "before dndx calc" << std::endl;
 
         /// dN/dx reconstruction
         if (m_method.value() == "Simple") { // Track level implementation
-- 
GitLab


From 61c07b089ae5a5e88bbfce439ecee64e5f7ad263 Mon Sep 17 00:00:00 2001
From: Guang Zhao <zhaog@ihep.ac.cn>
Date: Sun, 2 Jun 2024 10:57:32 +0800
Subject: [PATCH 10/10] remove uneccesary functions

---
 Reconstruction/ParticleID/src/TPCDndxAlg.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Reconstruction/ParticleID/src/TPCDndxAlg.cpp b/Reconstruction/ParticleID/src/TPCDndxAlg.cpp
index 2bd63ab5..0af4dbc2 100644
--- a/Reconstruction/ParticleID/src/TPCDndxAlg.cpp
+++ b/Reconstruction/ParticleID/src/TPCDndxAlg.cpp
@@ -55,8 +55,8 @@ StatusCode TPCDndxAlg::execute() {
 
     RecDqdxCollection* outCol = _dndxCol.createAndPut();
 
-    Navigation nav;
-    nav.Initialize();
+    // Navigation nav;
+    // nav.Initialize();
     for (std::size_t i = 0; i < trkcol->size(); i++) {
         Track trk(trkcol->at(i));
         
-- 
GitLab