Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
SensitiveSteppingAction.cpp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file SensitiveSteppingAction.cpp
1 // This file is part of the Acts project.
2 //
3 // Copyright (C) 2021 CERN for the benefit of the Acts project
4 //
5 // This Source Code Form is subject to the terms of the Mozilla Public
6 // License, v. 2.0. If a copy of the MPL was not distributed with this
7 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 
10 
19 
20 #include <cstddef>
21 #include <string>
22 #include <unordered_map>
23 #include <utility>
24 
25 #include <G4RunManager.hh>
26 #include <G4Step.hh>
27 #include <G4StepPoint.hh>
28 #include <G4Track.hh>
29 #include <G4UnitsTable.hh>
30 #include <G4VPhysicalVolume.hh>
31 
32 class G4PrimaryParticle;
33 
34 #if BOOST_VERSION >= 107800
35 #include <boost/describe.hpp>
36 
37 BOOST_DESCRIBE_ENUM(G4StepStatus, fWorldBoundary, fGeomBoundary,
38  fAtRestDoItProc, fAlongStepDoItProc, fPostStepDoItProc,
39  fUserDefinedLimit, fExclusivelyForcedProc, fUndefined);
40 
41 BOOST_DESCRIBE_ENUM(G4ProcessType, fNotDefined, fTransportation,
42  fElectromagnetic, fOptical, fHadronic, fPhotolepton_hadron,
43  fDecay, fGeneral, fParameterisation, fUserDefined,
44  fParallel, fPhonon, fUCN);
45 
46 BOOST_DESCRIBE_ENUM(G4TrackStatus, fAlive, fStopButAlive, fStopAndKill,
47  fKillTrackAndSecondaries, fSuspend, fPostponeToNextEvent);
48 #endif
49 
50 namespace {
51 
52 ActsFatras::Hit hitFromStep(const G4StepPoint* preStepPoint,
53  const G4StepPoint* postStepPoint,
54  ActsFatras::Barcode particleId,
55  Acts::GeometryIdentifier geoId, int32_t index) {
56  static constexpr double convertTime = Acts::UnitConstants::s / CLHEP::s;
57  static constexpr double convertLength = Acts::UnitConstants::mm / CLHEP::mm;
58  static constexpr double convertEnergy = Acts::UnitConstants::GeV / CLHEP::GeV;
59 
60  G4ThreeVector preStepPosition = convertLength * preStepPoint->GetPosition();
61  G4double preStepTime = convertTime * preStepPoint->GetGlobalTime();
62  G4ThreeVector postStepPosition = convertLength * postStepPoint->GetPosition();
63  G4double postStepTime = convertTime * postStepPoint->GetGlobalTime();
64 
65  G4ThreeVector preStepMomentum = convertEnergy * preStepPoint->GetMomentum();
66  G4double preStepEnergy = convertEnergy * preStepPoint->GetTotalEnergy();
67  G4ThreeVector postStepMomentum = convertEnergy * postStepPoint->GetMomentum();
68  G4double postStepEnergy = convertEnergy * postStepPoint->GetTotalEnergy();
69 
70  Acts::ActsScalar hX = 0.5 * (preStepPosition[0] + postStepPosition[0]);
71  Acts::ActsScalar hY = 0.5 * (preStepPosition[1] + postStepPosition[1]);
72  Acts::ActsScalar hZ = 0.5 * (preStepPosition[2] + postStepPosition[2]);
73  Acts::ActsScalar hT = 0.5 * (preStepTime + postStepTime);
74 
75  Acts::ActsScalar mXpre = preStepMomentum[0];
76  Acts::ActsScalar mYpre = preStepMomentum[1];
77  Acts::ActsScalar mZpre = preStepMomentum[2];
78  Acts::ActsScalar mEpre = preStepEnergy;
79  Acts::ActsScalar mXpost = postStepMomentum[0];
80  Acts::ActsScalar mYpost = postStepMomentum[1];
81  Acts::ActsScalar mZpost = postStepMomentum[2];
82  Acts::ActsScalar mEpost = postStepEnergy;
83 
84  Acts::Vector4 particlePosition(hX, hY, hZ, hT);
85  Acts::Vector4 beforeMomentum(mXpre, mYpre, mZpre, mEpre);
86  Acts::Vector4 afterMomentum(mXpost, mYpost, mZpost, mEpost);
87 
88  return ActsFatras::Hit(geoId, particleId, particlePosition, beforeMomentum,
89  afterMomentum, index);
90 }
91 } // namespace
92 
94  const Config& cfg, std::unique_ptr<const Acts::Logger> logger)
95  : G4UserSteppingAction(), m_cfg(cfg), m_logger(std::move(logger)) {}
96 
98  const G4Step* step) {
99  // Unit conversions G4->::ACTS
100 
101  // The particle after the step
102  G4Track* track = step->GetTrack();
103  G4PrimaryParticle* primaryParticle =
104  track->GetDynamicParticle()->GetPrimaryParticle();
105 
106  // Bail out if charged & configured to do so
107  G4double absCharge = std::abs(track->GetParticleDefinition()->GetPDGCharge());
108  if (not m_cfg.charged and absCharge > 0.) {
109  return;
110  }
111 
112  // Bail out if neutral & configured to do so
113  if (not m_cfg.neutral and absCharge == 0.) {
114  return;
115  }
116 
117  // Bail out if it is a primary & configured to be ignored
118  if (not m_cfg.primary and primaryParticle != nullptr) {
119  return;
120  }
121 
122  // Bail out if it is a secondary & configured to be ignored
123  if (not m_cfg.secondary and primaryParticle == nullptr) {
124  return;
125  }
126 
127  // Get the physical volume & check if it has the sensitive string name
128  std::string_view volumeName = track->GetVolume()->GetName();
129 
130  if (volumeName.find(SensitiveSurfaceMapper::mappingPrefix) ==
131  std::string_view::npos) {
132  return;
133  }
134 
135  // Cast out the GeometryIdentifier
136  volumeName = volumeName.substr(SensitiveSurfaceMapper::mappingPrefix.size(),
137  std::string_view::npos);
138  char* end = nullptr;
139  const Acts::GeometryIdentifier geoId(
140  std::strtoul(volumeName.data(), &end, 10));
141 
142  // This is not the case if we have a particle-ID collision
143  if (eventStore().trackIdMapping.find(track->GetTrackID()) ==
144  eventStore().trackIdMapping.end()) {
145  return;
146  }
147 
148  const auto particleId = eventStore().trackIdMapping.at(track->GetTrackID());
149 
150  ACTS_VERBOSE("Step of " << particleId << " in sensitive volume " << geoId);
151 
152  // Get PreStepPoint and PostStepPoint
153  const G4StepPoint* preStepPoint = step->GetPreStepPoint();
154  const G4StepPoint* postStepPoint = step->GetPostStepPoint();
155 
156  // Set particle hit count to zero, so we have this entry in the map later
157  if (eventStore().particleHitCount.find(particleId) ==
158  eventStore().particleHitCount.end()) {
159  eventStore().particleHitCount[particleId] = 0;
160  }
161 
162  // Extract if we are at volume boundaries
163  const bool preOnBoundary = preStepPoint->GetStepStatus() == fGeomBoundary;
164  const bool postOnBoundary = postStepPoint->GetStepStatus() == fGeomBoundary or
165  postStepPoint->GetStepStatus() == fWorldBoundary;
166  const bool particleStopped = (postStepPoint->GetKineticEnergy() == 0.0);
167  const bool particleDecayed =
168  (postStepPoint->GetProcessDefinedStep()->GetProcessType() == fDecay);
169 
170  auto print = [](auto s) {
171 #if BOOST_VERSION >= 107800
172  return boost::describe::enum_to_string(s, "unmatched");
173 #else
174  return s;
175 #endif
176  };
177  ACTS_VERBOSE("status: pre="
178  << print(preStepPoint->GetStepStatus())
179  << ", post=" << print(postStepPoint->GetStepStatus())
180  << ", post E_kin=" << std::boolalpha
181  << postStepPoint->GetKineticEnergy() << ", process_type="
182  << print(
183  postStepPoint->GetProcessDefinedStep()->GetProcessType())
184  << ", particle="
185  << track->GetParticleDefinition()->GetParticleName()
186  << ", process_name="
187  << postStepPoint->GetProcessDefinedStep()->GetProcessName()
188  << ", track status=" << print(track->GetTrackStatus()));
189 
190  // Case A: The step starts at the entry of the volume and ends at the exit.
191  // Add hit to collection.
192  if (preOnBoundary and postOnBoundary) {
193  ACTS_VERBOSE("-> merge single step to hit");
194  ++eventStore().particleHitCount[particleId];
195  eventStore().hits.push_back(
196  hitFromStep(preStepPoint, postStepPoint, particleId, geoId,
197  eventStore().particleHitCount.at(particleId) - 1));
198 
199  eventStore().numberGeantSteps += 1ul;
200  eventStore().maxStepsForHit = std::max(eventStore().maxStepsForHit, 1ul);
201  return;
202  }
203 
204  // Case B: The step ends at the exit of the volume. Add hit to hit buffer, and
205  // then combine hit buffer
206  if (postOnBoundary or particleStopped or particleDecayed) {
207  ACTS_VERBOSE("-> merge buffer to hit");
208  auto& buffer = eventStore().hitBuffer;
209  buffer.push_back(
210  hitFromStep(preStepPoint, postStepPoint, particleId, geoId, -1));
211 
212  const auto pos4 =
213  0.5 * (buffer.front().fourPosition() + buffer.back().fourPosition());
214 
215  ++eventStore().particleHitCount[particleId];
216  eventStore().hits.emplace_back(
217  geoId, particleId, pos4, buffer.front().momentum4Before(),
218  buffer.back().momentum4After(),
219  eventStore().particleHitCount.at(particleId) - 1);
220 
221  assert(std::all_of(buffer.begin(), buffer.end(),
222  [&](const auto& h) { return h.geometryId() == geoId; }));
223  assert(std::all_of(buffer.begin(), buffer.end(), [&](const auto& h) {
224  return h.particleId() == particleId;
225  }));
226 
227  eventStore().numberGeantSteps += buffer.size();
228  eventStore().maxStepsForHit =
229  std::max(eventStore().maxStepsForHit, buffer.size());
230 
231  buffer.clear();
232  return;
233  }
234 
235  // Case C: The step doesn't end at the exit of the volume. Add the hit to the
236  // hit buffer.
237  if (not postOnBoundary) {
238  // ACTS_VERBOSE("-> add hit to buffer");
239  eventStore().hitBuffer.push_back(
240  hitFromStep(preStepPoint, postStepPoint, particleId, geoId, -1));
241  return;
242  }
243 
244  assert(false && "should never reach this");
245 }