Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
SurfaceMaterialMapper.cpp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file SurfaceMaterialMapper.cpp
1 // This file is part of the Acts project.
2 //
3 // Copyright (C) 2018-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 
18 #include "Acts/Geometry/Layer.hpp"
35 
36 #include <cstddef>
37 #include <ostream>
38 #include <string>
39 #include <tuple>
40 #include <utility>
41 #include <vector>
42 
43 namespace Acts {
44 struct EndOfWorldReached;
45 } // namespace Acts
46 
48  const Config& cfg, StraightLinePropagator propagator,
49  std::unique_ptr<const Logger> slogger)
50  : m_cfg(cfg),
51  m_propagator(std::move(propagator)),
52  m_logger(std::move(slogger)) {}
53 
55  const GeometryContext& gctx, const MagneticFieldContext& mctx,
56  const TrackingGeometry& tGeometry) const {
57  // Parse the geometry and find all surfaces with material proxies
58  auto world = tGeometry.highestTrackingVolume();
59 
60  // The Surface material mapping state
61  State mState(gctx, mctx);
62  resolveMaterialSurfaces(mState, *world);
63  collectMaterialVolumes(mState, *world);
64 
65  ACTS_DEBUG(mState.accumulatedMaterial.size()
66  << " Surfaces with PROXIES collected ... ");
67  for (auto& smg : mState.accumulatedMaterial) {
68  ACTS_VERBOSE(" -> Surface in with id " << smg.first);
69  }
70  return mState;
71 }
72 
74  State& mState, const TrackingVolume& tVolume) const {
75  ACTS_VERBOSE("Checking volume '" << tVolume.volumeName()
76  << "' for material surfaces.")
77 
78  ACTS_VERBOSE("- boundary surfaces ...");
79  // Check the boundary surfaces
80  for (auto& bSurface : tVolume.boundarySurfaces()) {
81  checkAndInsert(mState, bSurface->surfaceRepresentation());
82  }
83 
84  ACTS_VERBOSE("- confined layers ...");
85  // Check the confined layers
86  if (tVolume.confinedLayers() != nullptr) {
87  for (auto& cLayer : tVolume.confinedLayers()->arrayObjects()) {
88  // Take only layers that are not navigation layers
89  if (cLayer->layerType() != navigation) {
90  // Check the representing surface
91  checkAndInsert(mState, cLayer->surfaceRepresentation());
92  // Get the approach surfaces if present
93  if (cLayer->approachDescriptor() != nullptr) {
94  for (auto& aSurface :
95  cLayer->approachDescriptor()->containedSurfaces()) {
96  if (aSurface != nullptr) {
97  checkAndInsert(mState, *aSurface);
98  }
99  }
100  }
101  // Get the sensitive surface is present
102  if (cLayer->surfaceArray() != nullptr) {
103  // Sensitive surface loop
104  for (auto& sSurface : cLayer->surfaceArray()->surfaces()) {
105  if (sSurface != nullptr) {
106  checkAndInsert(mState, *sSurface);
107  }
108  }
109  }
110  }
111  }
112  }
113  // Step down into the sub volume
114  if (tVolume.confinedVolumes()) {
115  for (auto& sVolume : tVolume.confinedVolumes()->arrayObjects()) {
116  // Recursive call
117  resolveMaterialSurfaces(mState, *sVolume);
118  }
119  }
120 }
121 
123  const Surface& surface) const {
124  auto surfaceMaterial = surface.surfaceMaterial();
125  // Check if the surface has a proxy
126  if (surfaceMaterial != nullptr) {
127  if (m_cfg.computeVariance) {
128  mState.inputSurfaceMaterial[surface.geometryId()] =
129  surface.surfaceMaterialSharedPtr();
130  }
131  auto geoID = surface.geometryId();
132  size_t volumeID = geoID.volume();
133  ACTS_DEBUG("Material surface found with volumeID " << volumeID);
134  ACTS_DEBUG(" - surfaceID is " << geoID);
135 
136  // We need a dynamic_cast to either a surface material proxy or
137  // proper surface material
138  auto psm = dynamic_cast<const ProtoSurfaceMaterial*>(surfaceMaterial);
139 
140  // Get the bin utility: try proxy material first
141  const BinUtility* bu = (psm != nullptr) ? (&psm->binUtility()) : nullptr;
142  if (bu != nullptr) {
143  // Screen output for Binned Surface material
144  ACTS_DEBUG(" - (proto) binning is " << *bu);
145  // Now update
146  BinUtility buAdjusted = adjustBinUtility(*bu, surface, mState.geoContext);
147  // Screen output for Binned Surface material
148  ACTS_DEBUG(" - adjusted binning is " << buAdjusted);
149  mState.accumulatedMaterial[geoID] =
150  AccumulatedSurfaceMaterial(buAdjusted);
151  return;
152  }
153 
154  // Second attempt: binned material
155  auto bmp = dynamic_cast<const BinnedSurfaceMaterial*>(surfaceMaterial);
156  bu = (bmp != nullptr) ? (&bmp->binUtility()) : nullptr;
157  // Creaete a binned type of material
158  if (bu != nullptr) {
159  // Screen output for Binned Surface material
160  ACTS_DEBUG(" - binning is " << *bu);
161  mState.accumulatedMaterial[geoID] = AccumulatedSurfaceMaterial(*bu);
162  } else {
163  // Create a homogeneous type of material
164  ACTS_DEBUG(" - this is homogeneous material.");
166  }
167  }
168 }
169 
171  State& mState, const TrackingVolume& tVolume) const {
172  ACTS_VERBOSE("Checking volume '" << tVolume.volumeName()
173  << "' for material surfaces.")
174  ACTS_VERBOSE("- Insert Volume ...");
175  if (tVolume.volumeMaterial() != nullptr) {
176  mState.volumeMaterial[tVolume.geometryId()] =
177  tVolume.volumeMaterialSharedPtr();
178  }
179 
180  // Step down into the sub volume
181  if (tVolume.confinedVolumes()) {
182  ACTS_VERBOSE("- Check children volume ...");
183  for (auto& sVolume : tVolume.confinedVolumes()->arrayObjects()) {
184  // Recursive call
185  collectMaterialVolumes(mState, *sVolume);
186  }
187  }
188  if (!tVolume.denseVolumes().empty()) {
189  for (auto& sVolume : tVolume.denseVolumes()) {
190  // Recursive call
191  collectMaterialVolumes(mState, *sVolume);
192  }
193  }
194 }
195 
197  // iterate over the map to call the total average
198  for (auto& accMaterial : mState.accumulatedMaterial) {
199  ACTS_DEBUG("Finalizing map for Surface " << accMaterial.first);
200  mState.surfaceMaterial[accMaterial.first] =
201  accMaterial.second.totalAverage();
202  }
203 }
204 
206  State& mState, RecordedMaterialTrack& mTrack) const {
207  // Retrieve the recorded material from the recorded material track
208  auto& rMaterial = mTrack.second.materialInteractions;
209  ACTS_VERBOSE("Retrieved " << rMaterial.size()
210  << " recorded material steps to map.")
211 
212  // Check if the material interactions are associated with a surface. If yes we
213  // simply need to loop over them and accumulate the material
214  if (rMaterial.begin()->intersectionID != GeometryIdentifier()) {
215  ACTS_VERBOSE(
216  "Material surfaces are associated with the material interaction. The "
217  "association interaction/surfaces won't be performed again.");
218  mapSurfaceInteraction(mState, rMaterial);
219  return;
220  } else {
221  ACTS_VERBOSE(
222  "Material interactions need to be associated with surfaces. Collecting "
223  "all surfaces on the trajectory.");
224  mapInteraction(mState, mTrack);
225  return;
226  }
227 }
229  State& mState, RecordedMaterialTrack& mTrack) const {
230  // Retrieve the recorded material from the recorded material track
231  auto& rMaterial = mTrack.second.materialInteractions;
232  std::map<GeometryIdentifier, unsigned int> assignedMaterial;
234  // Neutral curvilinear parameters
236  makeVector4(mTrack.first.first, 0), mTrack.first.second,
237  1 / mTrack.first.second.norm(), std::nullopt,
238  NeutralParticleHypothesis::geantino());
239 
240  // Prepare Action list and abort list
241  using MaterialSurfaceCollector = SurfaceCollector<MaterialSurface>;
242  using MaterialVolumeCollector = VolumeCollector<MaterialVolume>;
243  using ActionList =
244  ActionList<MaterialSurfaceCollector, MaterialVolumeCollector>;
245  using AbortList = AbortList<EndOfWorldReached>;
246 
248  mState.magFieldContext);
249 
250  // Now collect the material layers by using the straight line propagator
251  const auto& result = m_propagator.propagate(start, options).value();
252  auto mcResult = result.get<MaterialSurfaceCollector::result_type>();
253  auto mvcResult = result.get<MaterialVolumeCollector::result_type>();
254 
255  auto mappingSurfaces = mcResult.collected;
256  auto mappingVolumes = mvcResult.collected;
257 
258  // These should be mapped onto the mapping surfaces found
259  ACTS_VERBOSE("Found " << mappingSurfaces.size()
260  << " mapping surfaces for this track.");
261  ACTS_VERBOSE("Mapping surfaces are :")
262  for (auto& mSurface : mappingSurfaces) {
263  ACTS_VERBOSE(" - Surface : " << mSurface.surface->geometryId()
264  << " at position = (" << mSurface.position.x()
265  << ", " << mSurface.position.y() << ", "
266  << mSurface.position.z() << ")");
267  assignedMaterial[mSurface.surface->geometryId()] = 0;
268  }
269 
270  // Run the mapping process, i.e. take the recorded material and map it
271  // onto the mapping surfaces:
272  // - material steps and surfaces are assumed to be ordered along the
273  // mapping ray
274  //- do not record the material inside a volume with material
275  auto rmIter = rMaterial.begin();
276  auto sfIter = mappingSurfaces.begin();
277  auto volIter = mappingVolumes.begin();
278 
279  // Use those to minimize the lookup
282  Vector3 currentPos(0., 0., 0);
283  float currentPathCorrection = 1.;
284  auto currentAccMaterial = mState.accumulatedMaterial.end();
285 
286  // To remember the bins of this event
287  using MapBin = std::pair<AccumulatedSurfaceMaterial*, std::array<size_t, 3>>;
288  using MaterialBin = std::pair<AccumulatedSurfaceMaterial*,
289  std::shared_ptr<const ISurfaceMaterial>>;
290  std::map<AccumulatedSurfaceMaterial*, std::array<size_t, 3>> touchedMapBins;
291  std::map<AccumulatedSurfaceMaterial*, std::shared_ptr<const ISurfaceMaterial>>
292  touchedMaterialBin;
293  if (sfIter != mappingSurfaces.end() &&
294  sfIter->surface->surfaceMaterial()->mappingType() ==
296  ACTS_WARNING(
297  "The first mapping surface is a PostMapping one. Some material from "
298  "before the PostMapping surface will be mapped onto it ");
299  }
300 
301  // Assign the recorded ones, break if you hit an end
302  while (rmIter != rMaterial.end() && sfIter != mappingSurfaces.end()) {
303  // Material not inside current volume
304  if (volIter != mappingVolumes.end() &&
305  !volIter->volume->inside(rmIter->position)) {
306  double distVol = (volIter->position - mTrack.first.first).norm();
307  double distMat = (rmIter->position - mTrack.first.first).norm();
308  // Material past the entry point to the current volume
309  if (distMat - distVol > s_epsilon) {
310  // Switch to next material volume
311  ++volIter;
312  continue;
313  }
314  }
316  if (volIter != mappingVolumes.end() &&
317  volIter->volume->inside(rmIter->position)) {
318  ++rmIter;
319  continue;
320  }
321  // Do we need to switch to next assignment surface ?
322  if (sfIter != mappingSurfaces.end() - 1) {
323  int mappingType = sfIter->surface->surfaceMaterial()->mappingType();
324  int nextMappingType =
325  (sfIter + 1)->surface->surfaceMaterial()->mappingType();
326 
327  if (mappingType == Acts::MappingType::PreMapping ||
328  mappingType == Acts::MappingType::Sensor) {
329  // Change surface if the material after the current surface.
330  if ((rmIter->position - mTrack.first.first).norm() >
331  (sfIter->position - mTrack.first.first).norm()) {
332  if (nextMappingType == Acts::MappingType::PostMapping) {
333  ACTS_WARNING(
334  "PreMapping or Sensor surface followed by PostMapping. Some "
335  "material "
336  "from before the PostMapping surface will be mapped onto it");
337  }
338  ++sfIter;
339  continue;
340  }
341  } else if (mappingType == Acts::MappingType::Default ||
342  mappingType == Acts::MappingType::PostMapping) {
343  switch (nextMappingType) {
346  // Change surface if the material closest to the next surface.
347  if ((rmIter->position - sfIter->position).norm() >
348  (rmIter->position - (sfIter + 1)->position).norm()) {
349  ++sfIter;
350  continue;
351  }
352  break;
353  }
355  // Change surface if the material after the next surface.
356  if ((rmIter->position - sfIter->position).norm() >
357  ((sfIter + 1)->position - sfIter->position).norm()) {
358  ++sfIter;
359  continue;
360  }
361  break;
362  }
364  // Change surface if the next material after the next surface.
365  if ((rmIter == rMaterial.end() - 1) ||
366  ((rmIter + 1)->position - sfIter->position).norm() >
367  ((sfIter + 1)->position - sfIter->position).norm()) {
368  ++sfIter;
369  continue;
370  }
371  break;
372  }
373  default: {
374  ACTS_ERROR("Incorrect mapping type for the next surface : "
375  << (sfIter + 1)->surface->geometryId());
376  }
377  }
378  } else {
379  ACTS_ERROR("Incorrect mapping type for surface : "
380  << sfIter->surface->geometryId());
381  }
382  }
383 
384  // get the current Surface ID
385  currentID = sfIter->surface->geometryId();
386  // We have work to do: the assignment surface has changed
387  if (not(currentID == lastID)) {
388  // Let's (re-)assess the information
389  lastID = currentID;
390  currentPos = (sfIter)->position;
391  currentPathCorrection = sfIter->surface->pathCorrection(
392  mState.geoContext, currentPos, sfIter->direction);
393  currentAccMaterial = mState.accumulatedMaterial.find(currentID);
394  }
395  // Now assign the material for the accumulation process
396  auto tBin = currentAccMaterial->second.accumulate(
397  currentPos, rmIter->materialSlab, currentPathCorrection);
398  if (touchedMapBins.find(&(currentAccMaterial->second)) ==
399  touchedMapBins.end()) {
400  touchedMapBins.insert(MapBin(&(currentAccMaterial->second), tBin));
401  }
402  if (m_cfg.computeVariance) {
403  touchedMaterialBin[&(currentAccMaterial->second)] =
404  mState.inputSurfaceMaterial[currentID];
405  }
406  ++assignedMaterial[currentID];
407  // Update the material interaction with the associated surface and
408  // intersection
409  rmIter->surface = sfIter->surface;
410  rmIter->intersection = sfIter->position;
411  rmIter->intersectionID = currentID;
412  rmIter->pathCorrection = currentPathCorrection;
413  // Switch to next material
414  ++rmIter;
415  }
416 
417  ACTS_VERBOSE("Surfaces have following number of assigned hits :")
418  for (auto& [key, value] : assignedMaterial) {
419  ACTS_VERBOSE(" + Surface : " << key << " has " << value << " hits.");
420  }
421 
422  // After mapping this track, average the touched bins
423  for (auto tmapBin : touchedMapBins) {
424  std::vector<std::array<size_t, 3>> trackBins = {tmapBin.second};
425  if (m_cfg.computeVariance) {
426  tmapBin.first->trackVariance(
427  trackBins, touchedMaterialBin[tmapBin.first]->materialSlab(
428  trackBins[0][0], trackBins[0][1]));
429  }
430  tmapBin.first->trackAverage(trackBins);
431  }
432 
433  // After mapping this track, average the untouched but intersected bins
434  if (m_cfg.emptyBinCorrection) {
435  // Use the assignedMaterial map to account for empty hits, i.e.
436  // the material surface has been intersected by the mapping ray
437  // but no material step was assigned to this surface
438  for (auto& mSurface : mappingSurfaces) {
439  auto mgID = mSurface.surface->geometryId();
440  // Count an empty hit only if the surface does not appear in the
441  // list of assigned surfaces
442  if (assignedMaterial[mgID] == 0) {
443  auto missedMaterial = mState.accumulatedMaterial.find(mgID);
444  if (m_cfg.computeVariance) {
445  missedMaterial->second.trackVariance(
446  mSurface.position,
447  mState.inputSurfaceMaterial[currentID]->materialSlab(
448  mSurface.position),
449  true);
450  }
451  missedMaterial->second.trackAverage(mSurface.position, true);
452 
453  // Add an empty material hit for future material mapping iteration
454  Acts::MaterialInteraction noMaterial;
455  noMaterial.surface = mSurface.surface;
456  noMaterial.intersection = mSurface.position;
457  noMaterial.intersectionID = mgID;
458  rMaterial.push_back(noMaterial);
459  }
460  }
461  }
462 }
463 
465  State& mState, std::vector<MaterialInteraction>& rMaterial) const {
466  using MapBin = std::pair<AccumulatedSurfaceMaterial*, std::array<size_t, 3>>;
467  std::map<AccumulatedSurfaceMaterial*, std::array<size_t, 3>> touchedMapBins;
468  std::map<AccumulatedSurfaceMaterial*, std::shared_ptr<const ISurfaceMaterial>>
469  touchedMaterialBin;
470 
471  // Looping over all the material interactions
472  auto rmIter = rMaterial.begin();
473  while (rmIter != rMaterial.end()) {
474  // get the current interaction information
475  GeometryIdentifier currentID = rmIter->intersectionID;
476  Vector3 currentPos = rmIter->intersection;
477  auto currentAccMaterial = mState.accumulatedMaterial.find(currentID);
478 
479  // Now assign the material for the accumulation process
480  auto tBin = currentAccMaterial->second.accumulate(
481  currentPos, rmIter->materialSlab, rmIter->pathCorrection);
482  if (touchedMapBins.find(&(currentAccMaterial->second)) ==
483  touchedMapBins.end()) {
484  touchedMapBins.insert(MapBin(&(currentAccMaterial->second), tBin));
485  }
486  if (m_cfg.computeVariance) {
487  touchedMaterialBin[&(currentAccMaterial->second)] =
488  mState.inputSurfaceMaterial[currentID];
489  }
490  ++rmIter;
491  }
492 
493  // After mapping this track, average the touched bins
494  for (auto tmapBin : touchedMapBins) {
495  std::vector<std::array<size_t, 3>> trackBins = {tmapBin.second};
496  if (m_cfg.computeVariance) {
497  tmapBin.first->trackVariance(
498  trackBins,
499  touchedMaterialBin[tmapBin.first]->materialSlab(trackBins[0][0],
500  trackBins[0][1]),
501  true);
502  }
503  // No need to do an extra pass for untouched surfaces they would have been
504  // added to the material interaction in the initial mapping
505  tmapBin.first->trackAverage(trackBins, true);
506  }
507 }