Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
VolumeMaterialMapper.cpp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file VolumeMaterialMapper.cpp
1 // This file is part of the Acts project.
2 //
3 // Copyright (C) 2019-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 
17 #include "Acts/Geometry/Layer.hpp"
36 
37 #include <cmath>
38 #include <cstddef>
39 #include <iosfwd>
40 #include <ostream>
41 #include <stdexcept>
42 #include <string>
43 #include <tuple>
44 #include <type_traits>
45 #include <vector>
46 
47 namespace Acts {
48 struct EndOfWorldReached;
49 } // namespace Acts
50 
52  const Config& cfg, StraightLinePropagator propagator,
53  std::unique_ptr<const Logger> slogger)
54  : m_cfg(cfg),
55  m_propagator(std::move(propagator)),
56  m_logger(std::move(slogger)) {}
57 
59  const GeometryContext& gctx, const MagneticFieldContext& mctx,
60  const TrackingGeometry& tGeometry) const {
61  // Parse the geometry and find all surfaces with material proxies
62  auto world = tGeometry.highestTrackingVolume();
63 
64  // The Surface material mapping state
65  State mState(gctx, mctx);
66  resolveMaterialVolume(mState, *world);
67  collectMaterialSurfaces(mState, *world);
68  return mState;
69 }
70 
72  State& mState, const TrackingVolume& tVolume) const {
73  ACTS_VERBOSE("Checking volume '" << tVolume.volumeName()
74  << "' for material surfaces.")
75 
76  ACTS_VERBOSE("- Insert Volume ...");
77  checkAndInsert(mState, tVolume);
78 
79  // Step down into the sub volume
80  if (tVolume.confinedVolumes()) {
81  ACTS_VERBOSE("- Check children volume ...");
82  for (auto& sVolume : tVolume.confinedVolumes()->arrayObjects()) {
83  // Recursive call
84  resolveMaterialVolume(mState, *sVolume);
85  }
86  }
87  if (!tVolume.denseVolumes().empty()) {
88  for (auto& sVolume : tVolume.denseVolumes()) {
89  // Recursive call
90  resolveMaterialVolume(mState, *sVolume);
91  }
92  }
93 }
94 
96  State& mState, const TrackingVolume& volume) const {
97  auto volumeMaterial = volume.volumeMaterial();
98  // Check if the volume has a proxy
99  if (volumeMaterial != nullptr) {
100  auto geoID = volume.geometryId();
101  size_t volumeID = geoID.volume();
102  ACTS_DEBUG("Material volume found with volumeID " << volumeID);
103  ACTS_DEBUG(" - ID is " << geoID);
104 
105  // We need a dynamic_cast to either a volume material proxy or
106  // proper surface material
107  auto psm = dynamic_cast<const ProtoVolumeMaterial*>(volumeMaterial);
108  // Get the bin utility: try proxy material first
109  const BinUtility* bu = (psm != nullptr) ? (&psm->binUtility()) : nullptr;
110  if (bu != nullptr) {
111  // Screen output for Binned Surface material
112  ACTS_DEBUG(" - (proto) binning is " << *bu);
113  // Now update
114  BinUtility buAdjusted = adjustBinUtility(*bu, volume);
115  // Screen output for Binned Surface material
116  ACTS_DEBUG(" - adjusted binning is " << buAdjusted);
117  mState.materialBin[geoID] = buAdjusted;
118  if (bu->dimensions() == 0) {
119  ACTS_DEBUG("Binning of dimension 0 create AccumulatedVolumeMaterial");
120  Acts::AccumulatedVolumeMaterial homogeneousAccumulation;
121  mState.homogeneousGrid[geoID] = homogeneousAccumulation;
122  } else if (bu->dimensions() == 2) {
123  ACTS_DEBUG("Binning of dimension 2 create 2D Grid");
124  std::function<Acts::Vector2(Acts::Vector3)> transfoGlobalToLocal;
125  Acts::Grid2D Grid = createGrid2D(buAdjusted, transfoGlobalToLocal);
126  mState.grid2D.insert(std::make_pair(geoID, Grid));
127  mState.transform2D.insert(std::make_pair(geoID, transfoGlobalToLocal));
128  } else if (bu->dimensions() == 3) {
129  ACTS_DEBUG("Binning of dimension 3 create 3D Grid");
130  std::function<Acts::Vector3(Acts::Vector3)> transfoGlobalToLocal;
131  Acts::Grid3D Grid = createGrid3D(buAdjusted, transfoGlobalToLocal);
132  mState.grid3D.insert(std::make_pair(geoID, Grid));
133  mState.transform3D.insert(std::make_pair(geoID, transfoGlobalToLocal));
134  } else {
135  throw std::invalid_argument(
136  "Incorrect bin dimension, only 0, 2 and 3 are accepted");
137  }
138  return;
139  }
140  // Second attempt: 2D binned material
141  auto bmp2 = dynamic_cast<
143  volumeMaterial);
144  bu = (bmp2 != nullptr) ? (&bmp2->binUtility()) : nullptr;
145  if (bu != nullptr) {
146  // Screen output for Binned Surface material
147  ACTS_DEBUG(" - (2D grid) binning is " << *bu);
148  mState.materialBin[geoID] = *bu;
149  std::function<Acts::Vector2(Acts::Vector3)> transfoGlobalToLocal;
150  Acts::Grid2D Grid = createGrid2D(*bu, transfoGlobalToLocal);
151  mState.grid2D.insert(std::make_pair(geoID, Grid));
152  mState.transform2D.insert(std::make_pair(geoID, transfoGlobalToLocal));
153  return;
154  }
155  // Third attempt: 3D binned material
156  auto bmp3 = dynamic_cast<
158  volumeMaterial);
159  bu = (bmp3 != nullptr) ? (&bmp3->binUtility()) : nullptr;
160  if (bu != nullptr) {
161  // Screen output for Binned Surface material
162  ACTS_DEBUG(" - (3D grid) binning is " << *bu);
163  mState.materialBin[geoID] = *bu;
164  std::function<Acts::Vector3(Acts::Vector3)> transfoGlobalToLocal;
165  Acts::Grid3D Grid = createGrid3D(*bu, transfoGlobalToLocal);
166  mState.grid3D.insert(std::make_pair(geoID, Grid));
167  mState.transform3D.insert(std::make_pair(geoID, transfoGlobalToLocal));
168  return;
169  } else {
170  // Create a homogeneous type of material
171  ACTS_DEBUG(" - this is homogeneous material.");
172  BinUtility buHomogeneous;
173  mState.materialBin[geoID] = buHomogeneous;
174  Acts::AccumulatedVolumeMaterial homogeneousAccumulation;
175  mState.homogeneousGrid[geoID] = homogeneousAccumulation;
176  return;
177  }
178  }
179 }
180 
182  State& mState, const TrackingVolume& tVolume) const {
183  ACTS_VERBOSE("Checking volume '" << tVolume.volumeName()
184  << "' for material surfaces.")
185 
186  ACTS_VERBOSE("- boundary surfaces ...");
187  // Check the boundary surfaces
188  for (auto& bSurface : tVolume.boundarySurfaces()) {
189  if (bSurface->surfaceRepresentation().surfaceMaterial() != nullptr) {
190  mState.surfaceMaterial[bSurface->surfaceRepresentation().geometryId()] =
191  bSurface->surfaceRepresentation().surfaceMaterialSharedPtr();
192  }
193  }
194 
195  ACTS_VERBOSE("- confined layers ...");
196  // Check the confined layers
197  if (tVolume.confinedLayers() != nullptr) {
198  for (auto& cLayer : tVolume.confinedLayers()->arrayObjects()) {
199  // Take only layers that are not navigation layers
200  if (cLayer->layerType() != navigation) {
201  // Check the representing surface
202  if (cLayer->surfaceRepresentation().surfaceMaterial() != nullptr) {
203  mState.surfaceMaterial[cLayer->surfaceRepresentation().geometryId()] =
204  cLayer->surfaceRepresentation().surfaceMaterialSharedPtr();
205  }
206  // Get the approach surfaces if present
207  if (cLayer->approachDescriptor() != nullptr) {
208  for (auto& aSurface :
209  cLayer->approachDescriptor()->containedSurfaces()) {
210  if (aSurface != nullptr) {
211  if (aSurface->surfaceMaterial() != nullptr) {
212  mState.surfaceMaterial[aSurface->geometryId()] =
213  aSurface->surfaceMaterialSharedPtr();
214  }
215  }
216  }
217  }
218  // Get the sensitive surface is present
219  if (cLayer->surfaceArray() != nullptr) {
220  // Sensitive surface loop
221  for (auto& sSurface : cLayer->surfaceArray()->surfaces()) {
222  if (sSurface != nullptr) {
223  if (sSurface->surfaceMaterial() != nullptr) {
224  mState.surfaceMaterial[sSurface->geometryId()] =
225  sSurface->surfaceMaterialSharedPtr();
226  }
227  }
228  }
229  }
230  }
231  }
232  }
233  // Step down into the sub volume
234  if (tVolume.confinedVolumes()) {
235  for (auto& sVolume : tVolume.confinedVolumes()->arrayObjects()) {
236  // Recursive call
237  collectMaterialSurfaces(mState, *sVolume);
238  }
239  }
240 }
241 
243  State& mState,
244  std::pair<const GeometryIdentifier, BinUtility>& currentBinning,
245  Acts::MaterialSlab properties, const Vector3& position,
246  Vector3 direction) const {
247  if (currentBinning.second.dimensions() == 0) {
248  // Writing homogeneous material for the current volumes no need to create
249  // extra hits. We directly accumulate the material
250  mState.homogeneousGrid[currentBinning.first].accumulate(properties);
251  return;
252  }
253 
254  // Computing the extra hits properties based on the mappingStep length
255  int volumeStep =
256  static_cast<int>(std::floor(properties.thickness() / m_cfg.mappingStep));
257  float remainder = properties.thickness() - m_cfg.mappingStep * volumeStep;
258  properties.scaleThickness(m_cfg.mappingStep / properties.thickness());
259  direction = direction * (m_cfg.mappingStep / direction.norm());
260 
261  for (int extraStep = 0; extraStep < volumeStep; extraStep++) {
262  Vector3 extraPosition = position + extraStep * direction;
263  // Create additional extrapolated points for the grid mapping
264 
265  if (currentBinning.second.dimensions() == 2) {
266  auto grid = mState.grid2D.find(currentBinning.first);
267  if (grid != mState.grid2D.end()) {
268  // Find which grid bin the material fall into then accumulate
269  Acts::Grid2D::index_t index = grid->second.localBinsFromLowerLeftEdge(
270  mState.transform2D[currentBinning.first](extraPosition));
271  grid->second.atLocalBins(index).accumulate(properties);
272  } else {
273  throw std::domain_error("No grid 2D was found");
274  }
275  } else if (currentBinning.second.dimensions() == 3) {
276  auto grid = mState.grid3D.find(currentBinning.first);
277  if (grid != mState.grid3D.end()) {
278  // Find which grid bin the material fall into then accumulate
279  Acts::Grid3D::index_t index = grid->second.localBinsFromLowerLeftEdge(
280  mState.transform3D[currentBinning.first](extraPosition));
281  grid->second.atLocalBins(index).accumulate(properties);
282  } else {
283  throw std::domain_error("No grid 3D was found");
284  }
285  }
286  }
287 
288  if (remainder > 0) {
289  // We need to have an additional extra hit with the remainder length. Adjust
290  // the thickness of the last extrapolated step
291  properties.scaleThickness(remainder / properties.thickness());
292  Vector3 extraPosition = position + volumeStep * direction;
293  if (currentBinning.second.dimensions() == 2) {
294  auto grid = mState.grid2D.find(currentBinning.first);
295  if (grid != mState.grid2D.end()) {
296  // Find which grid bin the material fall into then accumulate
297  Acts::Grid2D::index_t index = grid->second.localBinsFromLowerLeftEdge(
298  mState.transform2D[currentBinning.first](extraPosition));
299  grid->second.atLocalBins(index).accumulate(properties);
300  } else {
301  throw std::domain_error("No grid 2D was found");
302  }
303  } else if (currentBinning.second.dimensions() == 3) {
304  auto grid = mState.grid3D.find(currentBinning.first);
305  if (grid != mState.grid3D.end()) {
306  // Find which grid bin the material fall into then accumulate
307  Acts::Grid3D::index_t index = grid->second.localBinsFromLowerLeftEdge(
308  mState.transform3D[currentBinning.first](extraPosition));
309  grid->second.atLocalBins(index).accumulate(properties);
310  } else {
311  throw std::domain_error("No grid 3D was found");
312  }
313  }
314  }
315 }
316 
318  // iterate over the volumes
319  for (auto& matBin : mState.materialBin) {
320  ACTS_DEBUG("Create the material for volume " << matBin.first);
321  if (matBin.second.dimensions() == 0) {
322  // Average the homogeneous volume material then store it
323  ACTS_DEBUG("Homogeneous material volume");
324  Acts::Material mat = mState.homogeneousGrid[matBin.first].average();
325  mState.volumeMaterial[matBin.first] =
326  std::make_unique<HomogeneousVolumeMaterial>(mat);
327  } else if (matBin.second.dimensions() == 2) {
328  // Average the material in the 2D grid then create an
329  // InterpolatedMaterialMap
330  ACTS_DEBUG("Grid material volume");
331  auto grid = mState.grid2D.find(matBin.first);
332  if (grid != mState.grid2D.end()) {
333  Acts::MaterialGrid2D matGrid = mapMaterialPoints(grid->second);
335  mState.transform2D[matBin.first], matGrid);
336  mState.volumeMaterial[matBin.first] = std::make_unique<
338  std::move(matMap), matBin.second);
339  } else {
340  throw std::domain_error("No grid 2D was found");
341  }
342  } else if (matBin.second.dimensions() == 3) {
343  // Average the material in the 3D grid then create an
344  // InterpolatedMaterialMap
345  ACTS_DEBUG("Grid material volume");
346  auto grid = mState.grid3D.find(matBin.first);
347  if (grid != mState.grid3D.end()) {
348  Acts::MaterialGrid3D matGrid = mapMaterialPoints(grid->second);
350  mState.transform3D[matBin.first], matGrid);
351  mState.volumeMaterial[matBin.first] = std::make_unique<
353  std::move(matMap), matBin.second);
354  } else {
355  throw std::domain_error("No grid 3D was found");
356  }
357  } else {
358  throw std::invalid_argument(
359  "Incorrect bin dimension, only 0, 2 and 3 are accepted");
360  }
361  }
362 }
363 
365  State& mState, RecordedMaterialTrack& mTrack) const {
367 
368  // Neutral curvilinear parameters
370  makeVector4(mTrack.first.first, 0), mTrack.first.second,
371  1 / mTrack.first.second.norm(), std::nullopt,
372  NeutralParticleHypothesis::geantino());
373 
374  // Prepare Action list and abort list
375  using BoundSurfaceCollector = SurfaceCollector<BoundSurfaceSelector>;
376  using MaterialVolumeCollector = VolumeCollector<MaterialVolumeSelector>;
377  using ActionList = ActionList<BoundSurfaceCollector, MaterialVolumeCollector>;
378  using AbortList = AbortList<EndOfWorldReached>;
379 
381  mState.magFieldContext);
382 
383  // Now collect the material volume by using the straight line propagator
384  const auto& result = m_propagator.propagate(start, options).value();
385  auto mcResult = result.get<BoundSurfaceCollector::result_type>();
386  auto mvcResult = result.get<MaterialVolumeCollector::result_type>();
387 
388  auto mappingSurfaces = mcResult.collected;
389  auto mappingVolumes = mvcResult.collected;
390 
391  // Retrieve the recorded material from the recorded material track
392  auto& rMaterial = mTrack.second.materialInteractions;
393  ACTS_VERBOSE("Retrieved " << rMaterial.size()
394  << " recorded material steps to map.")
395 
396  // These should be mapped onto the mapping surfaces found
397  ACTS_VERBOSE("Found " << mappingVolumes.size()
398  << " mapping volumes for this track.");
399  ACTS_VERBOSE("Mapping volumes are :")
400  for (auto& mVolumes : mappingVolumes) {
401  ACTS_VERBOSE(" - Volume : " << mVolumes.volume->geometryId()
402  << " at position = (" << mVolumes.position.x()
403  << ", " << mVolumes.position.y() << ", "
404  << mVolumes.position.z() << ")");
405 
406  mappingVolumes.push_back(mVolumes);
407  }
408  // Run the mapping process, i.e. take the recorded material and map it
409  // onto the mapping volume:
410  auto rmIter = rMaterial.begin();
411  auto sfIter = mappingSurfaces.begin();
412  auto volIter = mappingVolumes.begin();
413 
414  // Use those to minimize the lookup
417  auto currentBinning = mState.materialBin.end();
418 
419  // store end position of the last material slab
420  Acts::Vector3 lastPositionEnd = {0, 0, 0};
421  Acts::Vector3 direction = {0, 0, 0};
422 
423  if (volIter != mappingVolumes.end()) {
424  lastPositionEnd = volIter->position;
425  }
426 
427  // loop over all the material hit in the track or until there no more volume
428  // to map onto
429  while (rmIter != rMaterial.end() && volIter != mappingVolumes.end()) {
430  if (volIter != mappingVolumes.end() &&
431  !volIter->volume->inside(rmIter->position)) {
432  // Check if the material point is past the entry point to the current
433  // volume (this prevents switching volume before the first volume has been
434  // reached)
435  double distVol = (volIter->position - mTrack.first.first).norm();
436  double distMat = (rmIter->position - mTrack.first.first).norm();
437  if (distMat - distVol > s_epsilon) {
438  // Switch to next material volume
439  ++volIter;
440  continue;
441  }
442  }
443  if (volIter != mappingVolumes.end() &&
444  volIter->volume->inside(rmIter->position, s_epsilon)) {
445  currentID = volIter->volume->geometryId();
446  direction = rmIter->direction;
447  if (not(currentID == lastID)) {
448  // Let's (re-)assess the information
449  lastID = currentID;
450  lastPositionEnd = volIter->position;
451  currentBinning = mState.materialBin.find(currentID);
452  }
453  // If the current volume has a ProtoVolumeMaterial
454  // and the material hit has a non 0 thickness
455  if (currentBinning != mState.materialBin.end() &&
456  rmIter->materialSlab.thickness() > 0) {
457  // check if there is vacuum between this material point and the last one
458  float vacuumThickness = (rmIter->position - lastPositionEnd).norm();
459  if (vacuumThickness > s_epsilon) {
460  auto properties = Acts::MaterialSlab(vacuumThickness);
461  // creat vacuum hits
462  createExtraHits(mState, *currentBinning, properties, lastPositionEnd,
463  direction);
464  }
465  // determine the position of the last material slab using the track
466  // direction
467  direction =
468  direction * (rmIter->materialSlab.thickness() / direction.norm());
469  lastPositionEnd = rmIter->position + direction;
470  // create additional material point
471  createExtraHits(mState, *currentBinning, rmIter->materialSlab,
472  rmIter->position, direction);
473  }
474 
475  // check if we have reached the end of the volume or the last hit of the
476  // track.
477  if ((rmIter + 1) == rMaterial.end() ||
478  !volIter->volume->inside((rmIter + 1)->position, s_epsilon)) {
479  // find the boundary surface corresponding to the end of the volume
480  while (sfIter != mappingSurfaces.end()) {
481  if (sfIter->surface->geometryId().volume() == lastID.volume() ||
482  ((volIter + 1) != mappingVolumes.end() &&
483  sfIter->surface->geometryId().volume() ==
484  (volIter + 1)->volume->geometryId().volume())) {
485  double distVol = (volIter->position - mTrack.first.first).norm();
486  double distSur = (sfIter->position - mTrack.first.first).norm();
487  if (distSur - distVol > s_epsilon) {
488  float vacuumThickness =
489  (sfIter->position - lastPositionEnd).norm();
490  // if the last material slab stop before the boundary surface
491  // create vacuum hits
492  if (vacuumThickness > s_epsilon) {
493  auto properties = Acts::MaterialSlab(vacuumThickness);
494  createExtraHits(mState, *currentBinning, properties,
495  lastPositionEnd, direction);
496  lastPositionEnd = sfIter->position;
497  }
498  break;
499  }
500  }
501  sfIter++;
502  }
503  }
504  rmIter->volume = volIter->volume;
505  rmIter->intersectionID = currentID;
506  rmIter->intersection = rmIter->position;
507  }
508  ++rmIter;
509  }
510 }