File indexing completed on 2025-12-16 09:23:14
0001
0002
0003
0004
0005
0006
0007
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
0049 auto world = tGeometry.highestTrackingVolume();
0050
0051
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
0071 for (auto& bSurface : tVolume.boundarySurfaces()) {
0072 checkAndInsert(mState, bSurface->surfaceRepresentation());
0073 }
0074
0075 ACTS_VERBOSE("- confined layers ...");
0076
0077 if (tVolume.confinedLayers() != nullptr) {
0078 for (auto& cLayer : tVolume.confinedLayers()->arrayObjects()) {
0079
0080 if (cLayer->layerType() != navigation) {
0081
0082 checkAndInsert(mState, cLayer->surfaceRepresentation());
0083
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
0093 if (cLayer->surfaceArray() != nullptr) {
0094
0095 for (auto& sSurface : cLayer->surfaceArray()->surfaces()) {
0096 if (sSurface != nullptr) {
0097 checkAndInsert(mState, *sSurface);
0098 }
0099 }
0100 }
0101 }
0102 }
0103 }
0104
0105 if (tVolume.confinedVolumes()) {
0106 for (auto& sVolume : tVolume.confinedVolumes()->arrayObjects()) {
0107
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
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
0128
0129 auto psm = dynamic_cast<const ProtoSurfaceMaterial*>(surfaceMaterial);
0130
0131
0132 const BinUtility* bu = (psm != nullptr) ? (&psm->binning()) : nullptr;
0133 if (bu != nullptr) {
0134
0135 ACTS_DEBUG(" - (proto) binning is " << *bu);
0136
0137 BinUtility buAdjusted = adjustBinUtility(*bu, surface, mState.geoContext);
0138
0139 ACTS_DEBUG(" - adjusted binning is " << buAdjusted);
0140 mState.accumulatedMaterial[geoID] =
0141 AccumulatedSurfaceMaterial(buAdjusted);
0142 return;
0143 }
0144
0145
0146 auto bmp = dynamic_cast<const BinnedSurfaceMaterial*>(surfaceMaterial);
0147 bu = (bmp != nullptr) ? (&bmp->binUtility()) : nullptr;
0148
0149 if (bu != nullptr) {
0150
0151 ACTS_DEBUG(" - binning is " << *bu);
0152 mState.accumulatedMaterial[geoID] = AccumulatedSurfaceMaterial(*bu);
0153 } else {
0154
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
0171 if (tVolume.confinedVolumes()) {
0172 ACTS_VERBOSE("- Check children volume ...");
0173 for (auto& sVolume : tVolume.confinedVolumes()->arrayObjects()) {
0174
0175 collectMaterialVolumes(mState, *sVolume);
0176 }
0177 }
0178 if (!tVolume.denseVolumes().empty()) {
0179 for (auto& sVolume : tVolume.denseVolumes()) {
0180
0181 collectMaterialVolumes(mState, *sVolume);
0182 }
0183 }
0184 }
0185
0186 void SurfaceMaterialMapper::finalizeMaps(State& mState) const {
0187
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
0198 auto& rMaterial = mTrack.second.materialInteractions;
0199 ACTS_VERBOSE("Retrieved " << rMaterial.size()
0200 << " recorded material steps to map.");
0201
0202
0203
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
0221 auto& rMaterial = mTrack.second.materialInteractions;
0222 std::map<GeometryIdentifier, unsigned int> assignedMaterial;
0223 using VectorHelpers::makeVector4;
0224
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
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
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
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
0261
0262
0263
0264
0265 auto rmIter = rMaterial.begin();
0266 auto sfIter = mappingSurfaces.begin();
0267 auto volIter = mappingVolumes.begin();
0268
0269
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
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
0292 while (rmIter != rMaterial.end() && sfIter != mappingSurfaces.end()) {
0293
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
0299 if (distMat - distVol > s_epsilon) {
0300
0301 ++volIter;
0302 continue;
0303 }
0304 }
0305
0306 if (volIter != mappingVolumes.end() &&
0307 volIter->volume->inside(rmIter->position)) {
0308 ++rmIter;
0309 continue;
0310 }
0311
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
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
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
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
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
0375 currentID = sfIter->surface->geometryId();
0376
0377 if (!(currentID == lastID)) {
0378
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
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
0397
0398 rmIter->surface = sfIter->surface;
0399 rmIter->intersection = sfIter->position;
0400 rmIter->intersectionID = currentID;
0401 rmIter->pathCorrection = currentPathCorrection;
0402
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
0412 for (const auto& [key, value] : touchedMapBins) {
0413 std::vector<std::array<std::size_t, 3>> trackBins = {value};
0414 if (m_cfg.computeVariance) {
0415
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
0428 if (m_cfg.emptyBinCorrection) {
0429
0430
0431
0432 for (auto& mSurface : mappingSurfaces) {
0433 auto mgID = mSurface.surface->geometryId();
0434
0435
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
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
0468 auto rmIter = rMaterial.begin();
0469 while (rmIter != rMaterial.end()) {
0470
0471 GeometryIdentifier currentID = rmIter->intersectionID;
0472 Vector3 currentPos = rmIter->intersection;
0473 auto currentAccMaterial = mState.accumulatedMaterial.find(currentID);
0474
0475
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
0489 for (const auto& [key, value] : touchedMapBins) {
0490 std::vector<std::array<std::size_t, 3>> trackBins = {value};
0491 if (m_cfg.computeVariance) {
0492
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
0503
0504 key->trackAverage(trackBins, true);
0505 }
0506 }
0507
0508 }