Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-12-16 09:23:14

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