LDMX Software
HcalSD.cxx
1#include "SimCore/SDs/HcalSD.h"
2
3/*~~~~~~~~~~~~~~*/
4/* DetDescr */
5/*~~~~~~~~~~~~~~*/
7#include "DetDescr/HcalID.h"
8
9// STL
10#include <iostream>
11
12// Geant4
13#include "G4Box.hh"
14#include "G4ParticleTypes.hh"
15#include "G4Step.hh"
16#include "G4StepPoint.hh"
17
18namespace simcore {
19
20const std::string HcalSD::COLLECTION_NAME = "HcalSimHits";
21
22HcalSD::HcalSD(const std::string& name, simcore::ConditionsInterface& ci,
24 : SensitiveDetector(name, ci, p), birksc1_(1.29e-2), birksc2_(9.59e-6) {
25 gdmlIdentifiers_ = {
26 p.getParameter<std::vector<std::string>>("gdml_identifiers")};
27}
28
29ldmx::HcalID HcalSD::decodeCopyNumber(const std::uint32_t copyNumber,
30 const G4ThreeVector& localPosition,
31 const G4Box* scint) {
32 const unsigned int version{copyNumber / 0x01000000};
33 if (version != 0) {
35 return ldmx::HcalID{Index(copyNumber).field2(), Index(copyNumber).field1(),
36 Index(copyNumber).field0()};
37 }
38 const auto& geometry = getCondition<ldmx::HcalGeometry>(
40 unsigned int stripID = 0;
41 const unsigned int section = copyNumber / 1000;
42 const unsigned int layer = copyNumber % 1000;
43
44 // 5cm wide bars are HARD-CODED
45 if (section == ldmx::HcalID::BACK) {
46 if (geometry.backLayerIsHorizontal(layer)) {
47 stripID = int((localPosition.y() + scint->GetYHalfLength()) / 50.0);
48 } else {
49 stripID = int((localPosition.x() + scint->GetXHalfLength()) / 50.0);
50 }
51 } else {
52 stripID = int((localPosition.z() + scint->GetZHalfLength()) / 50.0);
53 }
54 return ldmx::HcalID{section, layer, stripID};
55}
56
57G4bool HcalSD::ProcessHits(G4Step* aStep, G4TouchableHistory* ROhist) {
58 // Get the edep from the step.
59 G4double edep = aStep->GetTotalEnergyDeposit();
60
61 // Skip steps with no energy dep which come from non-Geantino particles.
62 if (edep == 0.0 and not isGeantino(aStep)) {
63 if (verboseLevel > 2) {
64 std::cout << "CalorimeterSD skipping step with zero edep." << std::endl
65 << std::endl;
66 }
67 return false;
68 }
69
70 //---------------------------------------------------------------------------------------------------
71 // Birks' Law
72 // ===========
73 //
74 // In the case of Scintillator as active medium, we can
75 // describe the quenching effects with the Birks' law,
76 // using the expression and the coefficients taken from
77 // the paper NIM 80 (1970) 239-244 for the organic
78 // scintillator NE-102:
79 // S*dE/dr
80 // dL/dr = -----------------------------------
81 // 1 + C1*(dE/dr)
82 // with:
83 // S=1
84 // C1 = 1.29 x 10^-2 g*cm^-2*MeV^-1
85 // C2 = 9.59 x 10^-6 g^2*cm^-4*MeV^-2
86 // These are the same values used by ATLAS TileCal
87 // and CMS HCAL (and also the default in Geant3).
88 // You can try different values for these parameters,
89 // to have an idea on the uncertainties due to them,
90 // by uncommenting one of the lines below.
91 // To get the "dE/dr" that appears in the formula,
92 // which has the dimensions
93 // [ dE/dr ] = MeV * cm^2 / g
94 // we have to divide the energy deposit in MeV by the
95 // product of the step length (in cm) and the density
96 // of the scintillator:
97
98 G4double birksFactor(1.0);
99 G4double stepLength = aStep->GetStepLength() / CLHEP::cm;
100 // Do not apply Birks for gamma deposits!
101 // Check, cut if necessary.
102 if (stepLength > 1.0e-6) {
103 G4double rho = aStep->GetPreStepPoint()->GetMaterial()->GetDensity() /
104 (CLHEP::g / CLHEP::cm3);
105 G4double dedx = edep / (rho * stepLength); //[MeV*cm^2/g]
106 birksFactor = 1.0 / (1.0 + birksc1_ * dedx + birksc2_ * dedx * dedx);
107 if (aStep->GetTrack()->GetDefinition() == G4Gamma::GammaDefinition())
108 birksFactor = 1.0;
109 if (aStep->GetTrack()->GetDefinition() == G4Neutron::NeutronDefinition())
110 birksFactor = 1.0;
111 }
112
113 // update edep to include birksFactor
114 edep *= birksFactor;
115
116 // Create a new cal hit.
117 ldmx::SimCalorimeterHit& hit{hits_.emplace_back()};
118
119 // Get the scintillator solid box
120 G4Box* scint = nullptr;
121
122 if (aStep) {
123 const auto* preStepPoint = aStep->GetPreStepPoint();
124 if (preStepPoint) {
125 const auto& touchableHandle = preStepPoint->GetTouchableHandle();
126 if (touchableHandle) {
127 const auto* volume = touchableHandle->GetVolume();
128
129 if (volume) {
130 const auto* logicalVolume = volume->GetLogicalVolume();
131 if (logicalVolume) {
132 auto* solid = logicalVolume->GetSolid();
133 if (solid) {
134 scint = static_cast<G4Box*>(solid);
135 }
136 }
137 }
138 }
139 }
140 }
141
142 // Set the step mid-point as the hit position.
143 G4StepPoint* prePoint = aStep->GetPreStepPoint();
144 G4StepPoint* postPoint = aStep->GetPostStepPoint();
145 // A Geant4 "touchable" is a way to uniquely identify a particular volume,
146 // short for touchable detector element. See the detector definition and
147 // response section of the Geant4 application developers manual for details.
148 //
149 // The TouchableHandle is just a reference counted pointer to a
150 // G4TouchableHistory object, which is a concrete implementation of a
151 // G4Touchable interface.
152 //
153 auto touchableHistory{prePoint->GetTouchableHandle()->GetHistory()};
154 // Affine transform for converting between local and global coordinates
155 auto topTransform{touchableHistory->GetTopTransform()};
156 G4ThreeVector position =
157 0.5 * (prePoint->GetPosition() + postPoint->GetPosition());
158 G4ThreeVector localPosition = topTransform.TransformPoint(position);
159 hit.setPosition(position[0], position[1], position[2]);
160
161 // Create the ID for the hit. Note 2 here corresponds to the "depth" of the
162 // geometry tree. If this changes in the GDML, this would have to be updated
163 // here. Currently, 0 corresponds to the world volume, 1 corresponds to the
164 // Hcal, and 2 to the bars/absorbers
165 int copyNum = touchableHistory->GetVolume(2)->GetCopyNo();
166 ldmx::HcalID id = decodeCopyNumber(copyNum, localPosition, scint);
167 hit.setID(id.raw());
168
169 // add one contributor for this hit with
170 // ID of ancestor incident on Cal-Region
171 // ID of this track
172 // PDG of this track
173 // EDEP (including birks factor)
174 // time of this hit
175 const G4Track* track = aStep->GetTrack();
176 int track_id = track->GetTrackID();
177 hit.addContrib(getTrackMap().findIncident(track_id), track_id,
178 track->GetParticleDefinition()->GetPDGEncoding(), edep,
179 track->GetGlobalTime());
180 //
181 // Pre/post step details for scintillator response simulation
182
183 // Convert back to mm
184 hit.setPathLength(stepLength * CLHEP::cm / CLHEP::mm);
185 hit.setVelocity(track->GetVelocity());
186 const auto& geometry = getCondition<ldmx::HcalGeometry>(
188 // Convert pre/post step position from global coordinates to coordinates
189 // within the scintillator bar
190 const auto localPreStepPoint{
191 topTransform.TransformPoint(prePoint->GetPosition())};
192 const auto localPostStepPoint{
193 topTransform.TransformPoint(postPoint->GetPosition())};
194
195 // And rotate them to a local coordinate system for the bar that always has
196 // the same x/y/z definitions (see HcalGeometry for details)
197 auto localPrePositionRotated{geometry.rotateGlobalToLocalBarPosition(
198 {localPreStepPoint[0], localPreStepPoint[1], localPreStepPoint[2]}, id)};
199
200 auto localPostPositionRotated{geometry.rotateGlobalToLocalBarPosition(
201 {localPostStepPoint[0], localPostStepPoint[1], localPostStepPoint[2]},
202 id)};
203 hit.setPreStepPosition(localPrePositionRotated[0], localPrePositionRotated[1],
204 localPrePositionRotated[2]);
205 hit.setPostStepPosition(localPostPositionRotated[0],
206 localPostPositionRotated[1],
207 localPostPositionRotated[2]);
208 hit.setPreStepTime(prePoint->GetGlobalTime());
209 hit.setPostStepTime(postPoint->GetGlobalTime());
210
211 if (this->verboseLevel > 2) {
212 hit.Print();
213 }
214
215 return true;
216}
217
218} // namespace simcore
219
220DECLARE_SENSITIVEDETECTOR(simcore::HcalSD)
Class that translates HCal ID into positions of strip hits.
Class that defines an HCal sensitive detector.
Class encapsulating parameters for configuring a processor.
Definition Parameters.h:27
T getParameter(const std::string &name) const
Retrieve the parameter of the given name.
Definition Parameters.h:89
static constexpr const char * CONDITIONS_OBJECT_NAME
Conditions object: The name of the python configuration calling this class (Hcal/python/HcalGeometry....
Implements detector ids for HCal subdetector.
Definition HcalID.h:19
A maximally-packed index of up to four different fields.
Definition PackedIndex.h:32
Stores simulated calorimeter hit information.
Handle to the conditions system, provided at construction to classes which require it.
Class defining a sensitive detector of type HCal.
Definition HcalSD.h:23
ldmx::HcalID decodeCopyNumber(const std::uint32_t copyNumber, const G4ThreeVector &localPosition, const G4Box *scint)
Decode copy number of scintillator bar.
Definition HcalSD.cxx:29
static const std::string COLLECTION_NAME
name of collection to be added to event bus
Definition HcalSD.h:26
virtual G4bool ProcessHits(G4Step *aStep, G4TouchableHistory *ROhist) override
Create a hit out of the energy deposition deposited during a step.
Definition HcalSD.cxx:57
HcalSD(const std::string &name, simcore::ConditionsInterface &ci, const framework::config::Parameters &params)
Constructor.
Definition HcalSD.cxx:22
Dynamically loaded Geant4 SensitiveDetector for saving hits in specific volumes within the simulation...
bool isGeantino(const G4Step *step) const
Check if the passed step is a step of a geantino.
const TrackMap & getTrackMap() const
Get a handle to the current track map.