File indexing completed on 2026-09-12 08:22:12
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <boost/test/data/test_case.hpp>
0010 #include <boost/test/tools/old/interface.hpp>
0011 #include <boost/test/unit_test.hpp>
0012 #include <boost/test/unit_test_suite.hpp>
0013
0014 #include "Acts/Definitions/Algebra.hpp"
0015 #include "Acts/Definitions/Units.hpp"
0016 #include "Acts/Geometry/Blueprint.hpp"
0017 #include "Acts/Geometry/ContainerBlueprintNode.hpp"
0018 #include "Acts/Geometry/CylinderVolumeBounds.hpp"
0019 #include "Acts/Geometry/GeometryContext.hpp"
0020 #include "Acts/Geometry/LayerBlueprintNode.hpp"
0021 #include "Acts/Geometry/MaterialDesignatorBlueprintNode.hpp"
0022 #include "Acts/Geometry/Portal.hpp"
0023 #include "Acts/Geometry/PortalDesignatorBlueprintNode.hpp"
0024 #include "Acts/Geometry/TrackingGeometry.hpp"
0025 #include "Acts/Geometry/TrackingVolume.hpp"
0026 #include "Acts/Geometry/VolumeAttachmentStrategy.hpp"
0027 #include "Acts/Geometry/VolumeResizeStrategy.hpp"
0028 #include "Acts/Material/MergedMaterialMarker.hpp"
0029 #include "Acts/Navigation/INavigationPolicy.hpp"
0030 #include "Acts/Navigation/NavigationStream.hpp"
0031 #include "Acts/Surfaces/RectangleBounds.hpp"
0032 #include "Acts/Surfaces/Surface.hpp"
0033 #include "Acts/Utilities/AxisSpec.hpp"
0034 #include "Acts/Utilities/Logger.hpp"
0035 #include "Acts/Visualization/GeometryView3D.hpp"
0036 #include "Acts/Visualization/ObjVisualization3D.hpp"
0037 #include "ActsTests/CommonHelpers/DetectorElementStub.hpp"
0038
0039 #include <fstream>
0040 #include <random>
0041 #include <vector>
0042
0043 using namespace Acts;
0044 using namespace UnitLiterals;
0045
0046 using Acts::Blueprint;
0047 using Acts::LayerBlueprintNode;
0048 using Acts::MaterialDesignatorBlueprintNode;
0049
0050 namespace ActsTests {
0051
0052 auto logger = getDefaultLogger("UnitTests", Logging::DEBUG);
0053
0054 auto gctx = GeometryContext::dangerouslyDefaultConstruct();
0055
0056 inline std::vector<std::shared_ptr<Surface>> makeFanLayer(
0057 const Transform3& base,
0058 std::vector<std::unique_ptr<SurfacePlacementBase>>& elements,
0059 double r = 300_mm, std::size_t nSensors = 8, double thickness = 0) {
0060 auto recBounds = std::make_shared<RectangleBounds>(40_mm, 60_mm);
0061
0062 double deltaPhi = 2 * std::numbers::pi / nSensors;
0063 std::vector<std::shared_ptr<Surface>> surfaces;
0064 for (std::size_t i = 0; i < nSensors; i++) {
0065
0066
0067 Transform3 trf = base * AngleAxis3{deltaPhi * i, Vector3::UnitZ()} *
0068 Translation3(Vector3::UnitX() * r);
0069
0070 if (i % 2 == 0) {
0071 trf = trf * Translation3{Vector3::UnitZ() * 5_mm};
0072 }
0073
0074 auto& element = elements.emplace_back(
0075 std::make_unique<DetectorElementStub>(trf, recBounds, thickness));
0076
0077 element->surface().assignSurfacePlacement(*element);
0078
0079 surfaces.push_back(element->surface().getSharedPtr());
0080 }
0081 return surfaces;
0082 }
0083
0084 inline std::vector<std::shared_ptr<Surface>> makeBarrelLayer(
0085 const Transform3& base,
0086 std::vector<std::unique_ptr<SurfacePlacementBase>>& elements,
0087 double r = 300_mm, std::size_t nStaves = 10, int nSensorsPerStave = 8,
0088 double thickness = 0, double hlPhi = 40_mm, double hlZ = 60_mm) {
0089 auto recBounds = std::make_shared<RectangleBounds>(hlPhi, hlZ);
0090
0091 double deltaPhi = 2 * std::numbers::pi / nStaves;
0092 std::vector<std::shared_ptr<Surface>> surfaces;
0093
0094 for (std::size_t istave = 0; istave < nStaves; istave++) {
0095 for (int isensor = -nSensorsPerStave; isensor <= nSensorsPerStave;
0096 isensor++) {
0097 double z = isensor * (2 * hlZ + 5_mm);
0098
0099 Transform3 trf = base * Translation3(Vector3::UnitZ() * z) *
0100 AngleAxis3{deltaPhi * istave, Vector3::UnitZ()} *
0101 Translation3(Vector3::UnitX() * r) *
0102 AngleAxis3{10_degree, Vector3::UnitZ()} *
0103 AngleAxis3{90_degree, Vector3::UnitY()} *
0104 AngleAxis3{90_degree, Vector3::UnitZ()};
0105 auto& element = elements.emplace_back(
0106 std::make_unique<DetectorElementStub>(trf, recBounds, thickness));
0107 element->surface().assignSurfacePlacement(*element);
0108 surfaces.push_back(element->surface().getSharedPtr());
0109 }
0110 }
0111
0112 return surfaces;
0113 }
0114
0115 }
0116
0117 using namespace ActsTests;
0118
0119 BOOST_AUTO_TEST_SUITE(GeometrySuite);
0120
0121 void pseudoNavigation(const TrackingGeometry& trackingGeometry,
0122 Vector3 position, const Vector3& direction,
0123 std::ostream& csv, std::size_t run,
0124 std::size_t substepsPerCm, const Logger& logger) {
0125 ACTS_VERBOSE("start navigation " << run);
0126 ACTS_VERBOSE("dir: " << direction.transpose());
0127 ACTS_VERBOSE(direction.norm());
0128
0129 std::mt19937 rng{static_cast<unsigned int>(run)};
0130 std::uniform_real_distribution<> dist{0.01, 0.99};
0131
0132 const auto* volume =
0133 trackingGeometry.resolveLowestTrackingVolume(gctx, position).value();
0134 BOOST_REQUIRE_NE(volume, nullptr);
0135 ACTS_VERBOSE(volume->volumeName());
0136
0137 NavigationStream main;
0138 const TrackingVolume* currentVolume = volume;
0139
0140 csv << run << "," << position[0] << "," << position[1] << "," << position[2];
0141 csv << "," << volume->geometryId().volume();
0142 csv << "," << volume->geometryId().boundary();
0143 csv << "," << volume->geometryId().sensitive();
0144 csv << std::endl;
0145
0146 ACTS_VERBOSE("start pseudo navigation");
0147
0148 for (std::size_t i = 0; i < 100; i++) {
0149 main = NavigationStream{};
0150 AppendOnlyNavigationStream stream{main};
0151
0152 NavigationArguments navArgs{.position = position, .direction = direction};
0153 NavigationPolicyStateManager stateManager;
0154 currentVolume->navigationPolicy()->createState(gctx, navArgs, stateManager,
0155 logger);
0156 auto policyState = stateManager.currentState();
0157 currentVolume->initializeNavigationCandidates(gctx, navArgs, policyState,
0158 stream, logger);
0159
0160 ACTS_VERBOSE(main.candidates().size() << " candidates");
0161
0162 for (const auto& candidate : main.candidates()) {
0163 ACTS_VERBOSE(" -> " << candidate.surface().geometryId());
0164 ACTS_VERBOSE(" " << candidate.surface().toStream(gctx));
0165 }
0166
0167 ACTS_VERBOSE("initializing candidates");
0168 main.initialize(gctx, {position, direction}, BoundaryTolerance::None());
0169
0170 ACTS_VERBOSE(main.candidates().size() << " candidates remaining");
0171
0172 for (const auto& candidate : main.candidates()) {
0173 ACTS_VERBOSE(" -> " << candidate.surface().geometryId());
0174 ACTS_VERBOSE(" " << candidate.surface().toStream(gctx));
0175 }
0176
0177 if (main.currentCandidate().surface().isOnSurface(gctx, position,
0178 direction)) {
0179 ACTS_VERBOSE("Already on surface at initialization, skipping candidate");
0180
0181 auto id = main.currentCandidate().surface().geometryId();
0182 csv << run << "," << position[0] << "," << position[1] << ","
0183 << position[2];
0184 csv << "," << id.volume();
0185 csv << "," << id.boundary();
0186 csv << "," << id.sensitive();
0187 csv << std::endl;
0188 if (!main.switchToNextCandidate()) {
0189 ACTS_WARNING("candidates exhausted unexpectedly");
0190 break;
0191 }
0192 }
0193
0194 auto writeIntersection = [&](const Vector3& pos, const Surface& surface) {
0195 csv << run << "," << pos[0] << "," << pos[1] << "," << pos[2];
0196 csv << "," << surface.geometryId().volume();
0197 csv << "," << surface.geometryId().boundary();
0198 csv << "," << surface.geometryId().sensitive();
0199 csv << std::endl;
0200 };
0201
0202 bool terminated = false;
0203 while (main.remainingCandidates() > 0) {
0204 const auto& candidate = main.currentCandidate();
0205
0206 ACTS_VERBOSE(candidate.position().transpose());
0207
0208 ACTS_VERBOSE("moving to position: " << position.transpose() << " (r="
0209 << VectorHelpers::perp(position)
0210 << ")");
0211
0212 Vector3 delta = candidate.position() - position;
0213
0214 std::size_t substeps =
0215 std::max(1l, std::lround(delta.norm() / 10_cm * substepsPerCm));
0216
0217 for (std::size_t j = 0; j < substeps; j++) {
0218
0219 Vector3 subpos = position + dist(rng) * delta;
0220 csv << run << "," << subpos[0] << "," << subpos[1] << "," << subpos[2];
0221 csv << "," << currentVolume->geometryId().volume();
0222 csv << ",0,0";
0223 csv << std::endl;
0224 }
0225
0226 position = candidate.position();
0227 ACTS_VERBOSE(" -> "
0228 << position.transpose()
0229 << " (r=" << VectorHelpers::perp(position) << ")");
0230
0231 writeIntersection(position, candidate.surface());
0232
0233 if (candidate.isPortalTarget()) {
0234 ACTS_VERBOSE("On portal: " << candidate.surface().toStream(gctx));
0235 currentVolume =
0236 candidate.portal().resolveVolume(gctx, position, direction).value();
0237
0238 if (currentVolume == nullptr) {
0239 ACTS_VERBOSE("switched to nullptr -> we're done");
0240 terminated = true;
0241 }
0242 break;
0243
0244 } else {
0245 ACTS_VERBOSE("Not on portal");
0246 }
0247
0248 main.switchToNextCandidate();
0249 }
0250
0251 if (terminated) {
0252 ACTS_VERBOSE("Terminate pseudo navigation");
0253 break;
0254 }
0255
0256 ACTS_VERBOSE("switched to " << currentVolume->volumeName());
0257
0258 ACTS_VERBOSE("-----");
0259 }
0260 }
0261
0262 BOOST_AUTO_TEST_CASE(NodeApiTestContainers) {
0263
0264 Transform3 base{Transform3::Identity()};
0265
0266 std::vector<std::unique_ptr<SurfacePlacementBase>> detectorElements;
0267 auto makeFan = [&](const Transform3& layerBase, auto&&..., double r,
0268 std::size_t nSensors, double thickness) {
0269 return makeFanLayer(layerBase, detectorElements, r, nSensors, thickness);
0270 };
0271
0272 Blueprint::Config cfg;
0273 cfg.envelope[AxisDirection::AxisZ] = {20_mm, 20_mm};
0274 cfg.envelope[AxisDirection::AxisR] = {0_mm, 20_mm};
0275 auto root = std::make_unique<Blueprint>(cfg);
0276
0277 root->addMaterial("GlobalMaterial", [&](MaterialDesignatorBlueprintNode&
0278 mat) {
0279 using enum AxisDirection;
0280 using enum AxisBoundaryType;
0281 using enum CylinderVolumeBounds::Face;
0282
0283
0284 mat.configureFace(OuterCylinder,
0285 AxisSpec::DeferredEquidistant(20, AxisRPhi),
0286 AxisSpec::DeferredEquidistant(20, AxisZ));
0287 mat.configureFace(NegativeDisc, AxisSpec::DeferredEquidistant(15, AxisR),
0288 AxisSpec::DeferredEquidistant(25, AxisPhi));
0289 mat.configureFace(PositiveDisc, AxisSpec::DeferredEquidistant(15, AxisR),
0290 AxisSpec::DeferredEquidistant(25, AxisPhi));
0291
0292 mat.addCylinderContainer("Detector", AxisDirection::AxisR, [&](auto& det) {
0293 det.addCylinderContainer("Pixel", AxisDirection::AxisZ, [&](auto& cyl) {
0294 cyl.setAttachmentStrategy(VolumeAttachmentStrategy::Gap)
0295 .setResizeStrategy(VolumeResizeStrategy::Gap);
0296
0297 cyl.addCylinderContainer(
0298 "PixelNegativeEndcap", AxisDirection::AxisZ, [&](auto& ec) {
0299 ec.setAttachmentStrategy(VolumeAttachmentStrategy::Gap);
0300
0301 auto makeLayer = [&](const Transform3& trf, auto& layer) {
0302 std::vector<std::shared_ptr<Surface>> surfaces;
0303 auto layerSurfaces = makeFan(trf, 300_mm, 10, 2_mm);
0304 std::copy(layerSurfaces.begin(), layerSurfaces.end(),
0305 std::back_inserter(surfaces));
0306 layerSurfaces = makeFan(trf, 500_mm, 16, 2_mm);
0307 std::copy(layerSurfaces.begin(), layerSurfaces.end(),
0308 std::back_inserter(surfaces));
0309
0310 layer.setSurfaces(surfaces)
0311 .setLayerType(LayerBlueprintNode::LayerType::Disc)
0312 .setEnvelope(ExtentEnvelope{{
0313 .z = {5_mm, 5_mm},
0314 .r = {10_mm, 20_mm},
0315 }})
0316 .setTransform(base);
0317 };
0318
0319 ec.addLayer("PixelNeg1", [&](auto& layer) {
0320 makeLayer(base * Translation3{Vector3{0, 0, -700_mm}}, layer);
0321 });
0322
0323 ec.addLayer("PixelNeg2", [&](auto& layer) {
0324 makeLayer(base * Translation3{Vector3{0, 0, -500_mm}}, layer);
0325 });
0326 });
0327
0328 cyl.addCylinderContainer(
0329 "PixelBarrel", AxisDirection::AxisR, [&](auto& brl) {
0330 brl.setAttachmentStrategy(VolumeAttachmentStrategy::Gap)
0331 .setResizeStrategy(VolumeResizeStrategy::Gap);
0332
0333 auto makeLayer = [&](const std::string& name, double r,
0334 std::size_t nStaves, int nSensorsPerStave) {
0335 brl.addLayer(name, [&](auto& layer) {
0336 std::vector<std::shared_ptr<Surface>> surfaces =
0337 makeBarrelLayer(base, detectorElements, r, nStaves,
0338 nSensorsPerStave, 2.5_mm, 10_mm, 20_mm);
0339
0340 layer.setSurfaces(surfaces)
0341 .setLayerType(LayerBlueprintNode::LayerType::Cylinder)
0342 .setEnvelope(ExtentEnvelope{{
0343 .z = {5_mm, 5_mm},
0344 .r = {1_mm, 1_mm},
0345 }})
0346 .setTransform(base);
0347 });
0348 };
0349
0350 makeLayer("PixelLayer0", 30_mm, 18, 5);
0351 makeLayer("PixelLayer1", 90_mm, 30, 6);
0352
0353 brl.addStaticVolume(base,
0354 std::make_shared<CylinderVolumeBounds>(
0355 100_mm, 110_mm, 250_mm),
0356 "PixelSupport");
0357
0358 makeLayer("PixelLayer2", 150_mm, 40, 7);
0359 makeLayer("PixelLayer3", 250_mm, 70, 8);
0360 });
0361
0362 auto& ec =
0363 cyl.addCylinderContainer("PixelPosWrapper", AxisDirection::AxisR);
0364 ec.setResizeStrategy(VolumeResizeStrategy::Gap);
0365 ec.addStaticVolume(std::make_unique<TrackingVolume>(
0366 base * Translation3{Vector3{0, 0, 600_mm}},
0367 std::make_shared<CylinderVolumeBounds>(150_mm, 390_mm, 200_mm),
0368 "PixelPositiveEndcap"));
0369 });
0370
0371 det.addStaticVolume(
0372 base, std::make_shared<CylinderVolumeBounds>(0_mm, 23_mm, 1000_mm),
0373 "BeamPipe");
0374 });
0375 });
0376
0377 std::ofstream dot{"api_test_container.dot"};
0378 root->graphviz(dot);
0379
0380 auto trackingGeometry = root->construct({}, gctx, *logger);
0381
0382 BOOST_REQUIRE(trackingGeometry);
0383 BOOST_CHECK(trackingGeometry->geometryVersion() ==
0384 TrackingGeometry::GeometryVersion::Gen3);
0385
0386 trackingGeometry->visitVolumes([&](const TrackingVolume* volume) {
0387 std::cout << volume->volumeName() << std::endl;
0388 std::cout << " -> id: " << volume->geometryId() << std::endl;
0389 std::cout << " -> " << volume->portals().size() << " portals" << std::endl;
0390 });
0391
0392 ObjVisualization3D vis;
0393
0394 trackingGeometry->visualize(vis, gctx);
0395
0396 vis.write("api_test_container.obj");
0397
0398 Vector3 position = Vector3::Zero();
0399 std::ofstream csv{"api_test_container.csv"};
0400 csv << "x,y,z,volume,boundary,sensitive" << std::endl;
0401
0402 std::mt19937 rnd{42};
0403
0404 std::uniform_real_distribution<> dist{-1, 1};
0405
0406 double etaWidth = 3;
0407 double thetaMin = 2 * std::atan(std::exp(-etaWidth));
0408 double thetaMax = 2 * std::atan(std::exp(etaWidth));
0409 std::uniform_real_distribution<> thetaDist{thetaMin, thetaMax};
0410
0411 using namespace UnitLiterals;
0412
0413 for (std::size_t i = 0; i < 5000; i++) {
0414 double theta = thetaDist(rnd);
0415 double phi = 2 * std::numbers::pi * dist(rnd);
0416
0417 Vector3 direction;
0418 direction[0] = std::sin(theta) * std::cos(phi);
0419 direction[1] = std::sin(theta) * std::sin(phi);
0420 direction[2] = std::cos(theta);
0421
0422 pseudoNavigation(*trackingGeometry, position, direction, csv, i, 2,
0423 *logger->clone(std::nullopt, Logging::DEBUG));
0424 }
0425 }
0426
0427 BOOST_AUTO_TEST_CASE(NodeApiTestCuboid) {
0428 Transform3 base{Transform3::Identity()};
0429
0430 Blueprint::Config cfg;
0431 cfg.envelope[AxisDirection::AxisZ] = {20_mm, 20_mm};
0432 cfg.envelope[AxisDirection::AxisR] = {0_mm, 20_mm};
0433 auto root = std::make_unique<Blueprint>(cfg);
0434
0435 root->addMaterial("GlobalMaterial", [&](MaterialDesignatorBlueprintNode&
0436 mat) {
0437 using enum AxisDirection;
0438 using enum AxisBoundaryType;
0439 using enum CuboidVolumeBounds::Face;
0440
0441
0442 mat.configureFace(NegativeXFace, AxisSpec::DeferredEquidistant(20, AxisX),
0443 AxisSpec::DeferredEquidistant(20, AxisY));
0444 mat.configureFace(PositiveXFace, AxisSpec::DeferredEquidistant(20, AxisX),
0445 AxisSpec::DeferredEquidistant(20, AxisY));
0446 mat.configureFace(NegativeYFace, AxisSpec::DeferredEquidistant(15, AxisX),
0447 AxisSpec::DeferredEquidistant(25, AxisY));
0448 mat.configureFace(PositiveYFace, AxisSpec::DeferredEquidistant(15, AxisX),
0449 AxisSpec::DeferredEquidistant(25, AxisY));
0450 mat.configureFace(NegativeZFace, AxisSpec::DeferredEquidistant(15, AxisX),
0451 AxisSpec::DeferredEquidistant(25, AxisY));
0452 mat.configureFace(PositiveZFace, AxisSpec::DeferredEquidistant(15, AxisX),
0453 AxisSpec::DeferredEquidistant(25, AxisY));
0454
0455 mat.addStaticVolume(
0456 base, std::make_shared<CuboidVolumeBounds>(100_mm, 100_mm, 100_mm),
0457 "TestVolume");
0458 });
0459
0460 auto trackingGeometry = root->construct({}, gctx, *logger);
0461 BOOST_REQUIRE(trackingGeometry);
0462 BOOST_CHECK(trackingGeometry->geometryVersion() ==
0463 TrackingGeometry::GeometryVersion::Gen3);
0464 }
0465
0466
0467
0468
0469
0470
0471
0472
0473
0474
0475 BOOST_AUTO_TEST_CASE(MaterialOnMergedPortalThrows) {
0476 Transform3 base{Transform3::Identity()};
0477
0478 Blueprint::Config cfg;
0479 cfg.envelope[AxisDirection::AxisZ] = {20_mm, 20_mm};
0480 cfg.envelope[AxisDirection::AxisR] = {0_mm, 20_mm};
0481 auto root = std::make_unique<Blueprint>(cfg);
0482
0483 root->addCylinderContainer("Stack", AxisDirection::AxisZ, [&](auto& stack) {
0484 using enum AxisDirection;
0485 using enum AxisBoundaryType;
0486 using enum CylinderVolumeBounds::Face;
0487
0488
0489
0490 stack.addMaterial("Material", [&](auto& mat) {
0491 mat.configureFace(OuterCylinder,
0492 AxisSpec::DeferredEquidistant(20, AxisRPhi),
0493 AxisSpec::DeferredEquidistant(20, AxisZ));
0494 mat.addStaticVolume(
0495 base * Translation3{Vector3{0, 0, -200_mm}},
0496 std::make_shared<CylinderVolumeBounds>(0_mm, 100_mm, 100_mm),
0497 "VolumeA");
0498 });
0499
0500
0501 stack.addStaticVolume(
0502 base * Translation3{Vector3{0, 0, 200_mm}},
0503 std::make_shared<CylinderVolumeBounds>(0_mm, 100_mm, 100_mm),
0504 "VolumeB");
0505 });
0506
0507
0508
0509 bool thrown = false;
0510 {
0511 Logging::ScopedFailureThreshold threshold{Logging::Level::FATAL};
0512 try {
0513 root->construct({}, gctx, *logger);
0514 } catch (const PortalMergingException& e) {
0515 thrown = true;
0516 std::string msg = e.what();
0517 BOOST_CHECK(msg.find("OuterCylinder") != std::string::npos);
0518 BOOST_CHECK(msg.find("VolumeA") != std::string::npos);
0519 BOOST_CHECK(msg.find("material") != std::string::npos);
0520 }
0521 }
0522 BOOST_CHECK(thrown);
0523 }
0524
0525 BOOST_AUTO_TEST_CASE(MaterialOnMergedPortalKeepGoing) {
0526
0527
0528
0529 Transform3 base{Transform3::Identity()};
0530
0531 Blueprint::Config cfg;
0532 cfg.envelope[AxisDirection::AxisZ] = {20_mm, 20_mm};
0533 cfg.envelope[AxisDirection::AxisR] = {0_mm, 20_mm};
0534 auto root = std::make_unique<Blueprint>(cfg);
0535
0536 root->addCylinderContainer("Stack", AxisDirection::AxisZ, [&](auto& stack) {
0537 using enum AxisDirection;
0538 using enum AxisBoundaryType;
0539 using enum CylinderVolumeBounds::Face;
0540
0541 stack.addMaterial("Material", [&](auto& mat) {
0542 mat.configureFace(OuterCylinder,
0543 AxisSpec::DeferredEquidistant(20, AxisRPhi),
0544 AxisSpec::DeferredEquidistant(20, AxisZ));
0545 mat.addStaticVolume(
0546 base * Translation3{Vector3{0, 0, -200_mm}},
0547 std::make_shared<CylinderVolumeBounds>(0_mm, 100_mm, 100_mm),
0548 "VolumeA");
0549 });
0550
0551 stack.addStaticVolume(
0552 base * Translation3{Vector3{0, 0, 200_mm}},
0553 std::make_shared<CylinderVolumeBounds>(0_mm, 100_mm, 100_mm),
0554 "VolumeB");
0555 });
0556
0557 BlueprintOptions options;
0558 options.keepGoingOnMaterialMergeFailure = true;
0559
0560 std::unique_ptr<const TrackingGeometry> trackingGeometry;
0561 {
0562 Logging::ScopedFailureThreshold threshold{Logging::Level::FATAL};
0563 BOOST_REQUIRE_NO_THROW(trackingGeometry =
0564 root->construct(options, gctx, *logger));
0565 }
0566 BOOST_REQUIRE(trackingGeometry != nullptr);
0567
0568 std::size_t markerCount = 0;
0569 trackingGeometry->visitSurfaces(
0570 [&](const Surface* surface) {
0571 if (surface != nullptr && surface->surfaceMaterial() != nullptr &&
0572 dynamic_cast<const MergedMaterialMarker*>(
0573 surface->surfaceMaterial()) != nullptr) {
0574 ++markerCount;
0575 }
0576 },
0577 false);
0578 BOOST_CHECK_GE(markerCount, 1u);
0579 }
0580
0581 BOOST_AUTO_TEST_CASE(MaterialOnMergedPortalKeepGoingSingleChildFalseWarning) {
0582
0583
0584
0585
0586 Transform3 base{Transform3::Identity()};
0587
0588 Blueprint::Config cfg;
0589 cfg.envelope[AxisDirection::AxisZ] = {20_mm, 20_mm};
0590 cfg.envelope[AxisDirection::AxisR] = {0_mm, 20_mm};
0591 auto root = std::make_unique<Blueprint>(cfg);
0592
0593 root->addCylinderContainer("Stack", AxisDirection::AxisZ, [&](auto& stack) {
0594 using enum AxisDirection;
0595 using enum AxisBoundaryType;
0596 using enum CylinderVolumeBounds::Face;
0597
0598 stack.addMaterial("Material", [&](auto& mat) {
0599 mat.configureFace(OuterCylinder,
0600 AxisSpec::DeferredEquidistant(20, AxisRPhi),
0601 AxisSpec::DeferredEquidistant(20, AxisZ));
0602 mat.addStaticVolume(
0603 base, std::make_shared<CylinderVolumeBounds>(0_mm, 100_mm, 100_mm),
0604 "VolumeA");
0605 });
0606 });
0607
0608
0609
0610
0611 BlueprintOptions options;
0612
0613 std::unique_ptr<const TrackingGeometry> trackingGeometry;
0614 BOOST_REQUIRE_NO_THROW(trackingGeometry =
0615 root->construct(options, gctx, *logger));
0616 BOOST_REQUIRE(trackingGeometry != nullptr);
0617
0618 std::size_t markerCount = 0;
0619 trackingGeometry->visitSurfaces(
0620 [&](const Surface* surface) {
0621 if (surface != nullptr && surface->surfaceMaterial() != nullptr &&
0622 dynamic_cast<const MergedMaterialMarker*>(
0623 surface->surfaceMaterial()) != nullptr) {
0624 ++markerCount;
0625 }
0626 },
0627 false);
0628 BOOST_CHECK_EQUAL(markerCount, 0u);
0629 }
0630
0631
0632
0633 BOOST_AUTO_TEST_CASE(PortalTagLookup) {
0634 using Acts::PortalDesignatorBlueprintNode;
0635 Transform3 base{Transform3::Identity()};
0636
0637 Blueprint::Config cfg;
0638 cfg.envelope[AxisDirection::AxisZ] = {20_mm, 20_mm};
0639 cfg.envelope[AxisDirection::AxisR] = {0_mm, 20_mm};
0640 auto root = std::make_unique<Blueprint>(cfg);
0641
0642 root->addCylinderContainer("Stack", AxisDirection::AxisZ, [&](auto& stack) {
0643 using enum CylinderVolumeBounds::Face;
0644
0645
0646 stack.addPortalDesignator("Tags", [&](auto& tags) {
0647 tags.tagFace(PositiveDisc, "tracker_calo_boundary");
0648 tags.addStaticVolume(
0649 base * Translation3{Vector3{0, 0, -200_mm}},
0650 std::make_shared<CylinderVolumeBounds>(0_mm, 100_mm, 100_mm),
0651 "VolumeA");
0652 });
0653
0654 stack.addStaticVolume(
0655 base * Translation3{Vector3{0, 0, 200_mm}},
0656 std::make_shared<CylinderVolumeBounds>(0_mm, 100_mm, 100_mm),
0657 "VolumeB");
0658 });
0659
0660 std::unique_ptr<const TrackingGeometry> trackingGeometry =
0661 root->construct({}, gctx, *logger);
0662 BOOST_REQUIRE(trackingGeometry != nullptr);
0663
0664 const Portal* portal = trackingGeometry->findPortal("tracker_calo_boundary");
0665 BOOST_REQUIRE(portal != nullptr);
0666
0667 BOOST_CHECK(trackingGeometry->findPortal("does_not_exist") == nullptr);
0668
0669
0670 auto containsPortal = [&](const TrackingVolume& volume) {
0671 for (const auto& p : volume.portals()) {
0672 if (&p == portal) {
0673 return true;
0674 }
0675 }
0676 return false;
0677 };
0678
0679 const TrackingVolume* volumeA = nullptr;
0680 const TrackingVolume* volumeB = nullptr;
0681 trackingGeometry->apply([&](const TrackingVolume& volume) {
0682 if (volume.volumeName() == "VolumeA") {
0683 volumeA = &volume;
0684 } else if (volume.volumeName() == "VolumeB") {
0685 volumeB = &volume;
0686 }
0687 });
0688
0689 BOOST_REQUIRE(volumeA != nullptr);
0690 BOOST_REQUIRE(volumeB != nullptr);
0691 BOOST_CHECK(containsPortal(*volumeA));
0692 BOOST_CHECK(containsPortal(*volumeB));
0693 }
0694
0695
0696
0697 BOOST_AUTO_TEST_CASE(PortalTagDuplicateThrows) {
0698 using Acts::PortalDesignatorBlueprintNode;
0699 Transform3 base{Transform3::Identity()};
0700
0701 Blueprint::Config cfg;
0702 cfg.envelope[AxisDirection::AxisZ] = {20_mm, 20_mm};
0703 cfg.envelope[AxisDirection::AxisR] = {0_mm, 20_mm};
0704 auto root = std::make_unique<Blueprint>(cfg);
0705
0706 root->addCylinderContainer("Stack", AxisDirection::AxisZ, [&](auto& stack) {
0707 using enum CylinderVolumeBounds::Face;
0708
0709
0710 stack.addPortalDesignator("TagsA", [&](auto& tags) {
0711 tags.tagFace(NegativeDisc, "dup");
0712 tags.addStaticVolume(
0713 base * Translation3{Vector3{0, 0, -200_mm}},
0714 std::make_shared<CylinderVolumeBounds>(0_mm, 100_mm, 100_mm),
0715 "VolumeA");
0716 });
0717
0718
0719
0720 stack.addPortalDesignator("TagsB", [&](auto& tags) {
0721 tags.tagFace(PositiveDisc, "dup");
0722 tags.addStaticVolume(
0723 base * Translation3{Vector3{0, 0, 200_mm}},
0724 std::make_shared<CylinderVolumeBounds>(0_mm, 100_mm, 100_mm),
0725 "VolumeB");
0726 });
0727 });
0728
0729 Logging::ScopedFailureThreshold threshold{Logging::Level::FATAL};
0730 BOOST_CHECK_THROW(root->construct({}, gctx, *logger), std::invalid_argument);
0731 }
0732
0733
0734
0735 BOOST_AUTO_TEST_CASE(PortalTagLookupCuboid) {
0736 using Acts::PortalDesignatorBlueprintNode;
0737 Transform3 base{Transform3::Identity()};
0738
0739 Blueprint::Config cfg;
0740 cfg.envelope[AxisDirection::AxisX] = {20_mm, 20_mm};
0741 cfg.envelope[AxisDirection::AxisY] = {20_mm, 20_mm};
0742 cfg.envelope[AxisDirection::AxisZ] = {20_mm, 20_mm};
0743 auto root = std::make_unique<Blueprint>(cfg);
0744
0745 root->addCuboidContainer("Stack", AxisDirection::AxisX, [&](auto& stack) {
0746 using enum CuboidVolumeBounds::Face;
0747
0748 stack.addPortalDesignator("Tags", [&](auto& tags) {
0749 tags.tagFace(PositiveXFace, "cuboid_boundary");
0750 tags.addStaticVolume(
0751 base * Translation3{Vector3{-200_mm, 0, 0}},
0752 std::make_shared<CuboidVolumeBounds>(100_mm, 100_mm, 100_mm),
0753 "VolumeA");
0754 });
0755
0756 stack.addStaticVolume(
0757 base * Translation3{Vector3{200_mm, 0, 0}},
0758 std::make_shared<CuboidVolumeBounds>(100_mm, 100_mm, 100_mm),
0759 "VolumeB");
0760 });
0761
0762 std::unique_ptr<const TrackingGeometry> trackingGeometry =
0763 root->construct({}, gctx, *logger);
0764 BOOST_REQUIRE(trackingGeometry != nullptr);
0765
0766 const Portal* portal = trackingGeometry->findPortal("cuboid_boundary");
0767 BOOST_REQUIRE(portal != nullptr);
0768
0769 auto containsPortal = [&](const TrackingVolume& volume) {
0770 for (const auto& p : volume.portals()) {
0771 if (&p == portal) {
0772 return true;
0773 }
0774 }
0775 return false;
0776 };
0777
0778 const TrackingVolume* volumeA = nullptr;
0779 const TrackingVolume* volumeB = nullptr;
0780 trackingGeometry->apply([&](const TrackingVolume& volume) {
0781 if (volume.volumeName() == "VolumeA") {
0782 volumeA = &volume;
0783 } else if (volume.volumeName() == "VolumeB") {
0784 volumeB = &volume;
0785 }
0786 });
0787
0788 BOOST_REQUIRE(volumeA != nullptr);
0789 BOOST_REQUIRE(volumeB != nullptr);
0790 BOOST_CHECK(containsPortal(*volumeA));
0791 BOOST_CHECK(containsPortal(*volumeB));
0792 }
0793
0794 BOOST_AUTO_TEST_SUITE_END();