File indexing completed on 2026-05-16 07:37:08
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <boost/test/unit_test.hpp>
0010
0011 #include "Acts/Definitions/Algebra.hpp"
0012 #include "Acts/Geometry/GeometryContext.hpp"
0013 #include "Acts/Geometry/GeometryIdentifier.hpp"
0014 #include "Acts/Material/AccumulatedMaterialSlab.hpp"
0015 #include "Acts/Material/HomogeneousSurfaceMaterial.hpp"
0016 #include "Acts/Material/MaterialInteraction.hpp"
0017 #include "Acts/Material/MaterialMapper.hpp"
0018 #include "Acts/Material/MaterialSlab.hpp"
0019 #include "Acts/Material/interface/IAssignmentFinder.hpp"
0020 #include "Acts/Material/interface/ISurfaceMaterialAccumulator.hpp"
0021 #include "Acts/Surfaces/CylinderSurface.hpp"
0022 #include "Acts/Utilities/Enumerate.hpp"
0023 #include "Acts/Utilities/Intersection.hpp"
0024
0025 using namespace Acts;
0026
0027 namespace ActsTests {
0028
0029 auto tContext = GeometryContext::dangerouslyDefaultConstruct();
0030
0031
0032
0033 class IntersectSurfacesFinder : public IAssignmentFinder {
0034 public:
0035 std::vector<const Surface*> surfaces;
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046 std::pair<std::vector<IAssignmentFinder::SurfaceAssignment>,
0047 std::vector<IAssignmentFinder::VolumeAssignment>>
0048 assignmentCandidates(const GeometryContext& gctx,
0049 const MagneticFieldContext& ,
0050 const Vector3& position,
0051 const Vector3& direction) const override {
0052 std::vector<IAssignmentFinder::SurfaceAssignment> surfaceAssignments;
0053 std::vector<IAssignmentFinder::VolumeAssignment> volumeAssignments;
0054
0055 for (auto& surface : surfaces) {
0056
0057 MultiIntersection3D multiIntersection = surface->intersect(
0058 gctx, position, direction, BoundaryTolerance::None());
0059
0060 if (multiIntersection.size() == 1u &&
0061 multiIntersection.at(0).status() >= IntersectionStatus::reachable &&
0062 multiIntersection.at(0).pathLength() >= 0.0) {
0063 surfaceAssignments.push_back(
0064 {surface, multiIntersection.at(0).position(), direction});
0065 continue;
0066 }
0067 if (multiIntersection.size() > 1u) {
0068
0069 Intersection3D closestForward = multiIntersection.closestForward();
0070 if (closestForward.status() >= IntersectionStatus::reachable &&
0071 closestForward.pathLength() > 0.0) {
0072 surfaceAssignments.push_back(
0073 {surface, closestForward.position(), direction});
0074 continue;
0075 }
0076 }
0077 }
0078 return {surfaceAssignments, volumeAssignments};
0079 }
0080 };
0081
0082
0083 class MaterialBlender : public ISurfaceMaterialAccumulator {
0084 public:
0085 explicit MaterialBlender(
0086 const std::vector<std::shared_ptr<Surface>>& surfaces = {})
0087 : m_surfaces(surfaces) {}
0088
0089
0090
0091 class State final : public ISurfaceMaterialAccumulator::State {
0092 public:
0093 std::map<const Surface*, AccumulatedMaterialSlab> accumulatedMaterial;
0094 };
0095
0096
0097 std::unique_ptr<ISurfaceMaterialAccumulator::State> createState(
0098 const GeometryContext& ) const override {
0099 auto state = std::make_unique<State>();
0100 for (auto& surface : m_surfaces) {
0101 state->accumulatedMaterial[surface.get()] = AccumulatedMaterialSlab();
0102 }
0103 return state;
0104 };
0105
0106
0107
0108
0109
0110
0111
0112
0113 void accumulate(ISurfaceMaterialAccumulator::State& state,
0114 const GeometryContext& ,
0115 const std::vector<MaterialInteraction>& interactions,
0116 const std::vector<IAssignmentFinder::SurfaceAssignment>&
0117 ) const override {
0118 auto cState = static_cast<State*>(&state);
0119 for (const auto& mi : interactions) {
0120
0121 const Surface* surface = mi.surface;
0122
0123 auto accMaterial = cState->accumulatedMaterial.find(surface);
0124 if (accMaterial == cState->accumulatedMaterial.end()) {
0125 throw std::invalid_argument(
0126 "Surface material is not found, inconsistent configuration.");
0127 }
0128
0129 accMaterial->second.accumulate(mi.materialSlab);
0130 }
0131
0132 for (auto& [surface, accumulatedMaterial] : cState->accumulatedMaterial) {
0133 accumulatedMaterial.trackAverage();
0134 }
0135 };
0136
0137
0138
0139
0140
0141
0142 std::map<GeometryIdentifier, std::shared_ptr<const ISurfaceMaterial>>
0143 finalizeMaterial(ISurfaceMaterialAccumulator::State& state,
0144 const GeometryContext& ) const override {
0145 auto cState = static_cast<State*>(&state);
0146
0147 std::map<GeometryIdentifier, std::shared_ptr<const ISurfaceMaterial>>
0148 materialMaps;
0149 for (auto& [surface, accumulatedMaterial] : cState->accumulatedMaterial) {
0150 materialMaps[surface->geometryId()] =
0151 std::make_shared<HomogeneousSurfaceMaterial>(
0152 accumulatedMaterial.totalAverage().first);
0153 }
0154 return materialMaps;
0155 }
0156
0157 private:
0158 std::vector<std::shared_ptr<Surface>> m_surfaces;
0159 };
0160
0161 BOOST_AUTO_TEST_SUITE(MaterialSuite)
0162
0163
0164
0165
0166
0167
0168 BOOST_AUTO_TEST_CASE(MaterialMapperFlowTest) {
0169
0170 std::vector<std::shared_ptr<Surface>> surfaces = {
0171 Surface::makeShared<CylinderSurface>(Transform3::Identity(), 20.0, 100.0),
0172 Surface::makeShared<CylinderSurface>(Transform3::Identity(), 30.0, 100.0),
0173 Surface::makeShared<CylinderSurface>(Transform3::Identity(), 50.0,
0174 100.0)};
0175
0176 for (auto [is, surface] : enumerate(surfaces)) {
0177 surface->assignGeometryId(GeometryIdentifier().withSensitive(is + 1));
0178 }
0179
0180
0181 auto assigner = std::make_shared<IntersectSurfacesFinder>();
0182 assigner->surfaces = {surfaces[0].get(), surfaces[1].get(),
0183 surfaces[2].get()};
0184
0185
0186 auto accumulator = std::make_shared<MaterialBlender>(surfaces);
0187
0188
0189 MaterialMapper::Config mmConfig;
0190 mmConfig.assignmentFinder = assigner;
0191 mmConfig.surfaceMaterialAccumulator = accumulator;
0192
0193 MaterialMapper mapper(mmConfig);
0194
0195 auto state = mapper.createState(tContext);
0196 BOOST_CHECK(state.get() != nullptr);
0197 BOOST_CHECK(state->surfaceMaterialAccumulatorState.get() != nullptr);
0198
0199 std::vector<RecordedMaterialTrack> mappedTracks;
0200 std::vector<RecordedMaterialTrack> unmappedTracks;
0201
0202
0203 Vector3 position(0., 0., 0.);
0204 for (unsigned int it = 0; it < 11; ++it) {
0205 Vector3 direction =
0206 Vector3(0.9 + it * 0.02, 1.1 - it * 0.02, 0.).normalized();
0207 RecordedMaterialTrack mTrack{{position, direction}, {}};
0208 for (unsigned int im = 0; im < 60; ++im) {
0209 MaterialInteraction mi;
0210 mi.materialSlab = MaterialSlab(
0211 Material::fromMassDensity(it + 1, it + 1, it + 1, it + 1, it + 1),
0212 0.1);
0213 mi.position = position + (im + 1) * direction;
0214 mi.direction = direction;
0215 mTrack.second.materialInteractions.push_back(mi);
0216 }
0217 auto [mapped, unmapped] = mapper.mapMaterial(*state, tContext, {}, mTrack);
0218 mappedTracks.push_back(mapped);
0219 unmappedTracks.push_back(unmapped);
0220 }
0221
0222
0223 auto [surfaceMaps, volumeMaps] = mapper.finalizeMaps(*state, tContext);
0224
0225 BOOST_CHECK(surfaceMaps.size() == 3);
0226 BOOST_CHECK(volumeMaps.empty());
0227 }
0228
0229 BOOST_AUTO_TEST_CASE(MaterialMapperInvalidTest) {
0230
0231 auto assigner = std::make_shared<IntersectSurfacesFinder>();
0232
0233
0234 auto accumulator = std::make_shared<MaterialBlender>();
0235
0236
0237 MaterialMapper::Config mmConfigInvalid;
0238 mmConfigInvalid.assignmentFinder = nullptr;
0239 mmConfigInvalid.surfaceMaterialAccumulator = accumulator;
0240
0241 BOOST_CHECK_THROW(auto mapperIA = MaterialMapper(mmConfigInvalid),
0242 std::invalid_argument);
0243
0244
0245
0246 mmConfigInvalid.assignmentFinder = assigner;
0247 mmConfigInvalid.surfaceMaterialAccumulator = nullptr;
0248
0249 BOOST_CHECK_THROW(auto mapperIS = MaterialMapper(mmConfigInvalid),
0250 std::invalid_argument);
0251 }
0252
0253 BOOST_AUTO_TEST_SUITE_END()
0254
0255 }