Back to home page

sPhenix code displayed by LXR

 
 

    


File indexing completed on 2025-08-05 08:09:39

0001 // This file is part of the Acts project.
0002 //
0003 // Copyright (C) 2018-2020 CERN for the benefit of the Acts project
0004 //
0005 // This Source Code Form is subject to the terms of the Mozilla Public
0006 // License, v. 2.0. If a copy of the MPL was not distributed with this
0007 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
0008 
0009 #include "Acts/Material/SurfaceMaterialMapper.hpp"
0010 
0011 #include "Acts/Definitions/Algebra.hpp"
0012 #include "Acts/Definitions/Direction.hpp"
0013 #include "Acts/Definitions/Tolerance.hpp"
0014 #include "Acts/EventData/ParticleHypothesis.hpp"
0015 #include "Acts/EventData/TrackParameters.hpp"
0016 #include "Acts/Geometry/ApproachDescriptor.hpp"
0017 #include "Acts/Geometry/BoundarySurfaceT.hpp"
0018 #include "Acts/Geometry/Layer.hpp"
0019 #include "Acts/Geometry/TrackingGeometry.hpp"
0020 #include "Acts/Material/BinnedSurfaceMaterial.hpp"
0021 #include "Acts/Material/ISurfaceMaterial.hpp"
0022 #include "Acts/Material/MaterialInteraction.hpp"
0023 #include "Acts/Material/ProtoSurfaceMaterial.hpp"
0024 #include "Acts/Propagator/AbortList.hpp"
0025 #include "Acts/Propagator/ActionList.hpp"
0026 #include "Acts/Propagator/Navigator.hpp"
0027 #include "Acts/Propagator/Propagator.hpp"
0028 #include "Acts/Propagator/SurfaceCollector.hpp"
0029 #include "Acts/Propagator/VolumeCollector.hpp"
0030 #include "Acts/Surfaces/SurfaceArray.hpp"
0031 #include "Acts/Utilities/BinAdjustment.hpp"
0032 #include "Acts/Utilities/BinUtility.hpp"
0033 #include "Acts/Utilities/BinnedArray.hpp"
0034 #include "Acts/Utilities/Result.hpp"
0035 
0036 #include <cstddef>
0037 #include <ostream>
0038 #include <string>
0039 #include <tuple>
0040 #include <utility>
0041 #include <vector>
0042 
0043 namespace Acts {
0044 struct EndOfWorldReached;
0045 }  // namespace Acts
0046 
0047 Acts::SurfaceMaterialMapper::SurfaceMaterialMapper(
0048     const Config& cfg, StraightLinePropagator propagator,
0049     std::unique_ptr<const Logger> slogger)
0050     : m_cfg(cfg),
0051       m_propagator(std::move(propagator)),
0052       m_logger(std::move(slogger)) {}
0053 
0054 Acts::SurfaceMaterialMapper::State Acts::SurfaceMaterialMapper::createState(
0055     const GeometryContext& gctx, const MagneticFieldContext& mctx,
0056     const TrackingGeometry& tGeometry) const {
0057   // Parse the geometry and find all surfaces with material proxies
0058   auto world = tGeometry.highestTrackingVolume();
0059 
0060   // The Surface material mapping state
0061   State mState(gctx, mctx);
0062   resolveMaterialSurfaces(mState, *world);
0063   collectMaterialVolumes(mState, *world);
0064 
0065   ACTS_DEBUG(mState.accumulatedMaterial.size()
0066              << " Surfaces with PROXIES collected ... ");
0067   for (auto& smg : mState.accumulatedMaterial) {
0068     ACTS_VERBOSE(" -> Surface in with id " << smg.first);
0069   }
0070   return mState;
0071 }
0072 
0073 void Acts::SurfaceMaterialMapper::resolveMaterialSurfaces(
0074     State& mState, const TrackingVolume& tVolume) const {
0075   ACTS_VERBOSE("Checking volume '" << tVolume.volumeName()
0076                                    << "' for material surfaces.")
0077 
0078   ACTS_VERBOSE("- boundary surfaces ...");
0079   // Check the boundary surfaces
0080   for (auto& bSurface : tVolume.boundarySurfaces()) {
0081     checkAndInsert(mState, bSurface->surfaceRepresentation());
0082   }
0083 
0084   ACTS_VERBOSE("- confined layers ...");
0085   // Check the confined layers
0086   if (tVolume.confinedLayers() != nullptr) {
0087     for (auto& cLayer : tVolume.confinedLayers()->arrayObjects()) {
0088       // Take only layers that are not navigation layers
0089       if (cLayer->layerType() != navigation) {
0090         // Check the representing surface
0091         checkAndInsert(mState, cLayer->surfaceRepresentation());
0092         // Get the approach surfaces if present
0093         if (cLayer->approachDescriptor() != nullptr) {
0094           for (auto& aSurface :
0095                cLayer->approachDescriptor()->containedSurfaces()) {
0096             if (aSurface != nullptr) {
0097               checkAndInsert(mState, *aSurface);
0098             }
0099           }
0100         }
0101         // Get the sensitive surface is present
0102         if (cLayer->surfaceArray() != nullptr) {
0103           // Sensitive surface loop
0104           for (auto& sSurface : cLayer->surfaceArray()->surfaces()) {
0105             if (sSurface != nullptr) {
0106               checkAndInsert(mState, *sSurface);
0107             }
0108           }
0109         }
0110       }
0111     }
0112   }
0113   // Step down into the sub volume
0114   if (tVolume.confinedVolumes()) {
0115     for (auto& sVolume : tVolume.confinedVolumes()->arrayObjects()) {
0116       // Recursive call
0117       resolveMaterialSurfaces(mState, *sVolume);
0118     }
0119   }
0120 }
0121 
0122 void Acts::SurfaceMaterialMapper::checkAndInsert(State& mState,
0123                                                  const Surface& surface) const {
0124   auto surfaceMaterial = surface.surfaceMaterial();
0125   // Check if the surface has a proxy
0126   if (surfaceMaterial != nullptr) {
0127     if (m_cfg.computeVariance) {
0128       mState.inputSurfaceMaterial[surface.geometryId()] =
0129           surface.surfaceMaterialSharedPtr();
0130     }
0131     auto geoID = surface.geometryId();
0132     std::size_t volumeID = geoID.volume();
0133     ACTS_DEBUG("Material surface found with volumeID " << volumeID);
0134     ACTS_DEBUG("       - surfaceID is " << geoID);
0135 
0136     // We need a dynamic_cast to either a surface material proxy or
0137     // proper surface material
0138     auto psm = dynamic_cast<const ProtoSurfaceMaterial*>(surfaceMaterial);
0139 
0140     // Get the bin utility: try proxy material first
0141     const BinUtility* bu = (psm != nullptr) ? (&psm->binning()) : nullptr;
0142     if (bu != nullptr) {
0143       // Screen output for Binned Surface material
0144       ACTS_DEBUG("       - (proto) binning is " << *bu);
0145       // Now update
0146       BinUtility buAdjusted = adjustBinUtility(*bu, surface, mState.geoContext);
0147       // Screen output for Binned Surface material
0148       ACTS_DEBUG("       - adjusted binning is " << buAdjusted);
0149       mState.accumulatedMaterial[geoID] =
0150           AccumulatedSurfaceMaterial(buAdjusted);
0151       return;
0152     }
0153 
0154     // Second attempt: binned material
0155     auto bmp = dynamic_cast<const BinnedSurfaceMaterial*>(surfaceMaterial);
0156     bu = (bmp != nullptr) ? (&bmp->binUtility()) : nullptr;
0157     // Creaete a binned type of material
0158     if (bu != nullptr) {
0159       // Screen output for Binned Surface material
0160       ACTS_DEBUG("       - binning is " << *bu);
0161       mState.accumulatedMaterial[geoID] = AccumulatedSurfaceMaterial(*bu);
0162     } else {
0163       // Create a homogeneous type of material
0164       ACTS_DEBUG("       - this is homogeneous material.");
0165       mState.accumulatedMaterial[geoID] = AccumulatedSurfaceMaterial();
0166     }
0167   }
0168 }
0169 
0170 void Acts::SurfaceMaterialMapper::collectMaterialVolumes(
0171     State& mState, const TrackingVolume& tVolume) const {
0172   ACTS_VERBOSE("Checking volume '" << tVolume.volumeName()
0173                                    << "' for material surfaces.")
0174   ACTS_VERBOSE("- Insert Volume ...");
0175   if (tVolume.volumeMaterial() != nullptr) {
0176     mState.volumeMaterial[tVolume.geometryId()] =
0177         tVolume.volumeMaterialSharedPtr();
0178   }
0179 
0180   // Step down into the sub volume
0181   if (tVolume.confinedVolumes()) {
0182     ACTS_VERBOSE("- Check children volume ...");
0183     for (auto& sVolume : tVolume.confinedVolumes()->arrayObjects()) {
0184       // Recursive call
0185       collectMaterialVolumes(mState, *sVolume);
0186     }
0187   }
0188   if (!tVolume.denseVolumes().empty()) {
0189     for (auto& sVolume : tVolume.denseVolumes()) {
0190       // Recursive call
0191       collectMaterialVolumes(mState, *sVolume);
0192     }
0193   }
0194 }
0195 
0196 void Acts::SurfaceMaterialMapper::finalizeMaps(State& mState) const {
0197   // iterate over the map to call the total average
0198   for (auto& accMaterial : mState.accumulatedMaterial) {
0199     ACTS_DEBUG("Finalizing map for Surface " << accMaterial.first);
0200     mState.surfaceMaterial[accMaterial.first] =
0201         accMaterial.second.totalAverage();
0202   }
0203 }
0204 
0205 void Acts::SurfaceMaterialMapper::mapMaterialTrack(
0206     State& mState, RecordedMaterialTrack& mTrack) const {
0207   // Retrieve the recorded material from the recorded material track
0208   auto& rMaterial = mTrack.second.materialInteractions;
0209   ACTS_VERBOSE("Retrieved " << rMaterial.size()
0210                             << " recorded material steps to map.")
0211 
0212   // Check if the material interactions are associated with a surface. If yes we
0213   // simply need to loop over them and accumulate the material
0214   if (rMaterial.begin()->intersectionID != GeometryIdentifier()) {
0215     ACTS_VERBOSE(
0216         "Material surfaces are associated with the material interaction. The "
0217         "association interaction/surfaces won't be performed again.");
0218     mapSurfaceInteraction(mState, rMaterial);
0219     return;
0220   } else {
0221     ACTS_VERBOSE(
0222         "Material interactions need to be associated with surfaces. Collecting "
0223         "all surfaces on the trajectory.");
0224     mapInteraction(mState, mTrack);
0225     return;
0226   }
0227 }
0228 void Acts::SurfaceMaterialMapper::mapInteraction(
0229     State& mState, RecordedMaterialTrack& mTrack) const {
0230   // Retrieve the recorded material from the recorded material track
0231   auto& rMaterial = mTrack.second.materialInteractions;
0232   std::map<GeometryIdentifier, unsigned int> assignedMaterial;
0233   using VectorHelpers::makeVector4;
0234   // Neutral curvilinear parameters
0235   NeutralCurvilinearTrackParameters start(
0236       makeVector4(mTrack.first.first, 0), mTrack.first.second,
0237       1 / mTrack.first.second.norm(), std::nullopt,
0238       NeutralParticleHypothesis::geantino());
0239 
0240   // Prepare Action list and abort list
0241   using MaterialSurfaceCollector = SurfaceCollector<MaterialSurface>;
0242   using MaterialVolumeCollector = VolumeCollector<MaterialVolume>;
0243   using ActionList =
0244       ActionList<MaterialSurfaceCollector, MaterialVolumeCollector>;
0245   using AbortList = AbortList<EndOfWorldReached>;
0246 
0247   PropagatorOptions<ActionList, AbortList> options(mState.geoContext,
0248                                                    mState.magFieldContext);
0249 
0250   // Now collect the material layers by using the straight line propagator
0251   const auto& result = m_propagator.propagate(start, options).value();
0252   auto mcResult = result.get<MaterialSurfaceCollector::result_type>();
0253   auto mvcResult = result.get<MaterialVolumeCollector::result_type>();
0254 
0255   auto mappingSurfaces = mcResult.collected;
0256   auto mappingVolumes = mvcResult.collected;
0257 
0258   // These should be mapped onto the mapping surfaces found
0259   ACTS_VERBOSE("Found     " << mappingSurfaces.size()
0260                             << " mapping surfaces for this track.");
0261   ACTS_VERBOSE("Mapping surfaces are :")
0262   for (auto& mSurface : mappingSurfaces) {
0263     ACTS_VERBOSE(" - Surface : " << mSurface.surface->geometryId()
0264                                  << " at position = (" << mSurface.position.x()
0265                                  << ", " << mSurface.position.y() << ", "
0266                                  << mSurface.position.z() << ")");
0267     assignedMaterial[mSurface.surface->geometryId()] = 0;
0268   }
0269 
0270   // Run the mapping process, i.e. take the recorded material and map it
0271   // onto the mapping surfaces:
0272   // - material steps and surfaces are assumed to be ordered along the
0273   // mapping ray
0274   //- do not record the material inside a volume with material
0275   auto rmIter = rMaterial.begin();
0276   auto sfIter = mappingSurfaces.begin();
0277   auto volIter = mappingVolumes.begin();
0278 
0279   // Use those to minimize the lookup
0280   GeometryIdentifier lastID = GeometryIdentifier();
0281   GeometryIdentifier currentID = GeometryIdentifier();
0282   Vector3 currentPos(0., 0., 0);
0283   float currentPathCorrection = 1.;
0284   auto currentAccMaterial = mState.accumulatedMaterial.end();
0285 
0286   // To remember the bins of this event
0287   using MapBin =
0288       std::pair<AccumulatedSurfaceMaterial*, std::array<std::size_t, 3>>;
0289   using MaterialBin = std::pair<AccumulatedSurfaceMaterial*,
0290                                 std::shared_ptr<const ISurfaceMaterial>>;
0291   std::map<AccumulatedSurfaceMaterial*, std::array<std::size_t, 3>>
0292       touchedMapBins;
0293   std::map<AccumulatedSurfaceMaterial*, std::shared_ptr<const ISurfaceMaterial>>
0294       touchedMaterialBin;
0295   if (sfIter != mappingSurfaces.end() &&
0296       sfIter->surface->surfaceMaterial()->mappingType() ==
0297           Acts::MappingType::PostMapping) {
0298     ACTS_WARNING(
0299         "The first mapping surface is a PostMapping one. Some material from "
0300         "before the PostMapping surface will be mapped onto it ");
0301   }
0302 
0303   // Assign the recorded ones, break if you hit an end
0304   while (rmIter != rMaterial.end() && sfIter != mappingSurfaces.end()) {
0305     // Material not inside current volume
0306     if (volIter != mappingVolumes.end() &&
0307         !volIter->volume->inside(rmIter->position)) {
0308       double distVol = (volIter->position - mTrack.first.first).norm();
0309       double distMat = (rmIter->position - mTrack.first.first).norm();
0310       // Material past the entry point to the current volume
0311       if (distMat - distVol > s_epsilon) {
0312         // Switch to next material volume
0313         ++volIter;
0314         continue;
0315       }
0316     }
0317     /// check if we are inside a material volume
0318     if (volIter != mappingVolumes.end() &&
0319         volIter->volume->inside(rmIter->position)) {
0320       ++rmIter;
0321       continue;
0322     }
0323     // Do we need to switch to next assignment surface ?
0324     if (sfIter != mappingSurfaces.end() - 1) {
0325       int mappingType = sfIter->surface->surfaceMaterial()->mappingType();
0326       int nextMappingType =
0327           (sfIter + 1)->surface->surfaceMaterial()->mappingType();
0328 
0329       if (mappingType == Acts::MappingType::PreMapping ||
0330           mappingType == Acts::MappingType::Sensor) {
0331         // Change surface if the material after the current surface.
0332         if ((rmIter->position - mTrack.first.first).norm() >
0333             (sfIter->position - mTrack.first.first).norm()) {
0334           if (nextMappingType == Acts::MappingType::PostMapping) {
0335             ACTS_WARNING(
0336                 "PreMapping or Sensor surface followed by PostMapping. Some "
0337                 "material "
0338                 "from before the PostMapping surface will be mapped onto it");
0339           }
0340           ++sfIter;
0341           continue;
0342         }
0343       } else if (mappingType == Acts::MappingType::Default ||
0344                  mappingType == Acts::MappingType::PostMapping) {
0345         switch (nextMappingType) {
0346           case Acts::MappingType::PreMapping:
0347           case Acts::MappingType::Default: {
0348             // Change surface if the material closest to the next surface.
0349             if ((rmIter->position - sfIter->position).norm() >
0350                 (rmIter->position - (sfIter + 1)->position).norm()) {
0351               ++sfIter;
0352               continue;
0353             }
0354             break;
0355           }
0356           case Acts::MappingType::PostMapping: {
0357             // Change surface if the material after the next surface.
0358             if ((rmIter->position - sfIter->position).norm() >
0359                 ((sfIter + 1)->position - sfIter->position).norm()) {
0360               ++sfIter;
0361               continue;
0362             }
0363             break;
0364           }
0365           case Acts::MappingType::Sensor: {
0366             // Change surface if the next material after the next surface.
0367             if ((rmIter == rMaterial.end() - 1) ||
0368                 ((rmIter + 1)->position - sfIter->position).norm() >
0369                     ((sfIter + 1)->position - sfIter->position).norm()) {
0370               ++sfIter;
0371               continue;
0372             }
0373             break;
0374           }
0375           default: {
0376             ACTS_ERROR("Incorrect mapping type for the next surface : "
0377                        << (sfIter + 1)->surface->geometryId());
0378           }
0379         }
0380       } else {
0381         ACTS_ERROR("Incorrect mapping type for surface : "
0382                    << sfIter->surface->geometryId());
0383       }
0384     }
0385 
0386     // get the current Surface ID
0387     currentID = sfIter->surface->geometryId();
0388     // We have work to do: the assignment surface has changed
0389     if (!(currentID == lastID)) {
0390       // Let's (re-)assess the information
0391       lastID = currentID;
0392       currentPos = (sfIter)->position;
0393       currentPathCorrection = sfIter->surface->pathCorrection(
0394           mState.geoContext, currentPos, sfIter->direction);
0395       currentAccMaterial = mState.accumulatedMaterial.find(currentID);
0396     }
0397     // Now assign the material for the accumulation process
0398     auto tBin = currentAccMaterial->second.accumulate(
0399         currentPos, rmIter->materialSlab, currentPathCorrection);
0400     if (touchedMapBins.find(&(currentAccMaterial->second)) ==
0401         touchedMapBins.end()) {
0402       touchedMapBins.insert(MapBin(&(currentAccMaterial->second), tBin));
0403     }
0404     if (m_cfg.computeVariance) {
0405       touchedMaterialBin[&(currentAccMaterial->second)] =
0406           mState.inputSurfaceMaterial[currentID];
0407     }
0408     ++assignedMaterial[currentID];
0409     // Update the material interaction with the associated surface and
0410     // intersection
0411     rmIter->surface = sfIter->surface;
0412     rmIter->intersection = sfIter->position;
0413     rmIter->intersectionID = currentID;
0414     rmIter->pathCorrection = currentPathCorrection;
0415     // Switch to next material
0416     ++rmIter;
0417   }
0418 
0419   ACTS_VERBOSE("Surfaces have following number of assigned hits :")
0420   for (auto& [key, value] : assignedMaterial) {
0421     ACTS_VERBOSE(" + Surface : " << key << " has " << value << " hits.");
0422   }
0423 
0424   // After mapping this track, average the touched bins
0425   for (auto tmapBin : touchedMapBins) {
0426     std::vector<std::array<std::size_t, 3>> trackBins = {tmapBin.second};
0427     if (m_cfg.computeVariance) {
0428       // This only makes sense for the binned material
0429       auto binnedMaterial = dynamic_cast<const BinnedSurfaceMaterial*>(
0430           touchedMaterialBin[tmapBin.first].get());
0431       if (binnedMaterial != nullptr) {
0432         tmapBin.first->trackVariance(
0433             trackBins,
0434             binnedMaterial->fullMaterial()[trackBins[0][1]][trackBins[0][0]]);
0435       }
0436     }
0437     tmapBin.first->trackAverage(trackBins);
0438   }
0439 
0440   // After mapping this track, average the untouched but intersected bins
0441   if (m_cfg.emptyBinCorrection) {
0442     // Use the assignedMaterial map to account for empty hits, i.e.
0443     // the material surface has been intersected by the mapping ray
0444     // but no material step was assigned to this surface
0445     for (auto& mSurface : mappingSurfaces) {
0446       auto mgID = mSurface.surface->geometryId();
0447       // Count an empty hit only if the surface does not appear in the
0448       // list of assigned surfaces
0449       if (assignedMaterial[mgID] == 0) {
0450         auto missedMaterial = mState.accumulatedMaterial.find(mgID);
0451         if (m_cfg.computeVariance) {
0452           missedMaterial->second.trackVariance(
0453               mSurface.position,
0454               mState.inputSurfaceMaterial[currentID]->materialSlab(
0455                   mSurface.position),
0456               true);
0457         }
0458         missedMaterial->second.trackAverage(mSurface.position, true);
0459 
0460         // Add an empty material hit for future material mapping iteration
0461         Acts::MaterialInteraction noMaterial;
0462         noMaterial.surface = mSurface.surface;
0463         noMaterial.intersection = mSurface.position;
0464         noMaterial.intersectionID = mgID;
0465         rMaterial.push_back(noMaterial);
0466       }
0467     }
0468   }
0469 }
0470 
0471 void Acts::SurfaceMaterialMapper::mapSurfaceInteraction(
0472     State& mState, std::vector<MaterialInteraction>& rMaterial) const {
0473   using MapBin =
0474       std::pair<AccumulatedSurfaceMaterial*, std::array<std::size_t, 3>>;
0475   std::map<AccumulatedSurfaceMaterial*, std::array<std::size_t, 3>>
0476       touchedMapBins;
0477   std::map<AccumulatedSurfaceMaterial*, std::shared_ptr<const ISurfaceMaterial>>
0478       touchedMaterialBin;
0479 
0480   // Looping over all the material interactions
0481   auto rmIter = rMaterial.begin();
0482   while (rmIter != rMaterial.end()) {
0483     // get the current interaction information
0484     GeometryIdentifier currentID = rmIter->intersectionID;
0485     Vector3 currentPos = rmIter->intersection;
0486     auto currentAccMaterial = mState.accumulatedMaterial.find(currentID);
0487 
0488     // Now assign the material for the accumulation process
0489     auto tBin = currentAccMaterial->second.accumulate(
0490         currentPos, rmIter->materialSlab, rmIter->pathCorrection);
0491     if (touchedMapBins.find(&(currentAccMaterial->second)) ==
0492         touchedMapBins.end()) {
0493       touchedMapBins.insert(MapBin(&(currentAccMaterial->second), tBin));
0494     }
0495     if (m_cfg.computeVariance) {
0496       touchedMaterialBin[&(currentAccMaterial->second)] =
0497           mState.inputSurfaceMaterial[currentID];
0498     }
0499     ++rmIter;
0500   }
0501 
0502   // After mapping this track, average the touched bins
0503   for (auto tmapBin : touchedMapBins) {
0504     std::vector<std::array<std::size_t, 3>> trackBins = {tmapBin.second};
0505     if (m_cfg.computeVariance) {
0506       // This only makes sense for the binned material
0507       auto binnedMaterial = dynamic_cast<const BinnedSurfaceMaterial*>(
0508           touchedMaterialBin[tmapBin.first].get());
0509       if (binnedMaterial != nullptr) {
0510         tmapBin.first->trackVariance(
0511             trackBins,
0512             binnedMaterial->fullMaterial()[trackBins[0][1]][trackBins[0][0]],
0513             true);
0514       }
0515     }
0516     // No need to do an extra pass for untouched surfaces they would have been
0517     // added to the material interaction in the initial mapping
0518     tmapBin.first->trackAverage(trackBins, true);
0519   }
0520 }