Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
PlanarSteppingAlgorithm.cpp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file PlanarSteppingAlgorithm.cpp
1 // This file is part of the Acts project.
2 //
3 // Copyright (C) 2017-2020 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 
36 
37 #include <cmath>
38 #include <cstddef>
39 #include <ostream>
40 #include <stdexcept>
41 #include <utility>
42 #include <vector>
43 
47  : ActsExamples::IAlgorithm("PlanarSteppingAlgorithm", level),
48  m_cfg(std::move(config)) {
49  if (m_cfg.inputSimHits.empty()) {
50  throw std::invalid_argument("Missing input hits collection");
51  }
52  if (m_cfg.outputClusters.empty()) {
53  throw std::invalid_argument("Missing output clusters collection");
54  }
55  if (m_cfg.outputSourceLinks.empty()) {
56  throw std::invalid_argument("Missing source links output collection");
57  }
58  if (m_cfg.outputDigiSourceLinks.empty()) {
59  throw std::invalid_argument(
60  "Missing digitization source links output collection");
61  }
62  if (m_cfg.outputMeasurements.empty()) {
63  throw std::invalid_argument("Missing measurements output collection");
64  }
66  throw std::invalid_argument(
67  "Missing hit-to-particles map output collection");
68  }
69  if (m_cfg.outputMeasurementSimHitsMap.empty()) {
70  throw std::invalid_argument(
71  "Missing hit-to-simulated-hits map output collection");
72  }
73  if (not m_cfg.trackingGeometry) {
74  throw std::invalid_argument("Missing tracking geometry");
75  }
77  throw std::invalid_argument("Missing planar module stepper");
78  }
79  if (!m_cfg.randomNumbers) {
80  throw std::invalid_argument("Missing random numbers tool");
81  }
82 
84 
92 
93  // fill the digitizables map to allow lookup by geometry id only
94  m_cfg.trackingGeometry->visitSurfaces([this](const Acts::Surface* surface) {
95  Digitizable dg;
96  // require a valid surface
97  dg.surface = surface;
98  if (dg.surface == nullptr) {
99  return;
100  }
101  // require an associated detector element
102  dg.detectorElement = dynamic_cast<const Acts::IdentifiedDetectorElement*>(
103  dg.surface->associatedDetectorElement());
104  if (dg.detectorElement == nullptr) {
105  return;
106  }
107  // require an associated digitization module
108  dg.digitizer = dg.detectorElement->digitizationModule().get();
109  if (dg.digitizer == nullptr) {
110  return;
111  }
112  // record all valid surfaces
113  this->m_digitizables.insert_or_assign(surface->geometryId(), dg);
114  });
115 }
116 
118  const AlgorithmContext& ctx) const {
119  // retrieve input
120  const auto& simHits = m_inputSimHits(ctx);
121 
122  // prepare output containers
124 
125  std::vector<Acts::DigitizationSourceLink> digiSourceLinks;
126 
128  MeasurementContainer measurements;
129  IndexMultimap<ActsFatras::Barcode> hitParticlesMap;
130  IndexMultimap<Index> hitSimHitsMap;
131  clusters.reserve(simHits.size());
132  measurements.reserve(simHits.size());
133  hitParticlesMap.reserve(simHits.size());
134  hitSimHitsMap.reserve(simHits.size());
135 
136  for (auto&& [moduleGeoId, moduleSimHits] : groupByModule(simHits)) {
137  // can only digitize hits on digitizable surfaces
138  const auto it = m_digitizables.find(moduleGeoId);
139  if (it == m_digitizables.end()) {
140  continue;
141  }
142 
143  const auto& dg = it->second;
144  // local intersection / direction
145  const auto invTransfrom = dg.surface->transform(ctx.geoContext).inverse();
146 
147  // use iterators manually so we can retrieve the hit index in the container
148  for (auto ih = moduleSimHits.begin(); ih != moduleSimHits.end(); ++ih) {
149  const auto& simHit = *ih;
150  const auto simHitIdx = simHits.index_of(ih);
151 
152  Acts::Vector2 localIntersect =
153  (invTransfrom * simHit.position()).head<2>();
154  Acts::Vector3 localDirection = invTransfrom.linear() * simHit.direction();
155 
156  // compute digitization steps
157  const auto thickness = dg.detectorElement->thickness();
158  const auto lorentzAngle = dg.digitizer->lorentzAngle();
159  auto lorentzShift = thickness * std::tan(lorentzAngle);
160  lorentzShift *= -(dg.digitizer->readoutDirection());
161  // now calculate the steps through the silicon
162  std::vector<Acts::DigitizationStep> dSteps =
163  m_cfg.planarModuleStepper->cellSteps(ctx.geoContext, *dg.digitizer,
164  localIntersect, localDirection);
165  // everything under threshold or edge effects
166  if (dSteps.empty()) {
167  ACTS_VERBOSE("No steps returned from stepper.");
168  continue;
169  }
170 
171  // Create a cluster - centroid method
172  double localX = 0.;
173  double localY = 0.;
174  double totalPath = 0.;
175  // the cells to be used
176  std::vector<Acts::DigitizationCell> usedCells;
177  usedCells.reserve(dSteps.size());
178  // loop over the steps
179  for (auto dStep : dSteps) {
180  // @todo implement smearing
181  localX += dStep.stepLength * dStep.stepCellCenter.x();
182  localY += dStep.stepLength * dStep.stepCellCenter.y();
183  totalPath += dStep.stepLength;
184  usedCells.push_back(Acts::DigitizationCell(dStep.stepCell.channel0,
185  dStep.stepCell.channel1,
186  dStep.stepLength));
187  }
188  // divide by the total path
189  localX /= totalPath;
190  localX += lorentzShift;
191  localY /= totalPath;
192 
193  // get the segmentation & find the corresponding cell id
194  const Acts::Segmentation& segmentation = dg.digitizer->segmentation();
195  auto binUtility = segmentation.binUtility();
196  Acts::Vector2 localPosition(localX, localY);
197  // @todo remove unnecessary conversion
198  // size_t bin0 = binUtility.bin(localPosition, 0);
199  // size_t bin1 = binUtility.bin(localPosition, 1);
200  // size_t binSerialized = binUtility.serialize({{bin0, bin1, 0}});
201 
202  // the covariance is currently set to some arbitrary value.
204  cov << 0.05, 0., 0., 0., 0.05, 0., 0., 0.,
206  Acts::Vector3 par(localX, localY, simHit.time());
207 
208  // create the planar cluster
209  digiSourceLinks.emplace_back(moduleGeoId,
210  std::vector<std::size_t>{simHitIdx});
211  Acts::DigitizationSourceLink& digiSourceLink = digiSourceLinks.back();
212 
214  dg.surface->getSharedPtr(), Acts::SourceLink{digiSourceLink}, cov,
215  localX, localY, simHit.time(), std::move(usedCells));
216 
217  // the measurement container is unordered and the index under which
218  // the measurement will be stored is known before adding it.
219  Index hitIdx = measurements.size();
220  IndexSourceLink sourceLink{moduleGeoId, hitIdx};
221 
222  sourceLinks.insert(sourceLinks.end(), sourceLink);
223 
224  auto meas = Acts::makeMeasurement(Acts::SourceLink{sourceLink}, par, cov,
227 
228  // add to output containers. since the input is already geometry-order,
229  // new elements in geometry containers can just be appended at the end.
230  clusters.emplace_hint(clusters.end(), moduleGeoId, std::move(cluster));
231  measurements.emplace_back(std::move(meas));
232  // no hit merging -> only one mapping per digitized hit.
233  hitParticlesMap.emplace_hint(hitParticlesMap.end(), hitIdx,
234  simHit.particleId());
235  hitSimHitsMap.emplace_hint(hitSimHitsMap.end(), hitIdx, simHitIdx);
236  }
237  }
238 
239  ACTS_DEBUG("digitized " << simHits.size() << " hits into " << clusters.size()
240  << " clusters");
241 
242  m_outputClusters(ctx, std::move(clusters));
243  m_outputSourceLinks(ctx, std::move(sourceLinks));
244  m_outputDigiSourceLinks(ctx, std::move(digiSourceLinks));
245  m_outputMeasurements(ctx, std::move(measurements));
246  m_outputMeasurementParticlesMap(ctx, std::move(hitParticlesMap));
247  m_outputMeasurementSimHitsMap(ctx, std::move(hitSimHitsMap));
249 }