File indexing completed on 2025-06-30 07:53:11
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/Units.hpp"
0015 #include "Acts/Geometry/Blueprint.hpp"
0016 #include "Acts/Geometry/BlueprintNode.hpp"
0017 #include "Acts/Geometry/ContainerBlueprintNode.hpp"
0018 #include "Acts/Geometry/CylinderVolumeBounds.hpp"
0019 #include "Acts/Geometry/CylinderVolumeStack.hpp"
0020 #include "Acts/Geometry/GeometryContext.hpp"
0021 #include "Acts/Geometry/LayerBlueprintNode.hpp"
0022 #include "Acts/Geometry/MaterialDesignatorBlueprintNode.hpp"
0023 #include "Acts/Geometry/StaticBlueprintNode.hpp"
0024 #include "Acts/Geometry/TrackingVolume.hpp"
0025 #include "Acts/Geometry/TrapezoidVolumeBounds.hpp"
0026 #include "Acts/Geometry/VolumeAttachmentStrategy.hpp"
0027 #include "Acts/Material/BinnedSurfaceMaterial.hpp"
0028 #include "Acts/Material/HomogeneousSurfaceMaterial.hpp"
0029 #include "Acts/Material/Material.hpp"
0030 #include "Acts/Material/MaterialSlab.hpp"
0031 #include "Acts/Material/ProtoSurfaceMaterial.hpp"
0032 #include "Acts/Surfaces/RectangleBounds.hpp"
0033 #include "Acts/Tests/CommonHelpers/DetectorElementStub.hpp"
0034 #include "Acts/Utilities/BinningType.hpp"
0035 #include "Acts/Utilities/Logger.hpp"
0036 #include "Acts/Utilities/ProtoAxis.hpp"
0037
0038 #include <fstream>
0039 #include <memory>
0040 #include <stdexcept>
0041 #include <vector>
0042
0043 using namespace Acts::UnitLiterals;
0044
0045 using Acts::Experimental::Blueprint;
0046 using Acts::Experimental::BlueprintNode;
0047 using Acts::Experimental::BlueprintOptions;
0048 using Acts::Experimental::LayerBlueprintNode;
0049 using Acts::Experimental::MaterialDesignatorBlueprintNode;
0050 using Acts::Experimental::StaticBlueprintNode;
0051
0052 namespace Acts::Test {
0053
0054 auto logger = Acts::getDefaultLogger("UnitTests", Acts::Logging::VERBOSE);
0055
0056 GeometryContext gctx;
0057
0058 namespace {
0059
0060 auto nameLookup(const TrackingGeometry& geo) {
0061 return [&](const std::string& name) -> const TrackingVolume& {
0062 const TrackingVolume* volume = nullptr;
0063
0064 geo.visitVolumes([&](const TrackingVolume* v) {
0065 if (v->volumeName() == name) {
0066 volume = v;
0067 }
0068 });
0069
0070 if (volume == nullptr) {
0071 throw std::runtime_error("Volume not found: " + name);
0072 }
0073 return *volume;
0074 };
0075 }
0076
0077 std::size_t countVolumes(const TrackingGeometry& geo) {
0078 std::size_t nVolumes = 0;
0079 geo.visitVolumes([&](const TrackingVolume* ) { nVolumes++; });
0080 return nVolumes;
0081 }
0082
0083 }
0084
0085 BOOST_AUTO_TEST_SUITE(Geometry);
0086
0087 BOOST_AUTO_TEST_SUITE(BlueprintNodeTest);
0088
0089 BOOST_AUTO_TEST_CASE(InvalidRoot) {
0090 Logging::ScopedFailureThreshold threshold{Logging::Level::FATAL};
0091
0092 Blueprint::Config cfg;
0093 Blueprint root{cfg};
0094 BOOST_CHECK_THROW(root.construct({}, gctx, *logger), std::logic_error);
0095
0096
0097 auto cylBounds = std::make_shared<CylinderVolumeBounds>(10_mm, 20_mm, 100_mm);
0098 root.addChild(
0099 std::make_unique<StaticBlueprintNode>(std::make_unique<TrackingVolume>(
0100 Transform3::Identity(), cylBounds, "child1")));
0101 root.addChild(
0102 std::make_unique<StaticBlueprintNode>(std::make_unique<TrackingVolume>(
0103 Transform3::Identity(), cylBounds, "child2")));
0104
0105 BOOST_CHECK_THROW(root.construct({}, gctx, *logger), std::logic_error);
0106 }
0107
0108 class DummyNode : public BlueprintNode {
0109 public:
0110 explicit DummyNode(const std::string& name) : m_name(name) {}
0111
0112 const std::string& name() const override { return m_name; }
0113
0114 Volume& build(const BlueprintOptions& ,
0115 const GeometryContext& ,
0116 const Acts::Logger& ) override {
0117 throw std::logic_error("Not implemented");
0118 }
0119
0120 PortalShellBase& connect(const BlueprintOptions& ,
0121 const GeometryContext& ,
0122 const Logger& ) override {
0123 throw std::logic_error("Not implemented");
0124 }
0125
0126 void finalize(const BlueprintOptions& ,
0127 const GeometryContext& , TrackingVolume& ,
0128 const Logger& ) override {
0129 throw std::logic_error("Not implemented");
0130 }
0131
0132 private:
0133 std::string m_name;
0134 };
0135
0136 BOOST_AUTO_TEST_CASE(RootCannotBeChild) {
0137 auto node = std::make_shared<DummyNode>("node");
0138 Blueprint::Config cfg;
0139 auto root = std::make_shared<Blueprint>(cfg);
0140
0141 BOOST_CHECK_THROW(node->addChild(root), std::invalid_argument);
0142 }
0143
0144 BOOST_AUTO_TEST_CASE(AddChildInvalid) {
0145 auto node = std::make_shared<DummyNode>("node");
0146
0147
0148 BOOST_CHECK_THROW(node->addChild(node), std::invalid_argument);
0149
0150
0151 BOOST_CHECK_THROW(node->addChild(nullptr), std::invalid_argument);
0152
0153 auto nodeB = std::make_shared<DummyNode>("nodeB");
0154 auto nodeC = std::make_shared<DummyNode>("nodeC");
0155
0156 node->addChild(nodeB);
0157 nodeB->addChild(nodeC);
0158 BOOST_CHECK_THROW(nodeC->addChild(node), std::invalid_argument);
0159
0160
0161 BOOST_CHECK_THROW(node->addChild(nodeC), std::invalid_argument);
0162 }
0163
0164 BOOST_AUTO_TEST_CASE(Depth) {
0165 auto node1 = std::make_shared<DummyNode>("node1");
0166 auto node2 = std::make_shared<DummyNode>("node2");
0167 auto node3 = std::make_shared<DummyNode>("node3");
0168
0169 BOOST_CHECK_EQUAL(node1->depth(), 0);
0170 BOOST_CHECK_EQUAL(node2->depth(), 0);
0171 BOOST_CHECK_EQUAL(node3->depth(), 0);
0172
0173 node2->addChild(node3);
0174 BOOST_CHECK_EQUAL(node2->depth(), 0);
0175 BOOST_CHECK_EQUAL(node3->depth(), 1);
0176
0177 node1->addChild(node2);
0178 BOOST_CHECK_EQUAL(node1->depth(), 0);
0179 BOOST_CHECK_EQUAL(node2->depth(), 1);
0180 BOOST_CHECK_EQUAL(node3->depth(), 2);
0181 }
0182
0183 BOOST_AUTO_TEST_CASE(Static) {
0184 Blueprint::Config cfg;
0185 cfg.envelope[AxisDirection::AxisZ] = {20_mm, 20_mm};
0186 cfg.envelope[AxisDirection::AxisR] = {1_mm, 2_mm};
0187 Blueprint root{cfg};
0188
0189 double hlZ = 30_mm;
0190 auto cylBounds = std::make_shared<CylinderVolumeBounds>(10_mm, 20_mm, hlZ);
0191 auto cyl = std::make_unique<TrackingVolume>(Transform3::Identity(), cylBounds,
0192 "child");
0193
0194 root.addStaticVolume(std::move(cyl));
0195
0196 BOOST_CHECK_EQUAL(root.children().size(), 1);
0197
0198 auto tGeometry = root.construct({}, gctx, *logger);
0199
0200 BOOST_REQUIRE(tGeometry);
0201
0202 BOOST_CHECK(tGeometry->geometryVersion() ==
0203 TrackingGeometry::GeometryVersion::Gen3);
0204
0205 BOOST_CHECK_EQUAL(tGeometry->highestTrackingVolume()->volumes().size(), 1);
0206
0207 BOOST_CHECK_EQUAL(countVolumes(*tGeometry), 2);
0208
0209 auto lookup = nameLookup(*tGeometry);
0210 BOOST_CHECK_EQUAL(lookup("child").volumeBounds().type(),
0211 VolumeBounds::BoundsType::eCylinder);
0212 auto actCyl =
0213 dynamic_cast<const CylinderVolumeBounds&>(lookup("child").volumeBounds());
0214
0215 BOOST_CHECK_EQUAL(actCyl.get(CylinderVolumeBounds::eMinR), 10_mm);
0216 BOOST_CHECK_EQUAL(actCyl.get(CylinderVolumeBounds::eMaxR), 20_mm);
0217 BOOST_CHECK_EQUAL(actCyl.get(CylinderVolumeBounds::eHalfLengthZ), hlZ);
0218
0219 BOOST_CHECK_EQUAL(lookup("World").volumeBounds().type(),
0220 VolumeBounds::BoundsType::eCylinder);
0221 auto worldCyl =
0222 dynamic_cast<const CylinderVolumeBounds&>(lookup("World").volumeBounds());
0223 BOOST_CHECK_EQUAL(worldCyl.get(CylinderVolumeBounds::eMinR), 9_mm);
0224 BOOST_CHECK_EQUAL(worldCyl.get(CylinderVolumeBounds::eMaxR), 22_mm);
0225 BOOST_CHECK_EQUAL(worldCyl.get(CylinderVolumeBounds::eHalfLengthZ),
0226 hlZ + 20_mm);
0227
0228 BOOST_CHECK_EQUAL(lookup("World").portals().size(), 8);
0229 }
0230
0231 BOOST_AUTO_TEST_CASE(CylinderContainer) {
0232 Blueprint::Config cfg;
0233 cfg.envelope[AxisDirection::AxisZ] = {20_mm, 20_mm};
0234 cfg.envelope[AxisDirection::AxisR] = {2_mm, 20_mm};
0235 auto root = std::make_unique<Blueprint>(cfg);
0236
0237 auto& cyl = root->addCylinderContainer("Container", AxisDirection::AxisZ);
0238 cyl.setAttachmentStrategy(VolumeAttachmentStrategy::Gap);
0239
0240 double z0 = -200_mm;
0241 double hlZ = 30_mm;
0242 auto cylBounds = std::make_shared<CylinderVolumeBounds>(10_mm, 20_mm, hlZ);
0243 for (std::size_t i = 0; i < 3; i++) {
0244 auto childCyl = std::make_unique<TrackingVolume>(
0245 Transform3::Identity() *
0246 Translation3{Vector3{0, 0, z0 + i * 2 * hlZ * 1.2}},
0247 cylBounds, "child" + std::to_string(i));
0248 cyl.addStaticVolume(std::move(childCyl));
0249 }
0250
0251 auto tGeometry = root->construct({}, gctx, *logger);
0252 BOOST_CHECK(tGeometry->geometryVersion() ==
0253 TrackingGeometry::GeometryVersion::Gen3);
0254
0255
0256 BOOST_CHECK_EQUAL(countVolumes(*tGeometry), 6);
0257
0258 auto lookup = nameLookup(*tGeometry);
0259 BOOST_CHECK_EQUAL(lookup("World").volumeBounds().type(),
0260 VolumeBounds::BoundsType::eCylinder);
0261 auto worldCyl =
0262 dynamic_cast<const CylinderVolumeBounds&>(lookup("World").volumeBounds());
0263 BOOST_CHECK_EQUAL(worldCyl.get(CylinderVolumeBounds::eMinR), 8_mm);
0264 BOOST_CHECK_EQUAL(worldCyl.get(CylinderVolumeBounds::eMaxR), 40_mm);
0265 BOOST_CHECK_EQUAL(worldCyl.get(CylinderVolumeBounds::eHalfLengthZ), 122_mm);
0266
0267 BOOST_CHECK_EQUAL(lookup("World").portals().size(), 8);
0268
0269 for (std::size_t i = 0; i < 3; i++) {
0270 const auto& vol{lookup("child" + std::to_string(i))};
0271 BOOST_CHECK_EQUAL(vol.volumeBounds().type(),
0272 VolumeBounds::BoundsType::eCylinder);
0273 auto actCyl = dynamic_cast<const CylinderVolumeBounds&>(vol.volumeBounds());
0274 BOOST_CHECK_EQUAL(actCyl.get(CylinderVolumeBounds::eMinR), 10_mm);
0275 BOOST_CHECK_EQUAL(actCyl.get(CylinderVolumeBounds::eMaxR), 20_mm);
0276 BOOST_CHECK_EQUAL(actCyl.get(CylinderVolumeBounds::eHalfLengthZ), hlZ);
0277 }
0278
0279 for (std::size_t i = 0; i < 2; i++) {
0280 const auto& vol{lookup("Container::Gap" + std::to_string(i + 1))};
0281 BOOST_CHECK_EQUAL(vol.volumeBounds().type(),
0282 VolumeBounds::BoundsType::eCylinder);
0283 auto gapCyl = dynamic_cast<const CylinderVolumeBounds&>(vol.volumeBounds());
0284 BOOST_CHECK_EQUAL(gapCyl.get(CylinderVolumeBounds::eMinR), 10_mm);
0285 BOOST_CHECK_EQUAL(gapCyl.get(CylinderVolumeBounds::eMaxR), 20_mm);
0286 BOOST_CHECK_EQUAL(gapCyl.get(CylinderVolumeBounds::eHalfLengthZ), 6_mm);
0287 }
0288 }
0289
0290 BOOST_AUTO_TEST_CASE(Confined) {
0291 Transform3 base{Transform3::Identity()};
0292
0293 Blueprint::Config cfg;
0294 cfg.envelope[AxisDirection::AxisZ] = {20_mm, 20_mm};
0295 cfg.envelope[AxisDirection::AxisR] = {2_mm, 20_mm};
0296 auto root = std::make_unique<Blueprint>(cfg);
0297
0298 root->addStaticVolume(
0299 base, std::make_shared<CylinderVolumeBounds>(50_mm, 400_mm, 1000_mm),
0300 "PixelWrapper", [&](auto& wrap) {
0301 double rMin = 100_mm;
0302 double rMax = 350_mm;
0303 double hlZ = 100_mm;
0304
0305 wrap.addStaticVolume(
0306 base * Translation3{Vector3{0, 0, -600_mm}},
0307 std::make_shared<CylinderVolumeBounds>(rMin, rMax, hlZ),
0308 "PixelNeg1");
0309
0310 wrap.addStaticVolume(
0311 base * Translation3{Vector3{0, 0, -200_mm}},
0312 std::make_shared<CylinderVolumeBounds>(rMin, rMax, hlZ),
0313 "PixelNeg2");
0314
0315 wrap.addStaticVolume(
0316 base * Translation3{Vector3{0, 0, 200_mm}},
0317 std::make_shared<CylinderVolumeBounds>(rMin, rMax, hlZ),
0318 "PixelPos1");
0319
0320 wrap.addStaticVolume(
0321 base * Translation3{Vector3{0, 0, 600_mm}},
0322 std::make_shared<CylinderVolumeBounds>(rMin, rMax, hlZ),
0323 "PixelPos2");
0324 });
0325
0326 auto trackingGeometry = root->construct({}, gctx, *logger);
0327
0328
0329 auto lookup = nameLookup(*trackingGeometry);
0330
0331 BOOST_CHECK_EQUAL(lookup("World").volumeBounds().type(),
0332 VolumeBounds::BoundsType::eCylinder);
0333 auto worldCyl =
0334 dynamic_cast<const CylinderVolumeBounds&>(lookup("World").volumeBounds());
0335 BOOST_CHECK_EQUAL(worldCyl.get(CylinderVolumeBounds::eMinR), 48_mm);
0336 BOOST_CHECK_EQUAL(worldCyl.get(CylinderVolumeBounds::eMaxR), 420_mm);
0337 BOOST_CHECK_EQUAL(worldCyl.get(CylinderVolumeBounds::eHalfLengthZ), 1020_mm);
0338
0339
0340 BOOST_CHECK_EQUAL(lookup("World").portals().size(), 8);
0341 BOOST_CHECK_EQUAL(lookup("World").volumes().size(), 1);
0342
0343 BOOST_CHECK_EQUAL(lookup("PixelWrapper").volumeBounds().type(),
0344 VolumeBounds::BoundsType::eCylinder);
0345
0346 auto wrapperCyl = dynamic_cast<const CylinderVolumeBounds&>(
0347 lookup("PixelWrapper").volumeBounds());
0348 BOOST_CHECK_EQUAL(wrapperCyl.get(CylinderVolumeBounds::eMinR), 50_mm);
0349 BOOST_CHECK_EQUAL(wrapperCyl.get(CylinderVolumeBounds::eMaxR), 400_mm);
0350 BOOST_CHECK_EQUAL(wrapperCyl.get(CylinderVolumeBounds::eHalfLengthZ),
0351 1000_mm);
0352 BOOST_CHECK_EQUAL(lookup("PixelWrapper").portals().size(), 4 + 4 * 4);
0353 BOOST_CHECK_EQUAL(lookup("PixelWrapper").volumes().size(), 4);
0354
0355 for (const auto& name :
0356 {"PixelNeg1", "PixelNeg2", "PixelPos1", "PixelPos2"}) {
0357 BOOST_CHECK_EQUAL(lookup(name).volumeBounds().type(),
0358 VolumeBounds::BoundsType::eCylinder);
0359 auto actCyl =
0360 dynamic_cast<const CylinderVolumeBounds&>(lookup(name).volumeBounds());
0361 BOOST_CHECK_EQUAL(actCyl.get(CylinderVolumeBounds::eMinR), 100_mm);
0362 BOOST_CHECK_EQUAL(actCyl.get(CylinderVolumeBounds::eMaxR), 350_mm);
0363 BOOST_CHECK_EQUAL(actCyl.get(CylinderVolumeBounds::eHalfLengthZ), 100_mm);
0364 BOOST_CHECK_EQUAL(lookup(name).portals().size(), 4);
0365 }
0366 }
0367
0368 BOOST_AUTO_TEST_CASE(ConfinedWithShared) {
0369 Transform3 base{Transform3::Identity()};
0370
0371 constexpr double rMin = 100_mm;
0372 constexpr double rMax = 350_mm;
0373 constexpr double hlZ = 100_mm;
0374
0375 auto sharedBounds = std::make_shared<CylinderVolumeBounds>(rMin, rMax, hlZ);
0376 Blueprint::Config cfg;
0377 cfg.envelope[AxisDirection::AxisZ] = {20_mm, 20_mm};
0378 cfg.envelope[AxisDirection::AxisR] = {2_mm, 20_mm};
0379 auto root = std::make_unique<Blueprint>(cfg);
0380
0381 root->addCylinderContainer(
0382 "PixelWrapper", AxisDirection::AxisZ, [&](auto& wrap) {
0383 wrap.addStaticVolume(base * Translation3{Vector3{0, 0, -750_mm}},
0384 sharedBounds, "PixelNeg1");
0385
0386 wrap.addStaticVolume(base * Translation3{Vector3{0, 0, -200_mm}},
0387 sharedBounds, "PixelNeg2");
0388
0389 wrap.addStaticVolume(base * Translation3{Vector3{0, 0, 200_mm}},
0390 sharedBounds, "PixelPos1");
0391
0392 wrap.addStaticVolume(base * Translation3{Vector3{0, 0, 975_mm}},
0393 sharedBounds, "PixelPos2");
0394 });
0395 auto trackingGeometry = root->construct({}, gctx, *logger);
0396
0397 auto lookup = nameLookup(*trackingGeometry);
0398 BOOST_CHECK_EQUAL(lookup("World").volumeBounds().type(),
0399 VolumeBounds::BoundsType::eCylinder);
0400 auto worldCyl =
0401 dynamic_cast<const CylinderVolumeBounds&>(lookup("World").volumeBounds());
0402 BOOST_CHECK_EQUAL(worldCyl.get(CylinderVolumeBounds::eMinR), 98_mm);
0403 BOOST_CHECK_EQUAL(worldCyl.get(CylinderVolumeBounds::eMaxR), 370_mm);
0404 BOOST_CHECK_EQUAL(worldCyl.get(CylinderVolumeBounds::eHalfLengthZ), 982.5_mm);
0405
0406 BOOST_CHECK_EQUAL(lookup("World").portals().size(), 8);
0407 BOOST_CHECK_EQUAL(lookup("World").volumes().size(), 4);
0408
0409 constexpr std::array<double, 4> expHalfL{187.5_mm, 237.5_mm, 293.75_mm,
0410 243.75_mm};
0411 const std::array<std::string, 4> volNames{"PixelNeg1", "PixelNeg2",
0412 "PixelPos1", "PixelPos2"};
0413 for (std::size_t v = 0; v < 4; ++v) {
0414 const auto& testMe{lookup(volNames[v])};
0415 BOOST_CHECK_EQUAL(testMe.volumeBounds().type(),
0416 VolumeBounds::BoundsType::eCylinder);
0417 BOOST_CHECK_EQUAL(testMe.volumeBoundsPtr() != sharedBounds, true);
0418
0419 auto actCyl =
0420 dynamic_cast<const CylinderVolumeBounds&>(testMe.volumeBounds());
0421 BOOST_CHECK_EQUAL(actCyl.get(CylinderVolumeBounds::eMinR), 100_mm);
0422 BOOST_CHECK_EQUAL(actCyl.get(CylinderVolumeBounds::eMaxR), 350_mm);
0423 BOOST_CHECK_EQUAL(actCyl.get(CylinderVolumeBounds::eHalfLengthZ),
0424 expHalfL[v]);
0425 BOOST_CHECK_EQUAL(testMe.portals().size(), 4);
0426 if (v + 1 == 4) {
0427 break;
0428 }
0429 const auto& nextVol = lookup(volNames[(v + 1)]);
0430 const Acts::Vector3 outside =
0431 testMe.transform().translation() +
0432 Acts::Vector3{150_mm, 0.,
0433 actCyl.get(CylinderVolumeBounds::eHalfLengthZ) - 0.5_mm};
0434 BOOST_CHECK_EQUAL(nextVol.inside(outside), false);
0435 const Acts::Vector3 inside =
0436 testMe.transform().translation() +
0437 Acts::Vector3{150_mm, 0.,
0438 actCyl.get(CylinderVolumeBounds::eHalfLengthZ) + 0.5_mm};
0439 BOOST_CHECK_EQUAL(nextVol.inside(inside), true);
0440 }
0441 }
0442
0443 BOOST_AUTO_TEST_CASE(DiscLayer) {
0444 double yrot = 45_degree;
0445 Transform3 base = Transform3::Identity() * AngleAxis3{yrot, Vector3::UnitY()};
0446
0447 std::vector<std::shared_ptr<Surface>> surfaces;
0448 std::vector<std::unique_ptr<DetectorElementBase>> elements;
0449 double r = 300_mm;
0450 std::size_t nSensors = 8;
0451 double thickness = 2.5_mm;
0452 auto recBounds = std::make_shared<RectangleBounds>(40_mm, 60_mm);
0453
0454 double deltaPhi = 2 * std::numbers::pi / nSensors;
0455 for (std::size_t i = 0; i < nSensors; i++) {
0456
0457
0458 Transform3 trf = base * AngleAxis3{deltaPhi * i, Vector3::UnitZ()} *
0459 Translation3(Vector3::UnitX() * r);
0460
0461 if (i % 2 == 0) {
0462 trf = trf * Translation3{Vector3::UnitZ() * 5_mm};
0463 }
0464
0465 auto& element = elements.emplace_back(
0466 std::make_unique<DetectorElementStub>(trf, recBounds, thickness));
0467
0468 element->surface().assignDetectorElement(*element);
0469
0470 surfaces.push_back(element->surface().getSharedPtr());
0471 }
0472
0473 std::function<void(LayerBlueprintNode&)> withSurfaces =
0474 [&surfaces, &base](LayerBlueprintNode& layer) {
0475 layer.setSurfaces(surfaces)
0476 .setLayerType(LayerBlueprintNode::LayerType::Disc)
0477 .setEnvelope(ExtentEnvelope{{
0478 .z = {0.1_mm, 0.1_mm},
0479 .r = {1_mm, 1_mm},
0480 }})
0481 .setTransform(base);
0482 };
0483
0484 std::function<void(LayerBlueprintNode&)> withProtoLayer =
0485 [&surfaces, &base](LayerBlueprintNode& layer) {
0486 MutableProtoLayer protoLayer{gctx, surfaces, base.inverse()};
0487 layer.setProtoLayer(protoLayer)
0488 .setLayerType(LayerBlueprintNode::LayerType::Disc)
0489 .setEnvelope(ExtentEnvelope{{
0490 .z = {0.1_mm, 0.1_mm},
0491 .r = {1_mm, 1_mm},
0492 }});
0493 };
0494
0495 for (const auto& func : {withSurfaces, withProtoLayer}) {
0496 Blueprint root{{.envelope = ExtentEnvelope{{
0497 .z = {2_mm, 2_mm},
0498 .r = {3_mm, 5_mm},
0499 }}}};
0500
0501 root.addLayer("Layer0", [&](auto& layer) { func(layer); });
0502
0503 auto trackingGeometry = root.construct({}, gctx, *logger);
0504
0505 std::size_t nSurfaces = 0;
0506
0507 trackingGeometry->visitSurfaces([&](const Surface* surface) {
0508 if (surface->associatedDetectorElement() != nullptr) {
0509 nSurfaces++;
0510 }
0511 });
0512
0513 BOOST_CHECK_EQUAL(nSurfaces, surfaces.size());
0514 BOOST_CHECK_EQUAL(countVolumes(*trackingGeometry), 2);
0515 auto lookup = nameLookup(*trackingGeometry);
0516
0517 BOOST_CHECK_EQUAL(lookup("Layer0").volumeBounds().type(),
0518 VolumeBounds::BoundsType::eCylinder);
0519 auto layerCyl = dynamic_cast<const CylinderVolumeBounds&>(
0520 lookup("Layer0").volumeBounds());
0521 BOOST_CHECK_CLOSE(layerCyl.get(CylinderVolumeBounds::eMinR), 258.9999999_mm,
0522 1e-6);
0523 BOOST_CHECK_CLOSE(layerCyl.get(CylinderVolumeBounds::eMaxR),
0524 346.25353003_mm, 1e-6);
0525 BOOST_CHECK_CLOSE(layerCyl.get(CylinderVolumeBounds::eHalfLengthZ), 3.85_mm,
0526 1e-6);
0527 }
0528 }
0529
0530 BOOST_AUTO_TEST_CASE(CylinderLayer) {
0531 double yrot = 0_degree;
0532 Transform3 base = Transform3::Identity() * AngleAxis3{yrot, Vector3::UnitY()};
0533
0534 std::vector<std::shared_ptr<Surface>> surfaces;
0535 std::vector<std::unique_ptr<DetectorElementBase>> elements;
0536
0537 double r = 300_mm;
0538 std::size_t nStaves = 10;
0539 int nSensorsPerStave = 8;
0540 double thickness = 0;
0541 double hlPhi = 40_mm;
0542 double hlZ = 60_mm;
0543 auto recBounds = std::make_shared<RectangleBounds>(hlPhi, hlZ);
0544
0545 double deltaPhi = 2 * std::numbers::pi / nStaves;
0546
0547 for (std::size_t istave = 0; istave < nStaves; istave++) {
0548 for (int isensor = -nSensorsPerStave; isensor <= nSensorsPerStave;
0549 isensor++) {
0550 double z = isensor * (2 * hlZ + 5_mm);
0551
0552 Transform3 trf = base * Translation3(Vector3::UnitZ() * z) *
0553 AngleAxis3{deltaPhi * istave, Vector3::UnitZ()} *
0554 Translation3(Vector3::UnitX() * r) *
0555 AngleAxis3{10_degree, Vector3::UnitZ()} *
0556 AngleAxis3{90_degree, Vector3::UnitY()} *
0557 AngleAxis3{90_degree, Vector3::UnitZ()};
0558 auto& element = elements.emplace_back(
0559 std::make_unique<DetectorElementStub>(trf, recBounds, thickness));
0560 element->surface().assignDetectorElement(*element);
0561 surfaces.push_back(element->surface().getSharedPtr());
0562 }
0563 }
0564
0565 std::function<void(LayerBlueprintNode&)> withSurfaces =
0566 [&surfaces, &base](LayerBlueprintNode& layer) {
0567 layer.setSurfaces(surfaces)
0568 .setLayerType(LayerBlueprintNode::LayerType::Cylinder)
0569 .setEnvelope(ExtentEnvelope{{
0570 .z = {10_mm, 10_mm},
0571 .r = {20_mm, 10_mm},
0572 }})
0573 .setTransform(base);
0574 };
0575
0576 std::function<void(LayerBlueprintNode&)> withProtoLayer =
0577 [&surfaces, &base](LayerBlueprintNode& layer) {
0578 MutableProtoLayer protoLayer{gctx, surfaces, base.inverse()};
0579 layer.setProtoLayer(protoLayer)
0580 .setLayerType(LayerBlueprintNode::LayerType::Cylinder)
0581 .setEnvelope(ExtentEnvelope{{
0582 .z = {10_mm, 10_mm},
0583 .r = {20_mm, 10_mm},
0584 }});
0585 };
0586
0587 for (const auto& func : {withSurfaces, withProtoLayer}) {
0588 Blueprint root{{.envelope = ExtentEnvelope{{
0589 .z = {2_mm, 2_mm},
0590 .r = {3_mm, 5_mm},
0591 }}}};
0592
0593 root.addLayer("Layer0", [&](auto& layer) { func(layer); });
0594
0595 auto trackingGeometry = root.construct({}, gctx, *logger);
0596
0597 std::size_t nSurfaces = 0;
0598
0599 trackingGeometry->visitSurfaces([&](const Surface* surface) {
0600 if (surface->associatedDetectorElement() != nullptr) {
0601 nSurfaces++;
0602 }
0603 });
0604
0605 BOOST_CHECK_EQUAL(nSurfaces, surfaces.size());
0606 BOOST_CHECK_EQUAL(countVolumes(*trackingGeometry), 2);
0607 auto lookup = nameLookup(*trackingGeometry);
0608
0609 BOOST_CHECK_EQUAL(lookup("Layer0").volumeBounds().type(),
0610 VolumeBounds::BoundsType::eCylinder);
0611 auto layerCyl = dynamic_cast<const CylinderVolumeBounds&>(
0612 lookup("Layer0").volumeBounds());
0613 BOOST_CHECK_EQUAL(lookup("Layer0").portals().size(), 4);
0614 BOOST_CHECK_CLOSE(layerCyl.get(CylinderVolumeBounds::eMinR), 275.6897761_mm,
0615 1e-6);
0616 BOOST_CHECK_CLOSE(layerCyl.get(CylinderVolumeBounds::eMaxR), 319.4633358_mm,
0617 1e-6);
0618 BOOST_CHECK_CLOSE(layerCyl.get(CylinderVolumeBounds::eHalfLengthZ), 1070_mm,
0619 1e-6);
0620 }
0621 }
0622
0623 BOOST_AUTO_TEST_CASE(Material) {
0624 Blueprint::Config cfg;
0625 cfg.envelope[AxisDirection::AxisZ] = {20_mm, 20_mm};
0626 cfg.envelope[AxisDirection::AxisR] = {1_mm, 2_mm};
0627 Blueprint root{cfg};
0628
0629 double hlZ = 30_mm;
0630 auto cylBounds = std::make_shared<CylinderVolumeBounds>(10_mm, 20_mm, hlZ);
0631 auto cyl = std::make_unique<TrackingVolume>(Transform3::Identity(), cylBounds,
0632 "child");
0633
0634 using enum AxisDirection;
0635 using enum CylinderVolumeBounds::Face;
0636 using enum AxisBoundaryType;
0637
0638 root.addMaterial("Material", [&](auto& mat) {
0639 mat.configureFace(NegativeDisc, {AxisR, Bound, 5}, {AxisPhi, Bound, 10});
0640 mat.configureFace(PositiveDisc, {AxisR, Bound, 15}, {AxisPhi, Bound, 20});
0641 mat.configureFace(OuterCylinder, {AxisRPhi, Bound, 25}, {AxisZ, Bound, 30});
0642
0643 mat.addStaticVolume(std::move(cyl));
0644 });
0645
0646 auto trackingGeometry = root.construct({}, gctx, *logger);
0647
0648 BOOST_CHECK_EQUAL(countVolumes(*trackingGeometry), 2);
0649 auto lookup = nameLookup(*trackingGeometry);
0650 auto& child = lookup("child");
0651
0652
0653 const auto* negDisc = child.portals()
0654 .at(static_cast<std::size_t>(NegativeDisc))
0655 .surface()
0656 .surfaceMaterial();
0657 BOOST_REQUIRE_NE(negDisc, nullptr);
0658 const auto& negDiscMat =
0659 dynamic_cast<const ProtoGridSurfaceMaterial&>(*negDisc);
0660
0661 const auto* posDisc = child.portals()
0662 .at(static_cast<std::size_t>(PositiveDisc))
0663 .surface()
0664 .surfaceMaterial();
0665 BOOST_REQUIRE_NE(posDisc, nullptr);
0666 const auto& posDiscMat =
0667 dynamic_cast<const ProtoGridSurfaceMaterial&>(*posDisc);
0668
0669 BOOST_CHECK_EQUAL(negDiscMat.binning().at(0).getAxis().getNBins(), 5);
0670 BOOST_CHECK_EQUAL(negDiscMat.binning().at(1).getAxis().getNBins(), 10);
0671 BOOST_CHECK_EQUAL(posDiscMat.binning().at(0).getAxis().getNBins(), 15);
0672 BOOST_CHECK_EQUAL(posDiscMat.binning().at(1).getAxis().getNBins(), 20);
0673
0674
0675 const auto* outerCyl = child.portals()
0676 .at(static_cast<std::size_t>(OuterCylinder))
0677 .surface()
0678 .surfaceMaterial();
0679 BOOST_REQUIRE_NE(outerCyl, nullptr);
0680 const auto& outerCylMat =
0681 dynamic_cast<const ProtoGridSurfaceMaterial&>(*outerCyl);
0682 BOOST_CHECK_EQUAL(outerCylMat.binning().at(0).getAxis().getNBins(), 25);
0683 BOOST_CHECK_EQUAL(outerCylMat.binning().at(1).getAxis().getNBins(), 30);
0684
0685
0686 for (std::size_t i = 0; i < child.portals().size(); i++) {
0687 if (i != static_cast<std::size_t>(NegativeDisc) &&
0688 i != static_cast<std::size_t>(PositiveDisc) &&
0689 i != static_cast<std::size_t>(OuterCylinder)) {
0690 BOOST_CHECK_EQUAL(child.portals().at(i).surface().surfaceMaterial(),
0691 nullptr);
0692 }
0693 }
0694 }
0695
0696 BOOST_AUTO_TEST_CASE(MaterialInvalidAxisDirections) {
0697 Blueprint::Config cfg;
0698 cfg.envelope[AxisDirection::AxisZ] = {20_mm, 20_mm};
0699 cfg.envelope[AxisDirection::AxisR] = {1_mm, 2_mm};
0700 Blueprint root{cfg};
0701
0702 using enum AxisDirection;
0703 using enum AxisBoundaryType;
0704
0705
0706 BOOST_CHECK_THROW(
0707 root.addMaterial("Material",
0708 [&](auto& mat) {
0709 mat.configureFace(
0710 CylinderVolumeBounds::Face::NegativeDisc,
0711 {AxisZ, Bound, 5}, {AxisPhi, Bound, 10});
0712 }),
0713 std::invalid_argument);
0714
0715 BOOST_CHECK_THROW(
0716 root.addMaterial("Material",
0717 [&](auto& mat) {
0718 mat.configureFace(
0719 CylinderVolumeBounds::Face::OuterCylinder,
0720 {AxisR, Bound, 5}, {AxisR, Bound, 10});
0721 }),
0722 std::invalid_argument);
0723
0724
0725 BOOST_CHECK_THROW(
0726 root.addMaterial("Material",
0727 [&](auto& mat) {
0728 mat.configureFace(
0729 CuboidVolumeBounds::Face::NegativeXFace,
0730 {AxisX, Bound, 5}, {AxisZ, Bound, 10});
0731 }),
0732 std::invalid_argument);
0733
0734 BOOST_CHECK_THROW(
0735 root.addMaterial("Material",
0736 [&](auto& mat) {
0737 mat.configureFace(
0738 CuboidVolumeBounds::Face::PositiveYFace,
0739 {AxisY, Bound, 5}, {AxisX, Bound, 10});
0740 }),
0741 std::invalid_argument);
0742
0743 BOOST_CHECK_THROW(
0744 root.addMaterial("Material",
0745 [&](auto& mat) {
0746 mat.configureFace(
0747 CuboidVolumeBounds::Face::NegativeZFace,
0748 {AxisZ, Bound, 5}, {AxisY, Bound, 10});
0749 }),
0750 std::invalid_argument);
0751 }
0752
0753 BOOST_AUTO_TEST_CASE(MaterialMixedVolumeTypes) {
0754 Blueprint::Config cfg;
0755 cfg.envelope[AxisDirection::AxisZ] = {20_mm, 20_mm};
0756 cfg.envelope[AxisDirection::AxisR] = {1_mm, 2_mm};
0757 Blueprint root{cfg};
0758
0759 using enum AxisDirection;
0760 using enum AxisBoundaryType;
0761
0762
0763 BOOST_CHECK_THROW(
0764 root.addMaterial(
0765 "Material",
0766 [&](auto& mat) {
0767 mat.configureFace(CylinderVolumeBounds::Face::NegativeDisc,
0768 {AxisR, Bound, 5}, {AxisPhi, Bound, 10});
0769 mat.configureFace(CuboidVolumeBounds::Face::NegativeXFace,
0770 {AxisX, Bound, 5}, {AxisY, Bound, 10});
0771 }),
0772 std::runtime_error);
0773
0774
0775 BOOST_CHECK_THROW(
0776 root.addMaterial(
0777 "Material",
0778 [&](auto& mat) {
0779 mat.configureFace(CuboidVolumeBounds::Face::NegativeXFace,
0780 {AxisX, Bound, 5}, {AxisY, Bound, 10});
0781 mat.configureFace(CylinderVolumeBounds::Face::NegativeDisc,
0782 {AxisR, Bound, 5}, {AxisPhi, Bound, 10});
0783 }),
0784 std::runtime_error);
0785 }
0786
0787 BOOST_AUTO_TEST_CASE(MaterialCuboid) {
0788 Blueprint::Config cfg;
0789 cfg.envelope[AxisDirection::AxisZ] = {20_mm, 20_mm};
0790 cfg.envelope[AxisDirection::AxisR] = {1_mm, 2_mm};
0791 Blueprint root{cfg};
0792
0793 using enum AxisDirection;
0794 using enum AxisBoundaryType;
0795 using enum CuboidVolumeBounds::Face;
0796
0797 double hlX = 30_mm;
0798 double hlY = 40_mm;
0799 double hlZ = 50_mm;
0800 auto cuboidBounds = std::make_shared<CuboidVolumeBounds>(hlX, hlY, hlZ);
0801 auto cuboid = std::make_unique<TrackingVolume>(Transform3::Identity(),
0802 cuboidBounds, "child");
0803
0804 auto mat = std::make_shared<MaterialDesignatorBlueprintNode>("Material");
0805
0806
0807 mat->configureFace(NegativeXFace, {AxisX, Bound, 5}, {AxisY, Bound, 10});
0808 mat->configureFace(PositiveXFace, {AxisX, Bound, 15}, {AxisY, Bound, 20});
0809 mat->configureFace(NegativeYFace, {AxisX, Bound, 25}, {AxisY, Bound, 30});
0810 mat->configureFace(PositiveYFace, {AxisX, Bound, 35}, {AxisY, Bound, 40});
0811 mat->configureFace(NegativeZFace, {AxisX, Bound, 45}, {AxisY, Bound, 50});
0812 mat->configureFace(PositiveZFace, {AxisX, Bound, 55}, {AxisY, Bound, 60});
0813
0814 mat->addChild(std::make_shared<StaticBlueprintNode>(std::move(cuboid)));
0815
0816 root.addChild(mat);
0817
0818 auto trackingGeometry =
0819 root.construct({}, gctx, *logger->clone(std::nullopt, Logging::VERBOSE));
0820
0821 BOOST_REQUIRE(trackingGeometry);
0822
0823 auto lookup = nameLookup(*trackingGeometry);
0824 auto& child = lookup("child");
0825
0826
0827 for (std::size_t i = 0; i < child.portals().size(); i++) {
0828 const auto* material = child.portals().at(i).surface().surfaceMaterial();
0829 BOOST_REQUIRE_NE(material, nullptr);
0830
0831 const auto& gridMaterial =
0832 dynamic_cast<const ProtoGridSurfaceMaterial&>(*material);
0833
0834
0835 CuboidVolumeBounds::Face face = static_cast<CuboidVolumeBounds::Face>(i);
0836 switch (face) {
0837 case NegativeXFace:
0838 BOOST_CHECK_EQUAL(gridMaterial.binning().at(0).getAxis().getNBins(), 5);
0839 BOOST_CHECK_EQUAL(gridMaterial.binning().at(1).getAxis().getNBins(),
0840 10);
0841 break;
0842 case PositiveXFace:
0843 BOOST_CHECK_EQUAL(gridMaterial.binning().at(0).getAxis().getNBins(),
0844 15);
0845 BOOST_CHECK_EQUAL(gridMaterial.binning().at(1).getAxis().getNBins(),
0846 20);
0847 break;
0848 case NegativeYFace:
0849 BOOST_CHECK_EQUAL(gridMaterial.binning().at(0).getAxis().getNBins(),
0850 25);
0851 BOOST_CHECK_EQUAL(gridMaterial.binning().at(1).getAxis().getNBins(),
0852 30);
0853 break;
0854 case PositiveYFace:
0855 BOOST_CHECK_EQUAL(gridMaterial.binning().at(0).getAxis().getNBins(),
0856 35);
0857 BOOST_CHECK_EQUAL(gridMaterial.binning().at(1).getAxis().getNBins(),
0858 40);
0859 break;
0860 case NegativeZFace:
0861 BOOST_CHECK_EQUAL(gridMaterial.binning().at(0).getAxis().getNBins(),
0862 45);
0863 BOOST_CHECK_EQUAL(gridMaterial.binning().at(1).getAxis().getNBins(),
0864 50);
0865 break;
0866 case PositiveZFace:
0867 BOOST_CHECK_EQUAL(gridMaterial.binning().at(0).getAxis().getNBins(),
0868 55);
0869 BOOST_CHECK_EQUAL(gridMaterial.binning().at(1).getAxis().getNBins(),
0870 60);
0871 break;
0872 }
0873 }
0874 }
0875
0876 BOOST_AUTO_TEST_CASE(HomogeneousMaterialCylinder) {
0877 Blueprint::Config cfg;
0878 cfg.envelope[AxisDirection::AxisZ] = {20_mm, 20_mm};
0879 cfg.envelope[AxisDirection::AxisR] = {1_mm, 2_mm};
0880 Blueprint root{cfg};
0881
0882 double hlZ = 30_mm;
0883 auto cylBounds = std::make_shared<CylinderVolumeBounds>(10_mm, 20_mm, hlZ);
0884 auto cyl = std::make_unique<TrackingVolume>(Transform3::Identity(), cylBounds,
0885 "child");
0886
0887 using enum CylinderVolumeBounds::Face;
0888
0889
0890 auto testMaterial = Acts::Material::fromMolarDensity(
0891 9.370_cm, 46.52_cm, 28.0855, 14, (2.329 / 28.0855) * 1_mol / 1_cm3);
0892
0893 auto negDiscMat = std::make_shared<HomogeneousSurfaceMaterial>(
0894 MaterialSlab(testMaterial, 0.1_mm));
0895 auto posDiscMat = std::make_shared<HomogeneousSurfaceMaterial>(
0896 MaterialSlab(testMaterial, 0.2_mm));
0897 auto outerCylMat = std::make_shared<HomogeneousSurfaceMaterial>(
0898 MaterialSlab(testMaterial, 0.3_mm));
0899
0900 root.addMaterial("Material", [&](auto& mat) {
0901 mat.configureFace(NegativeDisc, negDiscMat);
0902 mat.configureFace(PositiveDisc, posDiscMat);
0903 mat.configureFace(OuterCylinder, outerCylMat);
0904
0905 mat.addStaticVolume(std::move(cyl));
0906 });
0907
0908 auto trackingGeometry = root.construct({}, gctx, *logger);
0909
0910 BOOST_CHECK_EQUAL(countVolumes(*trackingGeometry), 2);
0911 auto lookup = nameLookup(*trackingGeometry);
0912 auto& child = lookup("child");
0913
0914
0915 const auto* negDisc = child.portals()
0916 .at(static_cast<std::size_t>(NegativeDisc))
0917 .surface()
0918 .surfaceMaterial();
0919 BOOST_REQUIRE_NE(negDisc, nullptr);
0920 BOOST_CHECK_EQUAL(negDisc, negDiscMat.get());
0921
0922
0923 const auto* posDisc = child.portals()
0924 .at(static_cast<std::size_t>(PositiveDisc))
0925 .surface()
0926 .surfaceMaterial();
0927 BOOST_REQUIRE_NE(posDisc, nullptr);
0928 BOOST_CHECK_EQUAL(posDisc, posDiscMat.get());
0929
0930
0931 const auto* outerCyl = child.portals()
0932 .at(static_cast<std::size_t>(OuterCylinder))
0933 .surface()
0934 .surfaceMaterial();
0935 BOOST_REQUIRE_NE(outerCyl, nullptr);
0936 BOOST_CHECK_EQUAL(outerCyl, outerCylMat.get());
0937
0938
0939 for (std::size_t i = 0; i < child.portals().size(); i++) {
0940 if (i != static_cast<std::size_t>(NegativeDisc) &&
0941 i != static_cast<std::size_t>(PositiveDisc) &&
0942 i != static_cast<std::size_t>(OuterCylinder)) {
0943 BOOST_CHECK_EQUAL(child.portals().at(i).surface().surfaceMaterial(),
0944 nullptr);
0945 }
0946 }
0947 }
0948
0949 BOOST_AUTO_TEST_CASE(HomogeneousMaterialCuboid) {
0950 Blueprint::Config cfg;
0951 cfg.envelope[AxisDirection::AxisZ] = {20_mm, 20_mm};
0952 cfg.envelope[AxisDirection::AxisR] = {1_mm, 2_mm};
0953 Blueprint root{cfg};
0954
0955 using enum CuboidVolumeBounds::Face;
0956
0957 double hlX = 30_mm;
0958 double hlY = 40_mm;
0959 double hlZ = 50_mm;
0960 auto cuboidBounds = std::make_shared<CuboidVolumeBounds>(hlX, hlY, hlZ);
0961 auto cuboid = std::make_unique<TrackingVolume>(Transform3::Identity(),
0962 cuboidBounds, "child");
0963
0964
0965 auto testMaterial = Acts::Material::fromMolarDensity(
0966 9.370_cm, 46.52_cm, 28.0855, 14, (2.329 / 28.0855) * 1_mol / 1_cm3);
0967
0968 auto negXMat = std::make_shared<HomogeneousSurfaceMaterial>(
0969 MaterialSlab(testMaterial, 0.1_mm));
0970 auto posXMat = std::make_shared<HomogeneousSurfaceMaterial>(
0971 MaterialSlab(testMaterial, 0.2_mm));
0972 auto negYMat = std::make_shared<HomogeneousSurfaceMaterial>(
0973 MaterialSlab(testMaterial, 0.3_mm));
0974 auto posYMat = std::make_shared<HomogeneousSurfaceMaterial>(
0975 MaterialSlab(testMaterial, 0.4_mm));
0976 auto negZMat = std::make_shared<HomogeneousSurfaceMaterial>(
0977 MaterialSlab(testMaterial, 0.5_mm));
0978 auto posZMat = std::make_shared<HomogeneousSurfaceMaterial>(
0979 MaterialSlab(testMaterial, 0.6_mm));
0980
0981 root.addMaterial("Material", [&](auto& mat) {
0982 mat.configureFace(NegativeXFace, negXMat);
0983 mat.configureFace(PositiveXFace, posXMat);
0984 mat.configureFace(NegativeYFace, negYMat);
0985 mat.configureFace(PositiveYFace, posYMat);
0986 mat.configureFace(NegativeZFace, negZMat);
0987 mat.configureFace(PositiveZFace, posZMat);
0988
0989 mat.addStaticVolume(std::move(cuboid));
0990 });
0991
0992 auto trackingGeometry = root.construct({}, gctx, *logger);
0993
0994 BOOST_REQUIRE(trackingGeometry);
0995
0996 auto lookup = nameLookup(*trackingGeometry);
0997 auto& child = lookup("child");
0998
0999
1000 for (std::size_t i = 0; i < child.portals().size(); i++) {
1001 const auto* material = child.portals().at(i).surface().surfaceMaterial();
1002 BOOST_REQUIRE_NE(material, nullptr);
1003
1004 const auto* homMaterial =
1005 dynamic_cast<const HomogeneousSurfaceMaterial*>(material);
1006 BOOST_REQUIRE_NE(homMaterial, nullptr);
1007
1008
1009 CuboidVolumeBounds::Face face = static_cast<CuboidVolumeBounds::Face>(i);
1010 switch (face) {
1011 case NegativeXFace:
1012 BOOST_CHECK_EQUAL(homMaterial, negXMat.get());
1013 break;
1014 case PositiveXFace:
1015 BOOST_CHECK_EQUAL(homMaterial, posXMat.get());
1016 break;
1017 case NegativeYFace:
1018 BOOST_CHECK_EQUAL(homMaterial, negYMat.get());
1019 break;
1020 case PositiveYFace:
1021 BOOST_CHECK_EQUAL(homMaterial, posYMat.get());
1022 break;
1023 case NegativeZFace:
1024 BOOST_CHECK_EQUAL(homMaterial, negZMat.get());
1025 break;
1026 case PositiveZFace:
1027 BOOST_CHECK_EQUAL(homMaterial, posZMat.get());
1028 break;
1029 }
1030 }
1031 }
1032
1033 BOOST_AUTO_TEST_CASE(HomogeneousMaterialMixedVolumeTypes) {
1034 Blueprint::Config cfg;
1035 cfg.envelope[AxisDirection::AxisZ] = {20_mm, 20_mm};
1036 cfg.envelope[AxisDirection::AxisR] = {1_mm, 2_mm};
1037 Blueprint root{cfg};
1038
1039 auto testMaterial = Acts::Material::fromMolarDensity(
1040 9.370_cm, 46.52_cm, 28.0855, 14, (2.329 / 28.0855) * 1_mol / 1_cm3);
1041
1042 auto material = std::make_shared<HomogeneousSurfaceMaterial>(
1043 MaterialSlab(testMaterial, 0.1_mm));
1044
1045
1046 BOOST_CHECK_THROW(
1047 root.addMaterial("Material",
1048 [&](auto& mat) {
1049 mat.configureFace(
1050 CylinderVolumeBounds::Face::NegativeDisc,
1051 material);
1052 mat.configureFace(
1053 CuboidVolumeBounds::Face::NegativeXFace, material);
1054 }),
1055 std::runtime_error);
1056
1057
1058 BOOST_CHECK_THROW(
1059 root.addMaterial("Material",
1060 [&](auto& mat) {
1061 mat.configureFace(
1062 CuboidVolumeBounds::Face::NegativeXFace, material);
1063 mat.configureFace(
1064 CylinderVolumeBounds::Face::NegativeDisc,
1065 material);
1066 }),
1067 std::runtime_error);
1068 }
1069
1070 BOOST_AUTO_TEST_CASE(LayerCenterOfGravity) {
1071
1072 {
1073 double yrot = 45_degree;
1074 Transform3 base =
1075 Transform3::Identity() * AngleAxis3{yrot, Vector3::UnitY()};
1076
1077 std::vector<std::shared_ptr<Surface>> surfaces;
1078 std::vector<std::unique_ptr<DetectorElementBase>> elements;
1079 double r = 300_mm;
1080 std::size_t nSensors = 8;
1081 double thickness = 2.5_mm;
1082 auto recBounds = std::make_shared<RectangleBounds>(40_mm, 60_mm);
1083
1084 double deltaPhi = 2 * std::numbers::pi / nSensors;
1085 for (std::size_t i = 0; i < nSensors; i++) {
1086 Transform3 trf = base * AngleAxis3{deltaPhi * i, Vector3::UnitZ()} *
1087 Translation3(Vector3::UnitX() * r);
1088
1089 if (i % 2 == 0) {
1090 trf = trf * Translation3{Vector3::UnitZ() * 5_mm};
1091 }
1092
1093 auto& element = elements.emplace_back(
1094 std::make_unique<DetectorElementStub>(trf, recBounds, thickness));
1095
1096 element->surface().assignDetectorElement(*element);
1097 surfaces.push_back(element->surface().getSharedPtr());
1098 }
1099
1100 Blueprint root{{.envelope = ExtentEnvelope{{
1101 .z = {2_mm, 2_mm},
1102 .r = {3_mm, 5_mm},
1103 }}}};
1104
1105 root.addLayer("Layer0", [&](auto& layer) {
1106 layer.setSurfaces(surfaces)
1107 .setLayerType(LayerBlueprintNode::LayerType::Disc)
1108 .setEnvelope(ExtentEnvelope{{
1109 .z = {0.1_mm, 0.1_mm},
1110 .r = {1_mm, 1_mm},
1111 }})
1112 .setTransform(base)
1113 .setUseCenterOfGravity(false, false, false);
1114 });
1115
1116 auto trackingGeometry = root.construct({}, gctx, *logger);
1117 auto lookup = nameLookup(*trackingGeometry);
1118
1119 BOOST_CHECK_EQUAL(lookup("Layer0").volumeBounds().type(),
1120 VolumeBounds::BoundsType::eCylinder);
1121
1122 auto layerCyl = dynamic_cast<const CylinderVolumeBounds&>(
1123 lookup("Layer0").volumeBounds());
1124
1125
1126 BOOST_CHECK_CLOSE(layerCyl.get(CylinderVolumeBounds::eMinR), 258.9999999_mm,
1127 1e-6);
1128 BOOST_CHECK_CLOSE(layerCyl.get(CylinderVolumeBounds::eMaxR),
1129 346.25353003_mm, 1e-6);
1130 BOOST_CHECK_CLOSE(layerCyl.get(CylinderVolumeBounds::eHalfLengthZ), 3.85_mm,
1131 1e-6);
1132 }
1133
1134
1135 {
1136 double yrot = 0_degree;
1137 Transform3 base =
1138 Transform3::Identity() * AngleAxis3{yrot, Vector3::UnitY()};
1139
1140 std::vector<std::shared_ptr<Surface>> surfaces;
1141 std::vector<std::unique_ptr<DetectorElementBase>> elements;
1142
1143 double r = 300_mm;
1144 std::size_t nStaves = 10;
1145 int nSensorsPerStave = 8;
1146 double thickness = 0;
1147 double hlPhi = 40_mm;
1148 double hlZ = 60_mm;
1149 auto recBounds = std::make_shared<RectangleBounds>(hlPhi, hlZ);
1150
1151 double deltaPhi = 2 * std::numbers::pi / nStaves;
1152
1153 for (std::size_t istave = 0; istave < nStaves; istave++) {
1154 for (int isensor = -nSensorsPerStave; isensor <= nSensorsPerStave;
1155 isensor++) {
1156 double z = isensor * (2 * hlZ + 5_mm);
1157
1158 Transform3 trf = base * Translation3(Vector3::UnitZ() * z) *
1159 AngleAxis3{deltaPhi * istave, Vector3::UnitZ()} *
1160 Translation3(Vector3::UnitX() * r) *
1161 AngleAxis3{10_degree, Vector3::UnitZ()} *
1162 AngleAxis3{90_degree, Vector3::UnitY()} *
1163 AngleAxis3{90_degree, Vector3::UnitZ()};
1164 auto& element = elements.emplace_back(
1165 std::make_unique<DetectorElementStub>(trf, recBounds, thickness));
1166 element->surface().assignDetectorElement(*element);
1167 surfaces.push_back(element->surface().getSharedPtr());
1168 }
1169 }
1170
1171 Blueprint root{{.envelope = ExtentEnvelope{{
1172 .z = {2_mm, 2_mm},
1173 .r = {3_mm, 5_mm},
1174 }}}};
1175
1176 root.addLayer("Layer0", [&](auto& layer) {
1177 layer.setSurfaces(surfaces)
1178 .setLayerType(LayerBlueprintNode::LayerType::Cylinder)
1179 .setEnvelope(ExtentEnvelope{{
1180 .z = {10_mm, 10_mm},
1181 .r = {20_mm, 10_mm},
1182 }})
1183 .setTransform(base)
1184 .setUseCenterOfGravity(false, false, false);
1185 });
1186
1187 auto trackingGeometry = root.construct({}, gctx, *logger);
1188 auto lookup = nameLookup(*trackingGeometry);
1189 BOOST_CHECK_EQUAL(lookup("Layer0").volumeBounds().type(),
1190 VolumeBounds::BoundsType::eCylinder);
1191
1192 auto layerCyl = dynamic_cast<const CylinderVolumeBounds&>(
1193 lookup("Layer0").volumeBounds());
1194
1195
1196 BOOST_CHECK_EQUAL(lookup("Layer0").portals().size(), 4);
1197 BOOST_CHECK_CLOSE(layerCyl.get(CylinderVolumeBounds::eMinR), 275.6897761_mm,
1198 1e-6);
1199 BOOST_CHECK_CLOSE(layerCyl.get(CylinderVolumeBounds::eMaxR), 319.4633358_mm,
1200 1e-6);
1201 BOOST_CHECK_CLOSE(layerCyl.get(CylinderVolumeBounds::eHalfLengthZ), 1070_mm,
1202 1e-6);
1203 }
1204 }
1205
1206 BOOST_AUTO_TEST_CASE(GeometryIdnetifiersForPortals) {
1207 Blueprint::Config cfg;
1208 cfg.envelope[AxisDirection::AxisZ] = {20_mm, 20_mm};
1209 cfg.envelope[AxisDirection::AxisR] = {1_mm, 2_mm};
1210 Blueprint root{cfg};
1211
1212 auto& cubcontainer =
1213 root.addCuboidContainer("CuboidContainer", AxisDirection::AxisX);
1214 auto parentBounds = std::make_shared<CuboidVolumeBounds>(1_m, 20_mm, 20_mm);
1215 auto parentVol = std::make_unique<TrackingVolume>(Transform3::Identity(),
1216 parentBounds, "parent");
1217 parentVol->assignGeometryId(Acts::GeometryIdentifier{}.withVolume(1));
1218 auto parentNode = std::make_shared<StaticBlueprintNode>(std::move(parentVol));
1219 std::size_t nChambers = 50;
1220
1221 double startX = -1000. + 3. + 0.5;
1222 Transform3 trf = Transform3(Translation3(startX, 0, 0));
1223 auto tbounds =
1224 std::make_shared<TrapezoidVolumeBounds>(3_mm, 3_mm, 10_mm, 15_mm);
1225
1226 for (std::size_t i = 0; i < nChambers; i++) {
1227
1228 trf.translation() += Vector3::UnitX() * i * 7_mm;
1229
1230 auto childVol = std::make_unique<TrackingVolume>(
1231 trf, tbounds, "child" + std::to_string(i));
1232 childVol->assignGeometryId(
1233 Acts::GeometryIdentifier{}.withVolume(1).withLayer(i + 1));
1234 auto childNode = std::make_shared<StaticBlueprintNode>(std::move(childVol));
1235 parentNode->addChild(std::move(childNode));
1236 }
1237
1238 cubcontainer.addChild(std::move(parentNode));
1239
1240 auto trackingGeometry = root.construct({}, gctx, *logger);
1241 }
1242
1243 BOOST_AUTO_TEST_SUITE_END();
1244
1245 BOOST_AUTO_TEST_SUITE_END();
1246
1247 }