File indexing completed on 2026-09-17 08:21:29
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include "Acts/Material/VolumeMaterialMapper.hpp"
0010
0011 #include "Acts/Definitions/Tolerance.hpp"
0012 #include "Acts/EventData/ParticleHypothesis.hpp"
0013 #include "Acts/Geometry/ApproachDescriptor.hpp"
0014 #include "Acts/Geometry/Layer.hpp"
0015 #include "Acts/Geometry/TrackingGeometry.hpp"
0016 #include "Acts/Material/AccumulatedVolumeMaterial.hpp"
0017 #include "Acts/Material/HomogeneousVolumeMaterial.hpp"
0018 #include "Acts/Material/IVolumeMaterial.hpp"
0019 #include "Acts/Material/InterpolatedMaterialMap.hpp"
0020 #include "Acts/Material/Material.hpp"
0021 #include "Acts/Material/MaterialGridHelper.hpp"
0022 #include "Acts/Material/MaterialInteraction.hpp"
0023 #include "Acts/Material/ProtoVolumeMaterial.hpp"
0024 #include "Acts/Propagator/ActorList.hpp"
0025 #include "Acts/Propagator/SurfaceCollector.hpp"
0026 #include "Acts/Propagator/VolumeCollector.hpp"
0027 #include "Acts/Surfaces/SurfaceArray.hpp"
0028 #include "Acts/Utilities/BinAdjustmentVolume.hpp"
0029 #include "Acts/Utilities/Grid.hpp"
0030 #include "Acts/Utilities/Result.hpp"
0031
0032 #include <cmath>
0033 #include <cstddef>
0034 #include <iosfwd>
0035 #include <ostream>
0036 #include <stdexcept>
0037 #include <vector>
0038
0039 namespace Acts {
0040
0041 VolumeMaterialMapper::VolumeMaterialMapper(
0042 const Config& cfg, StraightLinePropagator propagator,
0043 std::unique_ptr<const Logger> slogger)
0044 : m_cfg(cfg),
0045 m_propagator(std::move(propagator)),
0046 m_logger(std::move(slogger)) {}
0047
0048 VolumeMaterialMapper::State VolumeMaterialMapper::createState(
0049 const GeometryContext& gctx, const MagneticFieldContext& mctx,
0050 const TrackingGeometry& tGeometry) const {
0051
0052 auto world = tGeometry.highestTrackingVolume();
0053
0054
0055 State mState(gctx, mctx);
0056 resolveMaterialVolume(mState, *world);
0057 collectMaterialSurfaces(mState, *world);
0058 return mState;
0059 }
0060
0061 void VolumeMaterialMapper::resolveMaterialVolume(
0062 State& mState, const TrackingVolume& tVolume) const {
0063 ACTS_VERBOSE("Checking volume '" << tVolume.volumeName()
0064 << "' for material surfaces.");
0065
0066 ACTS_VERBOSE("- Insert Volume ...");
0067 checkAndInsert(mState, tVolume);
0068
0069
0070 if (tVolume.confinedVolumes()) {
0071 ACTS_VERBOSE("- Check children volume ...");
0072 for (auto& sVolume : tVolume.confinedVolumes()->arrayObjects()) {
0073
0074 resolveMaterialVolume(mState, *sVolume);
0075 }
0076 }
0077 if (!tVolume.denseVolumes().empty()) {
0078 for (auto& sVolume : tVolume.denseVolumes()) {
0079
0080 resolveMaterialVolume(mState, *sVolume);
0081 }
0082 }
0083 }
0084
0085 void VolumeMaterialMapper::checkAndInsert(State& mState,
0086 const TrackingVolume& volume) const {
0087 auto volumeMaterial = volume.volumeMaterial();
0088
0089 if (volumeMaterial != nullptr) {
0090 auto geoID = volume.geometryId();
0091 std::size_t volumeID = geoID.volume();
0092 ACTS_DEBUG("Material volume found with volumeID " << volumeID);
0093 ACTS_DEBUG(" - ID is " << geoID);
0094
0095
0096
0097 auto psm = dynamic_cast<const ProtoVolumeMaterial*>(volumeMaterial);
0098
0099 const BinUtility* bu = (psm != nullptr) ? (&psm->binUtility()) : nullptr;
0100 if (bu != nullptr) {
0101
0102 ACTS_DEBUG(" - (proto) binning is " << *bu);
0103
0104 BinUtility buAdjusted = adjustBinUtility(mState.geoContext, *bu, volume);
0105
0106 ACTS_DEBUG(" - adjusted binning is " << buAdjusted);
0107 mState.materialBin[geoID] = buAdjusted;
0108 if (bu->dimensions() == 0) {
0109 ACTS_DEBUG("Binning of dimension 0 create AccumulatedVolumeMaterial");
0110 AccumulatedVolumeMaterial homogeneousAccumulation;
0111 mState.homogeneousGrid[geoID] = homogeneousAccumulation;
0112 } else if (bu->dimensions() == 2) {
0113 ACTS_DEBUG("Binning of dimension 2 create 2D Grid");
0114 std::function<Vector2(Vector3)> transfoGlobalToLocal;
0115 Grid2D Grid = createGrid2D(buAdjusted, transfoGlobalToLocal);
0116 mState.grid2D.insert(std::make_pair(geoID, Grid));
0117 mState.transform2D.insert(std::make_pair(geoID, transfoGlobalToLocal));
0118 } else if (bu->dimensions() == 3) {
0119 ACTS_DEBUG("Binning of dimension 3 create 3D Grid");
0120 std::function<Vector3(Vector3)> transfoGlobalToLocal;
0121 Grid3D Grid = createGrid3D(buAdjusted, transfoGlobalToLocal);
0122 mState.grid3D.insert(std::make_pair(geoID, Grid));
0123 mState.transform3D.insert(std::make_pair(geoID, transfoGlobalToLocal));
0124 } else {
0125 throw std::invalid_argument(
0126 "Incorrect bin dimension, only 0, 2 and 3 are accepted");
0127 }
0128 return;
0129 }
0130
0131 auto bmp2 = dynamic_cast<
0132 const InterpolatedMaterialMap<MaterialMapLookup<MaterialGrid2D>>*>(
0133 volumeMaterial);
0134 bu = (bmp2 != nullptr) ? (&bmp2->binUtility()) : nullptr;
0135 if (bu != nullptr) {
0136
0137 ACTS_DEBUG(" - (2D grid) binning is " << *bu);
0138 mState.materialBin[geoID] = *bu;
0139 std::function<Vector2(Vector3)> transfoGlobalToLocal;
0140 Grid2D Grid = createGrid2D(*bu, transfoGlobalToLocal);
0141 mState.grid2D.insert(std::make_pair(geoID, Grid));
0142 mState.transform2D.insert(std::make_pair(geoID, transfoGlobalToLocal));
0143 return;
0144 }
0145
0146 auto bmp3 = dynamic_cast<
0147 const InterpolatedMaterialMap<MaterialMapLookup<MaterialGrid3D>>*>(
0148 volumeMaterial);
0149 bu = (bmp3 != nullptr) ? (&bmp3->binUtility()) : nullptr;
0150 if (bu != nullptr) {
0151
0152 ACTS_DEBUG(" - (3D grid) binning is " << *bu);
0153 mState.materialBin[geoID] = *bu;
0154 std::function<Vector3(Vector3)> transfoGlobalToLocal;
0155 Grid3D Grid = createGrid3D(*bu, transfoGlobalToLocal);
0156 mState.grid3D.insert(std::make_pair(geoID, Grid));
0157 mState.transform3D.insert(std::make_pair(geoID, transfoGlobalToLocal));
0158 return;
0159 } else {
0160
0161 ACTS_DEBUG(" - this is homogeneous material.");
0162 BinUtility buHomogeneous;
0163 mState.materialBin[geoID] = buHomogeneous;
0164 AccumulatedVolumeMaterial homogeneousAccumulation;
0165 mState.homogeneousGrid[geoID] = homogeneousAccumulation;
0166 return;
0167 }
0168 }
0169 }
0170
0171 void VolumeMaterialMapper::collectMaterialSurfaces(
0172 State& mState, const TrackingVolume& tVolume) const {
0173 ACTS_VERBOSE("Checking volume '" << tVolume.volumeName()
0174 << "' for material surfaces.");
0175
0176 ACTS_VERBOSE("- boundary surfaces ...");
0177
0178 for (auto& bSurface : tVolume.boundarySurfaces()) {
0179 if (bSurface->surfaceRepresentation().hasMaterial()) {
0180 mState.surfaceMaterial[bSurface->surfaceRepresentation().geometryId()] =
0181 bSurface->surfaceRepresentation().surfaceMaterialSharedPtr();
0182 }
0183 }
0184
0185 ACTS_VERBOSE("- confined layers ...");
0186
0187 if (tVolume.confinedLayers() != nullptr) {
0188 for (auto& cLayer : tVolume.confinedLayers()->arrayObjects()) {
0189
0190 if (cLayer->layerType() == navigation) {
0191 continue;
0192 }
0193
0194
0195 if (cLayer->surfaceRepresentation().hasMaterial()) {
0196 mState.surfaceMaterial[cLayer->surfaceRepresentation().geometryId()] =
0197 cLayer->surfaceRepresentation().surfaceMaterialSharedPtr();
0198 }
0199
0200
0201 if (cLayer->approachDescriptor() != nullptr) {
0202 for (auto& aSurface :
0203 cLayer->approachDescriptor()->containedSurfaces()) {
0204 if (aSurface != nullptr && aSurface->hasMaterial()) {
0205 mState.surfaceMaterial[aSurface->geometryId()] =
0206 aSurface->surfaceMaterialSharedPtr();
0207 }
0208 }
0209 }
0210
0211
0212 if (cLayer->surfaceArray() != nullptr) {
0213
0214 for (auto& sSurface : cLayer->surfaceArray()->surfaces()) {
0215 if (sSurface != nullptr && sSurface->hasMaterial()) {
0216 mState.surfaceMaterial[sSurface->geometryId()] =
0217 sSurface->surfaceMaterialSharedPtr();
0218 }
0219 }
0220 }
0221 }
0222 }
0223
0224 if (tVolume.confinedVolumes()) {
0225 for (auto& sVolume : tVolume.confinedVolumes()->arrayObjects()) {
0226
0227 collectMaterialSurfaces(mState, *sVolume);
0228 }
0229 }
0230 }
0231
0232 void VolumeMaterialMapper::createExtraHits(
0233 State& mState,
0234 std::pair<const GeometryIdentifier, BinUtility>& currentBinning,
0235 MaterialSlab properties, const Vector3& position, Vector3 direction) const {
0236 if (currentBinning.second.dimensions() == 0) {
0237
0238
0239 mState.homogeneousGrid[currentBinning.first].accumulate(properties);
0240 return;
0241 }
0242
0243
0244 int volumeStep =
0245 static_cast<int>(std::floor(properties.thickness() / m_cfg.mappingStep));
0246 float remainder = properties.thickness() - m_cfg.mappingStep * volumeStep;
0247 properties.scaleThickness(m_cfg.mappingStep / properties.thickness());
0248 direction = direction * (m_cfg.mappingStep / direction.norm());
0249
0250 for (int extraStep = 0; extraStep < volumeStep; extraStep++) {
0251 Vector3 extraPosition = position + extraStep * direction;
0252
0253
0254 if (currentBinning.second.dimensions() == 2) {
0255 auto grid = mState.grid2D.find(currentBinning.first);
0256 if (grid != mState.grid2D.end()) {
0257
0258 Grid2D::index_t index =
0259 grid->second.multiAxis().getLocalBinsFromLowerLeftEdge(
0260 mState.transform2D[currentBinning.first](extraPosition));
0261 grid->second.atLocalBins(index).accumulate(properties);
0262 } else {
0263 throw std::domain_error("No grid 2D was found");
0264 }
0265 } else if (currentBinning.second.dimensions() == 3) {
0266 auto grid = mState.grid3D.find(currentBinning.first);
0267 if (grid != mState.grid3D.end()) {
0268
0269 Grid3D::index_t index =
0270 grid->second.multiAxis().getLocalBinsFromLowerLeftEdge(
0271 mState.transform3D[currentBinning.first](extraPosition));
0272 grid->second.atLocalBins(index).accumulate(properties);
0273 } else {
0274 throw std::domain_error("No grid 3D was found");
0275 }
0276 }
0277 }
0278
0279 if (remainder > 0) {
0280
0281
0282 properties.scaleThickness(remainder / properties.thickness());
0283 Vector3 extraPosition = position + volumeStep * direction;
0284 if (currentBinning.second.dimensions() == 2) {
0285 auto grid = mState.grid2D.find(currentBinning.first);
0286 if (grid != mState.grid2D.end()) {
0287
0288 Grid2D::index_t index =
0289 grid->second.multiAxis().getLocalBinsFromLowerLeftEdge(
0290 mState.transform2D[currentBinning.first](extraPosition));
0291 grid->second.atLocalBins(index).accumulate(properties);
0292 } else {
0293 throw std::domain_error("No grid 2D was found");
0294 }
0295 } else if (currentBinning.second.dimensions() == 3) {
0296 auto grid = mState.grid3D.find(currentBinning.first);
0297 if (grid != mState.grid3D.end()) {
0298
0299 Grid3D::index_t index =
0300 grid->second.multiAxis().getLocalBinsFromLowerLeftEdge(
0301 mState.transform3D[currentBinning.first](extraPosition));
0302 grid->second.atLocalBins(index).accumulate(properties);
0303 } else {
0304 throw std::domain_error("No grid 3D was found");
0305 }
0306 }
0307 }
0308 }
0309
0310 void VolumeMaterialMapper::finalizeMaps(State& mState) const {
0311
0312 for (auto& matBin : mState.materialBin) {
0313 ACTS_DEBUG("Create the material for volume " << matBin.first);
0314 if (matBin.second.dimensions() == 0) {
0315
0316 ACTS_DEBUG("Homogeneous material volume");
0317 Material mat = mState.homogeneousGrid[matBin.first].average();
0318 mState.volumeMaterial[matBin.first] =
0319 std::make_unique<HomogeneousVolumeMaterial>(mat);
0320 } else if (matBin.second.dimensions() == 2) {
0321
0322
0323 ACTS_DEBUG("Grid material volume");
0324 auto grid = mState.grid2D.find(matBin.first);
0325 if (grid != mState.grid2D.end()) {
0326 MaterialGrid2D matGrid = mapMaterialPoints(grid->second);
0327 MaterialMapLookup<MaterialGrid2D> matMap(
0328 mState.transform2D[matBin.first], matGrid);
0329 mState.volumeMaterial[matBin.first] = std::make_unique<
0330 InterpolatedMaterialMap<MaterialMapLookup<MaterialGrid2D>>>(
0331 std::move(matMap), matBin.second);
0332 } else {
0333 throw std::domain_error("No grid 2D was found");
0334 }
0335 } else if (matBin.second.dimensions() == 3) {
0336
0337
0338 ACTS_DEBUG("Grid material volume");
0339 auto grid = mState.grid3D.find(matBin.first);
0340 if (grid != mState.grid3D.end()) {
0341 MaterialGrid3D matGrid = mapMaterialPoints(grid->second);
0342 MaterialMapLookup<MaterialGrid3D> matMap(
0343 mState.transform3D[matBin.first], matGrid);
0344 mState.volumeMaterial[matBin.first] = std::make_unique<
0345 InterpolatedMaterialMap<MaterialMapLookup<MaterialGrid3D>>>(
0346 std::move(matMap), matBin.second);
0347 } else {
0348 throw std::domain_error("No grid 3D was found");
0349 }
0350 } else {
0351 throw std::invalid_argument(
0352 "Incorrect bin dimension, only 0, 2 and 3 are accepted");
0353 }
0354 }
0355 }
0356
0357 Result<void> VolumeMaterialMapper::mapMaterialTrack(
0358 State& mState, RecordedMaterialTrack& mTrack) const {
0359 using VectorHelpers::makeVector4;
0360
0361
0362 BoundTrackParameters start = BoundTrackParameters::createCurvilinear(
0363 makeVector4(mTrack.first.first, 0), mTrack.first.second,
0364 1 / mTrack.first.second.norm(), std::nullopt,
0365 ParticleHypothesis::geantino());
0366
0367
0368 using BoundSurfaceCollector = SurfaceCollector<BoundSurfaceSelector>;
0369 using MaterialVolumeCollector = VolumeCollector<MaterialVolumeSelector>;
0370 using ActionList = ActorList<BoundSurfaceCollector, MaterialVolumeCollector,
0371 EndOfWorldReached>;
0372
0373 StraightLinePropagator::Options<ActionList> options(mState.geoContext,
0374 mState.magFieldContext);
0375
0376
0377 const auto& result = m_propagator.propagate(start, options);
0378 if (!result.ok()) {
0379 ACTS_DEBUG("Encountered a propagator error for initial parameters:");
0380 ACTS_DEBUG(" - Position: " << mTrack.first.first.transpose());
0381 ACTS_DEBUG(" - Momentum: " << mTrack.first.second.transpose());
0382 return result.error();
0383 }
0384
0385 auto mcResult = result.value().get<BoundSurfaceCollector::result_type>();
0386 auto mvcResult = result.value().get<MaterialVolumeCollector::result_type>();
0387
0388 auto mappingSurfaces = mcResult.collected;
0389 auto mappingVolumes = mvcResult.collected;
0390
0391
0392 auto& rMaterial = mTrack.second.materialInteractions;
0393 ACTS_VERBOSE("Retrieved " << rMaterial.size()
0394 << " recorded material steps to map.");
0395
0396
0397 ACTS_VERBOSE("Found " << mappingVolumes.size()
0398 << " mapping volumes for this track.");
0399 ACTS_VERBOSE("Mapping volumes are :");
0400 for (auto& mVolumes : mappingVolumes) {
0401 ACTS_VERBOSE(" - Volume : " << mVolumes.volume->geometryId()
0402 << " at position = (" << mVolumes.position.x()
0403 << ", " << mVolumes.position.y() << ", "
0404 << mVolumes.position.z() << ")");
0405 }
0406
0407
0408 auto rmIter = rMaterial.begin();
0409 auto sfIter = mappingSurfaces.begin();
0410 auto volIter = mappingVolumes.begin();
0411
0412
0413 GeometryIdentifier lastID = GeometryIdentifier();
0414 GeometryIdentifier currentID = GeometryIdentifier();
0415 auto currentBinning = mState.materialBin.end();
0416
0417
0418 Vector3 lastPositionEnd = {0, 0, 0};
0419 Vector3 direction = {0, 0, 0};
0420
0421 if (volIter != mappingVolumes.end()) {
0422 lastPositionEnd = volIter->position;
0423 }
0424
0425
0426
0427 while (rmIter != rMaterial.end() && volIter != mappingVolumes.end()) {
0428 if (volIter != mappingVolumes.end() &&
0429 !volIter->volume->inside(mState.geoContext, rmIter->position)) {
0430
0431
0432
0433 double distVol = (volIter->position - mTrack.first.first).norm();
0434 double distMat = (rmIter->position - mTrack.first.first).norm();
0435 if (distMat - distVol > s_epsilon) {
0436
0437 ++volIter;
0438 continue;
0439 }
0440 }
0441 if (volIter != mappingVolumes.end() &&
0442 volIter->volume->inside(mState.geoContext, rmIter->position,
0443 s_epsilon)) {
0444 currentID = volIter->volume->geometryId();
0445 direction = rmIter->direction;
0446 if (!(currentID == lastID)) {
0447
0448 lastID = currentID;
0449 lastPositionEnd = volIter->position;
0450 currentBinning = mState.materialBin.find(currentID);
0451 }
0452
0453
0454 if (currentBinning != mState.materialBin.end() &&
0455 rmIter->materialSlab.thickness() > 0) {
0456
0457 float vacuumThickness = (rmIter->position - lastPositionEnd).norm();
0458 if (vacuumThickness > s_epsilon) {
0459 auto properties = MaterialSlab::Vacuum(vacuumThickness);
0460
0461 createExtraHits(mState, *currentBinning, properties, lastPositionEnd,
0462 direction);
0463 }
0464
0465
0466 direction =
0467 direction * (rmIter->materialSlab.thickness() / direction.norm());
0468 lastPositionEnd = rmIter->position + direction;
0469
0470 createExtraHits(mState, *currentBinning, rmIter->materialSlab,
0471 rmIter->position, direction);
0472 }
0473
0474
0475
0476 if ((rmIter + 1) == rMaterial.end() ||
0477 !volIter->volume->inside(mState.geoContext, (rmIter + 1)->position,
0478 s_epsilon)) {
0479
0480 while (sfIter != mappingSurfaces.end()) {
0481 if (sfIter->surface->geometryId().volume() == lastID.volume() ||
0482 ((volIter + 1) != mappingVolumes.end() &&
0483 sfIter->surface->geometryId().volume() ==
0484 (volIter + 1)->volume->geometryId().volume())) {
0485 double distVol = (volIter->position - mTrack.first.first).norm();
0486 double distSur = (sfIter->position - mTrack.first.first).norm();
0487 if (distSur - distVol > s_epsilon) {
0488 float vacuumThickness =
0489 (sfIter->position - lastPositionEnd).norm();
0490
0491
0492 if (vacuumThickness > s_epsilon) {
0493 auto properties = MaterialSlab::Vacuum(vacuumThickness);
0494 createExtraHits(mState, *currentBinning, properties,
0495 lastPositionEnd, direction);
0496 lastPositionEnd = sfIter->position;
0497 }
0498 break;
0499 }
0500 }
0501 sfIter++;
0502 }
0503 }
0504 rmIter->volume = InteractionVolume(volIter->volume);
0505 rmIter->intersectionID = currentID;
0506 rmIter->intersection = rmIter->position;
0507 }
0508 ++rmIter;
0509 }
0510
0511 return Result<void>::success();
0512 }
0513
0514 }