diff --git a/Simulation/DetSimGeom/src/AnExampleDetElemTool.cpp b/Simulation/DetSimGeom/src/AnExampleDetElemTool.cpp
index fffebf7940367aa72b08de7fba925a034efea7bd..001675db9cd978911b736fd95e35c7bb59dc7f1f 100644
--- a/Simulation/DetSimGeom/src/AnExampleDetElemTool.cpp
+++ b/Simulation/DetSimGeom/src/AnExampleDetElemTool.cpp
@@ -120,6 +120,16 @@ AnExampleDetElemTool::ConstructSDandField() {
warning() << "DriftChamberSensDetTool is not found. " << endmsg;
}
}
+ else if (nam == "TPC") {
+ m_tpc_sdtool = ToolHandle<ISensDetTool>("TimeProjectionChamberSensDetTool");
+ if (m_tpc_sdtool) {
+ info() << "Find the TimeProjectionChamberSensDetTool" << endmsg;
+ g4sd = m_tpc_sdtool->createSD(nam);
+ }
+ else {
+ warning() << "TimeProjectionChamberSensDetTool is not found, and default tracker SD will be used" << endmsg;
+ }
+ }
}
}
@@ -199,6 +209,7 @@ AnExampleDetElemTool::initialize() {
m_calo_sdtool = ToolHandle<ISensDetTool>("CalorimeterSensDetTool");
m_driftchamber_sdtool = ToolHandle<ISensDetTool>("DriftChamberSensDetTool");
+ m_tpc_sdtool = ToolHandle<ISensDetTool>("TimeProjectionChamberSensDetTool");
return sc;
}
diff --git a/Simulation/DetSimGeom/src/AnExampleDetElemTool.h b/Simulation/DetSimGeom/src/AnExampleDetElemTool.h
index 31fcfc0ea420185f3e15fcc9f8502e9f6d4ccb7e..908de073479727ff2b035c8b703ad9e4fca38522 100644
--- a/Simulation/DetSimGeom/src/AnExampleDetElemTool.h
+++ b/Simulation/DetSimGeom/src/AnExampleDetElemTool.h
@@ -34,6 +34,7 @@ private:
SmartIF<IGeomSvc> m_geosvc;
ToolHandle<ISensDetTool> m_calo_sdtool;
ToolHandle<ISensDetTool> m_driftchamber_sdtool;
+ ToolHandle<ISensDetTool> m_tpc_sdtool;
};
#endif
diff --git a/Simulation/DetSimSD/CMakeLists.txt b/Simulation/DetSimSD/CMakeLists.txt
index 1828c1e1123331776a305923b83876f4d8c14f7f..33d00e18e00177f47582ad8ab7679c1debc2d08b 100644
--- a/Simulation/DetSimSD/CMakeLists.txt
+++ b/Simulation/DetSimSD/CMakeLists.txt
@@ -19,6 +19,9 @@ set(DetSimSD_srcs
src/DriftChamberSensDetTool.cpp
src/DriftChamberSensitiveDetector.cpp
+
+ src/TimeProjectionChamberSensDetTool.cpp
+ src/TimeProjectionChamberSensitiveDetector.cpp
)
gaudi_add_module(DetSimSD ${DetSimSD_srcs}
diff --git a/Simulation/DetSimSD/DetSimSD/DDG4SensitiveDetector.h b/Simulation/DetSimSD/DetSimSD/DDG4SensitiveDetector.h
index f7806f7de5f97c5cf36a60c6b22e6d1491ccb24e..cee8826ee89012deb132843baa924dffef193a20 100644
--- a/Simulation/DetSimSD/DetSimSD/DDG4SensitiveDetector.h
+++ b/Simulation/DetSimSD/DetSimSD/DDG4SensitiveDetector.h
@@ -45,12 +45,12 @@ public:
/// Returns the volumeID of the sensitive volume corresponding to the step -
/// combining the VolIDS of the complete geometry path (Geant4TouchableHistory)
// from the current sensitive volume to the world volume
- virtual long long getVolumeID(G4Step* step);
+ virtual long long getVolumeID(const G4Step* step);
/// Returns the volumeID of the sensitive volume corresponding to the step -
/// combining the VolIDS of the complete geometry path (Geant4TouchableHistory)
// from the current sensitive volume to the world volume
- virtual long long getCellID(G4Step* step);
+ virtual long long getCellID(const G4Step* step);
protected:
diff --git a/Simulation/DetSimSD/src/DDG4SensitiveDetector.cpp b/Simulation/DetSimSD/src/DDG4SensitiveDetector.cpp
index 25ce06c0a2da935a1d8d7e4bad09e360962f1985..9d12dde315b8f45e1deb2606999598e69c0161c2 100644
--- a/Simulation/DetSimSD/src/DDG4SensitiveDetector.cpp
+++ b/Simulation/DetSimSD/src/DDG4SensitiveDetector.cpp
@@ -48,7 +48,7 @@ DDG4SensitiveDetector::EndOfEvent(G4HCofThisEvent* HCE) {
}
long long
-DDG4SensitiveDetector::getVolumeID(G4Step* aStep) {
+DDG4SensitiveDetector::getVolumeID(const G4Step* aStep) {
dd4hep::sim::Geant4StepHandler step(aStep);
dd4hep::sim::Geant4VolumeManager volMgr = dd4hep::sim::Geant4Mapping::instance().volumeManager();
@@ -58,7 +58,7 @@ DDG4SensitiveDetector::getVolumeID(G4Step* aStep) {
}
long long
-DDG4SensitiveDetector::getCellID(G4Step* step) {
+DDG4SensitiveDetector::getCellID(const G4Step* step) {
dd4hep::sim::Geant4StepHandler h(step);
dd4hep::sim::Geant4VolumeManager volMgr = dd4hep::sim::Geant4Mapping::instance().volumeManager();
dd4hep::VolumeID volID = volMgr.volumeID(h.preTouchable());
diff --git a/Simulation/DetSimSD/src/TimeProjectionChamberSensDetTool.cpp b/Simulation/DetSimSD/src/TimeProjectionChamberSensDetTool.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9ca7b471af8832494703c9fab48febd49e72d6df
--- /dev/null
+++ b/Simulation/DetSimSD/src/TimeProjectionChamberSensDetTool.cpp
@@ -0,0 +1,57 @@
+#include "TimeProjectionChamberSensDetTool.h"
+
+#include "G4VSensitiveDetector.hh"
+
+#include "DD4hep/Detector.h"
+
+#include "TimeProjectionChamberSensitiveDetector.h"
+
+#include "CLHEP/Units/SystemOfUnits.h"
+
+DECLARE_COMPONENT(TimeProjectionChamberSensDetTool);
+
+StatusCode TimeProjectionChamberSensDetTool::initialize() {
+ StatusCode sc;
+ debug() << "initialize() " << endmsg;
+ debug() << "TypeOption = " << m_sdTypeOption.value() << endmsg;
+
+ m_geosvc = service<IGeomSvc>("GeomSvc");
+ if (!m_geosvc) {
+ error() << "Failed to find GeomSvc." << endmsg;
+ return StatusCode::FAILURE;
+ }
+
+ return AlgTool::initialize();
+}
+
+StatusCode TimeProjectionChamberSensDetTool::finalize() {
+ StatusCode sc;
+
+ return sc;
+}
+
+G4VSensitiveDetector* TimeProjectionChamberSensDetTool::createSD(const std::string& name) {
+ debug() << "createSD for TPC" << endmsg;
+
+ dd4hep::Detector* dd4hep_geo = m_geosvc->lcdd();
+
+ G4VSensitiveDetector* sd = nullptr;
+ if (name == "TPC") {
+ if(m_sdTypeOption==1/*FIXME: const defined by CEPC whole description*/){
+ TimeProjectionChamberSensitiveDetector* tpcsd = new TimeProjectionChamberSensitiveDetector(name, *dd4hep_geo);
+ // switch units to Geant4's, Geant4 uses CLHEP units in fact
+ tpcsd->setThreshold(m_threshold/dd4hep::eV*CLHEP::eV); //FIXME: let SD get threshold from dd4hep_geo object
+ tpcsd->setSameStepLimit(m_sameStepLimit);
+ tpcsd->setWriteMCTruthForLowPtHits(m_writeMCTruthForLowPtHits);
+ tpcsd->setLowPtCut(m_lowPtCut/dd4hep::MeV*CLHEP::MeV);
+ tpcsd->setLowPtMaxHitSeparation(m_lowPtMaxHitSeparation/dd4hep::mm*CLHEP::mm);
+ sd = tpcsd;
+ info() << "TPC will use TimeProjectionChamberSensitiveDetector" << endmsg;
+ }
+ else{
+ info() << "TPC will use default Geant4TrackerHit SD " << endmsg;
+ }
+ }
+
+ return sd;
+}
diff --git a/Simulation/DetSimSD/src/TimeProjectionChamberSensDetTool.h b/Simulation/DetSimSD/src/TimeProjectionChamberSensDetTool.h
new file mode 100644
index 0000000000000000000000000000000000000000..89a8593ec99f2a54a059075e9096af3794ed669a
--- /dev/null
+++ b/Simulation/DetSimSD/src/TimeProjectionChamberSensDetTool.h
@@ -0,0 +1,40 @@
+#ifndef TimeProjectionChamberSensDetTool_h
+#define TimeProjectionChamberSensDetTool_h
+
+/*
+ * TimeProjectionChamberSensDetTool is used to create Time Projection Chamber SD.
+ */
+
+#include "GaudiKernel/AlgTool.h"
+#include "GaudiKernel/ToolHandle.h"
+#include "DetSimInterface/ISensDetTool.h"
+#include "DetInterface/IGeomSvc.h"
+
+#include "DD4hep/DD4hepUnits.h"
+
+class TimeProjectionChamberSensDetTool: public extends<AlgTool, ISensDetTool> {
+
+ public:
+
+ using extends::extends;
+
+ /// Overriding initialize and finalize
+ StatusCode initialize() override;
+ StatusCode finalize() override;
+
+ /// Override ISensDetTool
+ virtual G4VSensitiveDetector* createSD(const std::string& name) override;
+
+private:
+
+ // in order to initialize SD, we need to get the lcdd()
+ SmartIF<IGeomSvc> m_geosvc;
+ Gaudi::Property<int> m_sdTypeOption{this, "TypeOption", 0};
+ Gaudi::Property<double> m_threshold{this, "ThresholdEnergyDeposit", 32*dd4hep::eV};
+ Gaudi::Property<double> m_lowPtCut{this, "LowPtCut", 10*dd4hep::MeV};
+ Gaudi::Property<double> m_lowPtMaxHitSeparation{this, "LowPtMaxHitSeparation", 5*dd4hep::mm};
+ Gaudi::Property<bool> m_sameStepLimit{this, "SameStepLimit", true};
+ Gaudi::Property<bool> m_writeMCTruthForLowPtHits{this, "WriteMCTruthForLowPtHits", false};
+};
+
+#endif
diff --git a/Simulation/DetSimSD/src/TimeProjectionChamberSensitiveDetector.cpp b/Simulation/DetSimSD/src/TimeProjectionChamberSensitiveDetector.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..fdb1ff09ce8b5fff32a2929b90d5255db8858c15
--- /dev/null
+++ b/Simulation/DetSimSD/src/TimeProjectionChamberSensitiveDetector.cpp
@@ -0,0 +1,331 @@
+#include "TimeProjectionChamberSensitiveDetector.h"
+
+#include "G4Step.hh"
+#include "G4VProcess.hh"
+//#include "G4SteppingManager.hh"
+#include "G4SDManager.hh"
+//#include "UserTrackInformation.hh"
+#include "DD4hep/DD4hepUnits.h"
+
+TimeProjectionChamberSensitiveDetector::TimeProjectionChamberSensitiveDetector(const std::string& name,
+ dd4hep::Detector& description)
+ : DDG4SensitiveDetector(name, description){
+
+ G4String CollName1=name+"Collection";
+ collectionName.insert(CollName1);
+
+ G4String CollName2=name+"SpacePointCollection";
+ collectionName.insert(CollName2);
+
+ G4String CollName3=name+"LowPtCollection";
+ collectionName.insert(CollName3);
+}
+
+void TimeProjectionChamberSensitiveDetector::Initialize(G4HCofThisEvent* HCE){
+ m_hc = new HitCollection(GetName(), collectionName[0]);
+ int HCID = G4SDManager::GetSDMpointer()->GetCollectionID(m_hc);
+ HCE->AddHitsCollection( HCID, m_hc );
+
+ m_spaceHC = new HitCollection(GetName(), collectionName[1]);
+ int HCIDSpace = G4SDManager::GetSDMpointer()->GetCollectionID(m_spaceHC);
+ HCE->AddHitsCollection( HCIDSpace, m_spaceHC );
+
+ m_lowPtHC = new HitCollection(GetName(), collectionName[2]);
+ int HCIDLowPt = G4SDManager::GetSDMpointer()->GetCollectionID(m_lowPtHC);
+ HCE->AddHitsCollection( HCIDLowPt, m_lowPtHC );
+
+ CumulativeNumSteps=0;
+
+ dEInPadRow = 0.0;
+ CumulativeEnergyDeposit = 0.0;
+ globalTimeAtPadRingCentre=0.0;
+ pathLengthInPadRow=0.0;
+ CumulativePathLength=0.0;
+ CrossingOfPadRingCentre.SetXYZ(0,0,0);// = dd4hep::Position(0,0,0);
+ MomentumAtPadRingCentre.SetXYZ(0,0,0);// = dd4hep::sim::Momentum(0,0,0);
+
+ CumulativeMeanPosition.SetXYZ(0,0,0);// = dd4hep::Position(0,0,0);
+ CumulativeMeanMomentum.SetXYZ(0,0,0);// = dd4hep::sim::Momentum(0,0,0);
+
+ PreviousPostStepPosition.SetXYZ(0,0,0);// = dd4hep::Position(0,0,0);
+ CurrentPDGEncoding=0;
+ CurrentTrackID=-1;
+ CurrentGlobalTime=0.0;
+ CurrentCopyNumber=0;
+
+ /*
+ if( m_lowPtStepLimit ) {
+ std::cout << "TimeProjectionChamberSensitiveDetector: TPCLowPtStepLimit ON" << std::endl;
+ }
+ else {
+ std::cout << "TimeProjectionChamberSensitiveDetector: TPCLowPtStepLimit OFF" << std::endl;
+ }
+
+ if( m_writeMCParticleForLowPtHits ) {
+ std::cout << "TimeProjectionChamberSensitiveDetector: TPCLowPtStoreMCPForHits ON" << std::endl;
+ }
+ else {
+ std::cout << "TimeProjectionChamberSensitiveDetector: TPCLowPtStoreMCPForHits OFF" << std::endl;
+ }
+
+ std::cout << "TimeProjectionChamberSensitiveDetector: Threshold for Energy Deposit = " << m_thresholdEnergyDeposit / CLHEP::eV << " eV" << G4endl;
+ std::cout << "TimeProjectionChamberSensitiveDetector: TPCLowPtCut = " << m_lowPtCut / CLHEP::MeV << " MeV "<< G4endl;
+ std::cout << "TimeProjectionChamberSensitiveDetector: TPCLowPt Max hit separation "<< m_lowPtMaxHitSeparation / CLHEP::mm << " mm" << G4endl;
+ */
+}
+
+G4bool TimeProjectionChamberSensitiveDetector::ProcessHits(G4Step* step, G4TouchableHistory*){
+ // FIXME:
+ // (i) in the following algorithm if a particle "appears" within a pad-ring half and
+ // leaves without passing through the middle of the pad-ring it will not create a hit in
+ // this ring
+ // (ii) a particle that crosses the boundry between two pad-ring halves will have the hit
+ // placed on this surface at the last crossing point, and will be assinged the total energy
+ // deposited in the whole pad-ring. This is a possible source of bias for the hit
+
+ G4TouchableHandle touchPost = step->GetPostStepPoint()->GetTouchableHandle();
+ G4TouchableHandle touchPre = step->GetPreStepPoint()->GetTouchableHandle();
+ dd4hep::sim::Geant4StepHandler h(step);
+
+ if (fabs(h.trackDef()->GetPDGCharge()) < 0.01) return true;
+
+ const dd4hep::Position PrePosition = h.prePos();
+ const dd4hep::Position PostPosition = h.postPos();
+ const dd4hep::sim::Momentum Momentum = h.postMom(); //use post step momentum, Mokka history
+
+ float ptSQRD = Momentum.X()*Momentum.X()+Momentum.Y()*Momentum.Y();
+ if( ptSQRD >= (m_lowPtCut*m_lowPtCut) ){
+ // Step finishes at a geometric boundry
+ if(step->GetPostStepPoint()->GetStepStatus() == fGeomBoundary){
+ //if(h.postStepStatus() == "GeomBoundary"){
+ // step within the same pair of upper and lower pad ring halves
+ if(int(touchPre->GetCopyNumber()/2)==int(touchPost->GetCopyNumber()/2)) {
+ //this step must have ended on the boundry between these two pad ring halfs
+ //record the tracks coordinates at this position
+ //and return
+
+ CrossingOfPadRingCentre.SetXYZ(PostPosition.X(),PostPosition.Y(),PostPosition.Z());
+ MomentumAtPadRingCentre = Momentum;
+ dEInPadRow += h.deposit();
+ globalTimeAtPadRingCentre = h.trkTime();
+ pathLengthInPadRow += h.stepLength();
+
+ // G4cout << "step must have ended on the boundry between these two pad ring halfs" << G4endl;
+ // G4cout << "CrossingOfPadRingCentre = "
+ // << sqrt( CrossingOfPadRingCentre[0]*CrossingOfPadRingCentre[0]
+ // +CrossingOfPadRingCentre[1]*CrossingOfPadRingCentre[1] )
+ // << G4endl;
+
+ return true;
+ }
+ else if(!(CrossingOfPadRingCentre.X()==0.0 && CrossingOfPadRingCentre.Y()==0.0 && CrossingOfPadRingCentre.Z()==0.0)) {
+ // the above IF statment is to catch the case where the particle "appears" in this pad-row half volume and
+ // leaves with out crossing the pad-ring centre, as mentioned above
+
+ //it is leaving the pad ring couplet
+ //write out a hit
+ //make sure particle is added to MC list
+ //and return
+ // G4cout << "step must be leaving the pad ring couplet" << G4endl;
+ // G4cout << "write out hit at "
+ // << sqrt( CrossingOfPadRingCentre[0]*CrossingOfPadRingCentre[0]
+ // +CrossingOfPadRingCentre[1]*CrossingOfPadRingCentre[1] )
+ // << " " << "dEdx = " << step->GetTotalEnergyDeposit()+dEInPadRow
+ // << " " << "step length = " << step->GetStepLength()+pathLengthInPadRow
+ // << G4endl;
+
+ double dE = h.deposit()+dEInPadRow;
+ if ( dE > m_thresholdEnergyDeposit || m_trackingPhysicsListELossOn == false ) {
+ // needed for delta electrons: all particles causing hits have to be saved
+ // TODO: set track flag to save
+
+ dd4hep::sim::Geant4TrackerHit* hit = new dd4hep::sim::Geant4TrackerHit(h.trkID(),h.trkPdgID(),dE,globalTimeAtPadRingCentre);
+ hit->cellID = touchPre->GetCopyNumber()/2+1;//getCellID(step);
+ hit->energyDeposit = dE;
+ hit->position = CrossingOfPadRingCentre;
+ hit->momentum = MomentumAtPadRingCentre;
+ hit->length = h.stepLength()+pathLengthInPadRow;
+ m_hc->insert(hit);
+ }
+
+ // zero cumulative variables
+ dEInPadRow = 0.0;
+ globalTimeAtPadRingCentre = 0.0;
+ pathLengthInPadRow = 0.0;
+ CrossingOfPadRingCentre.SetXYZ(0.,0.,0.);
+ MomentumAtPadRingCentre.SetXYZ(0.,0.,0.);
+ return true;
+ }
+ }
+ //case for which the step remains within geometric volume
+ //FIXME: need and another IF case to catch particles which Stop within the padring
+ else if(step->GetPostStepPoint()->GetStepStatus() != fGeomBoundary){
+ //else if(h.postStepStatus() != "GeomBoundary") {
+ //the step is not limited by the step length
+ if(step->GetPostStepPoint()->GetProcessDefinedStep()->GetProcessName()!="StepLimiter"){
+ // if(particle not stoped){
+ //add the dEdx and return
+ // G4cout << "Step ended by Physics Process: Add dEdx and carry on" << G4endl;
+ dEInPadRow += h.deposit();
+ pathLengthInPadRow += h.stepLength();
+ return true;
+ //}
+ //else{
+ // write out the hit and clear counters
+ //}
+ }
+ else {
+ //double position_x = PostPosition[0];
+ //double position_y = PostPosition[1];
+ //double position_z = PostPosition[2];
+
+ const dd4hep::sim::Momentum PreMomentum = h.preMom();
+ const dd4hep::sim::Momentum PostMomentum = h.postMom();
+
+ //double momentum_x = PostMomentum[0];
+ //double momentum_y = PostMomentum[1];
+ //double momentum_z = PostMomentum[2];
+
+
+ // G4cout << "step must have been stopped by the step limiter" << G4endl;
+ // G4cout << "write out hit at "
+ // << sqrt( position_x*position_x
+ // +position_y*position_y )
+ // << " " << "dEdx = " << step->GetTotalEnergyDeposit()
+ // << " " << "step length = " << step->GetStepLength()
+ // << G4endl;
+
+ // write out step limited hit
+ // these are just space point hits so do not save the dE, which is set to ZERO
+ // if ( step->GetTotalEnergyDeposit() > fThresholdEnergyDeposit )
+
+ // needed for delta electrons: all particles causing hits have to be saved in the LCIO file
+ // TODO: set track flag to be saved
+ //UserTrackInformation *info = (UserTrackInformation*)(step->GetTrack()->GetUserInformation());
+ //if (info) info->GetTheTrackSummary()->SetToBeSaved();
+
+ dd4hep::sim::Geant4TrackerHit* hit = new dd4hep::sim::Geant4TrackerHit(h.trkID(),h.trkPdgID(),0.0,h.trkTime());
+ hit->cellID = touchPre->GetCopyNumber()/2+1;//getCellID(step);
+ hit->energyDeposit = 0.0;
+ hit->position = PostPosition;
+ hit->momentum = PostMomentum;
+ hit->length = h.stepLength();
+ m_spaceHC->insert(hit);
+
+ // add dE and pathlegth and return
+ dEInPadRow += h.deposit();
+ pathLengthInPadRow += h.stepLength();
+ return true;
+ }
+ }
+ }
+ else if(m_lowPtStepLimit) { // low pt tracks will be treated differently as their step length is limited by the special low pt steplimiter
+ if ( ( PreviousPostStepPosition - PrePosition ).R() > 1.0e-6 * dd4hep::mm ) {
+ // This step does not continue the previous path. Deposit the energy and begin a new Pt hit.
+ if (CumulativeEnergyDeposit > m_thresholdEnergyDeposit) {
+ DepositLowPtHit();
+ }
+ else {
+ // reset the cumulative variables if the hit has not been deposited.
+ // The previous track has ended and the cumulated energy left at the end
+ // was not enough to ionize
+ //G4cout << "reset due to new track , discarding " << CumulativeEnergyDeposit / eV << " eV" << std::endl;
+ ResetCumulativeVariables();
+ }
+ }
+ else {
+ //G4cout << "continuing track" << endl;
+ }
+
+ CumulateLowPtStep(h);
+
+ // check whether to deposit the hit
+ if( ( CumulativePathLength > m_lowPtMaxHitSeparation ) ) {
+ // hit is deposited because the step limit is reached and there is enough energy
+ // to ionize
+ if ( CumulativeEnergyDeposit > m_thresholdEnergyDeposit) {
+ DepositLowPtHit();
+ }
+ //else {
+ //G4cout << "not deposited, energy is " << CumulativeEnergyDeposit/eV << " eV" << std::endl;
+ //}
+ }
+ else { // even if the step lenth has not been reached the hit might
+ // be deposited because the particle track ends
+ if ( h.post->GetKineticEnergy() == 0 ) {
+ // only deposit the hit if the energy is high enough
+ if (CumulativeEnergyDeposit > m_thresholdEnergyDeposit) {
+ DepositLowPtHit();
+ }
+ else { // energy is not enoug to ionize.
+ // However, the track has ended and the energy is discarded and not added to the next step
+ //G4cout << "reset due to end of track, discarding " << CumulativeEnergyDeposit/eV << " eV" << std::endl;
+ ResetCumulativeVariables();
+ }
+ }
+ }
+
+ PreviousPostStepPosition = h.postPos();
+
+ return true;
+ }
+
+ return true;
+}
+
+void TimeProjectionChamberSensitiveDetector::EndOfEvent(G4HCofThisEvent* HCE){
+ // There might be one low Pt hit which has not been added to the collection.
+ if (CumulativeEnergyDeposit > m_thresholdEnergyDeposit) {
+ DepositLowPtHit();
+ }
+}
+
+void TimeProjectionChamberSensitiveDetector::DepositLowPtHit(){
+ dd4hep::sim::Geant4TrackerHit* hit = new dd4hep::sim::Geant4TrackerHit(CurrentTrackID,CurrentPDGEncoding,CumulativeEnergyDeposit,CurrentGlobalTime);
+ hit->cellID = CurrentCopyNumber;
+ hit->energyDeposit = CumulativeEnergyDeposit; // FIXME: also use the dedx
+ hit->position = CumulativeMeanPosition;
+ hit->momentum = CumulativeMeanMomentum;
+ hit->length = CumulativePathLength;
+ m_lowPtHC->insert(hit);
+ // reset the cumulative variables after positioning the hit
+ ResetCumulativeVariables();
+}
+
+void TimeProjectionChamberSensitiveDetector::ResetCumulativeVariables(){
+ CumulativeMeanPosition.SetXYZ(0.,0.,0.);// = dd4hep::Position(0.0,0.0,0.0);
+ CumulativeMeanMomentum.SetXYZ(0.,0.,0.);// = dd4hep::sim::Momentum(0.0,0.0,0.0);
+ CumulativeNumSteps = 0;
+ CumulativeEnergyDeposit = 0;
+ CumulativePathLength = 0;
+}
+
+void TimeProjectionChamberSensitiveDetector::CumulateLowPtStep(dd4hep::sim::Geant4StepHandler& h){
+ //dd4hep::Position prePos = h.prePos();
+ //dd4hep::Position postPos = h.postPos();
+ //dd4hep::Position direction = postPos - prePos;
+ dd4hep::Position meanPosition = h.avgPosition();//0.5*(prePos+postPos);
+ //double hit_len = direction.R();
+ dd4hep::Position meanMomentum = 0.5*(h.preMom() + h.postMom());
+
+ ++CumulativeNumSteps;
+ CumulativeMeanPosition = ( (CumulativeMeanPosition*(CumulativeNumSteps-1)) + meanPosition ) / CumulativeNumSteps;
+ CumulativeMeanMomentum = ( (CumulativeMeanMomentum*(CumulativeNumSteps-1)) + meanMomentum ) / CumulativeNumSteps;
+ CumulativeEnergyDeposit += h.deposit();
+ CumulativePathLength += h.stepLength();
+ CurrentPDGEncoding = h.trkPdgID();
+ CurrentTrackID = h.trkID();
+ CurrentGlobalTime = h.trkTime();
+ CurrentCopyNumber = h.preTouchable()->GetCopyNumber()/2+1;//getCellID( h.step );
+
+ // needed for delta electrons: all particles causing hits have to be saved in the LCIO file
+ if ( CumulativeEnergyDeposit > m_thresholdEnergyDeposit ) {
+ // This low Pt hit will eventually be saved, so set the flag to store the particle
+
+ // writing the MC Particles can be turned on or off for the lowPt particles
+ if(m_writeMCParticleForLowPtHits){
+ // TODO: here set write flag for this track
+
+ }
+ }
+}
diff --git a/Simulation/DetSimSD/src/TimeProjectionChamberSensitiveDetector.h b/Simulation/DetSimSD/src/TimeProjectionChamberSensitiveDetector.h
new file mode 100644
index 0000000000000000000000000000000000000000..bcb91b435f2506d5ee6cf1eda8c61a12f26ff2bf
--- /dev/null
+++ b/Simulation/DetSimSD/src/TimeProjectionChamberSensitiveDetector.h
@@ -0,0 +1,68 @@
+// *********************************************************
+// Implemented from Mokka
+// *********************************************************
+//
+// $Id: TimeProjectionChamberSensitiveDetector.hh,v 1.1 2009/05/13 08:22:57 steve Exp $
+
+#ifndef TimeProjectionChamberSensitiveDetector_h
+#define TimeProjectionChamberSensitiveDetector_h
+
+#include "DetSimSD/DDG4SensitiveDetector.h"
+#include "DDG4/Defs.h"
+
+class TimeProjectionChamberSensitiveDetector: public DDG4SensitiveDetector {
+ public:
+ TimeProjectionChamberSensitiveDetector(const std::string& name, dd4hep::Detector& description);
+
+ void Initialize(G4HCofThisEvent* HCE);
+ G4bool ProcessHits(G4Step* step, G4TouchableHistory* history);
+ void EndOfEvent(G4HCofThisEvent* HCE);
+
+ void setThreshold(double e){m_thresholdEnergyDeposit=e;};
+ void setSameStepLimit(bool flag){m_lowPtStepLimit=(!flag);};
+ void setWriteMCTruthForLowPtHits(bool flag){m_writeMCParticleForLowPtHits=flag;};
+ void setLowPtCut(double cut){m_lowPtCut=cut;};
+ void setLowPtMaxHitSeparation(double length){m_lowPtMaxHitSeparation=length;};
+
+ /// helper function to avoid code duplication, writes a low Pt hit to the collection
+ void DepositLowPtHit();
+
+ /// helper function to avoid code duplication, resets all cumulative variables
+ void ResetCumulativeVariables();
+
+ /// helper function to avoid code duplication,
+ /// adds energy, track length and momentum of a low pt step to the cumulative variables
+ void CumulateLowPtStep(dd4hep::sim::Geant4StepHandler& h);
+
+ protected:
+
+ double m_thresholdEnergyDeposit = 0;
+ bool m_lowPtStepLimit = false;
+ bool m_writeMCParticleForLowPtHits = false;
+ double m_lowPtCut = 0;
+ double m_lowPtMaxHitSeparation = 0;
+ bool m_trackingPhysicsListELossOn = true;
+
+ HitCollection* m_hc = nullptr;
+ HitCollection* m_spaceHC = nullptr;
+ HitCollection* m_lowPtHC = nullptr;
+
+ dd4hep::Position CrossingOfPadRingCentre;
+ dd4hep::sim::Momentum MomentumAtPadRingCentre;
+ double dEInPadRow;
+ double globalTimeAtPadRingCentre;
+ double pathLengthInPadRow;
+ double CumulativePathLength;
+ double CumulativeEnergyDeposit;
+ dd4hep::Position CumulativeMeanPosition;
+ dd4hep::sim::Momentum CumulativeMeanMomentum;
+ int CumulativeNumSteps;
+
+ dd4hep::Position PreviousPostStepPosition; //< the end point of the previous step
+ int CurrentPDGEncoding; //< the PDG encoding of the particle causing the cumulative energy deposit
+ int CurrentTrackID; //< the TrackID of the particle causing the cumulative energy deposit
+ double CurrentGlobalTime; ///< the global time of the track causing the cumulative energy deposit
+ int CurrentCopyNumber; ///< copy number of the preStepPoint's TouchableHandle for the cumulative energy deposit
+
+};
+#endif