Back to home page

sPhenix code displayed by LXR

 
 

    


File indexing completed on 2025-08-06 08:11:17

0001 // This file is part of the Acts project.
0002 //
0003 // Copyright (C) 2023 CERN for the benefit of the Acts project
0004 //
0005 // This Source Code Form is subject to the terms of the Mozilla Public
0006 // License, v. 2.0. If a copy of the MPL was not distributed with this
0007 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
0008 
0009 #include <boost/test/unit_test.hpp>
0010 
0011 #include "Acts/Definitions/Algebra.hpp"
0012 #include "Acts/Detector/Detector.hpp"
0013 #include "Acts/Detector/DetectorComponents.hpp"
0014 #include "Acts/Detector/DetectorVolume.hpp"
0015 #include "Acts/Detector/GeometryIdGenerator.hpp"
0016 #include "Acts/Detector/PortalGenerators.hpp"
0017 #include "Acts/Detector/detail/CylindricalDetectorHelper.hpp"
0018 #include "Acts/Geometry/CuboidVolumeBounds.hpp"
0019 #include "Acts/Geometry/CutoutCylinderVolumeBounds.hpp"
0020 #include "Acts/Geometry/CylinderVolumeBounds.hpp"
0021 #include "Acts/Geometry/GeometryContext.hpp"
0022 #include "Acts/Navigation/DetectorVolumeFinders.hpp"
0023 #include "Acts/Navigation/SurfaceCandidatesUpdaters.hpp"
0024 #include "Acts/Utilities/Enumerate.hpp"
0025 #include "Acts/Utilities/Logger.hpp"
0026 
0027 #include <algorithm>
0028 #include <array>
0029 #include <cmath>
0030 #include <iterator>
0031 #include <map>
0032 #include <memory>
0033 #include <ostream>
0034 #include <stdexcept>
0035 #include <string>
0036 #include <utility>
0037 #include <vector>
0038 
0039 namespace Acts::Experimental {
0040 class Portal;
0041 }  // namespace Acts::Experimental
0042 
0043 using namespace Acts;
0044 using namespace Experimental;
0045 using namespace Experimental::detail;
0046 using namespace Experimental::detail::CylindricalDetectorHelper;
0047 
0048 Logging::Level logLevel = Logging::VERBOSE;
0049 
0050 GeometryContext tContext;
0051 std::vector<std::shared_ptr<DetectorVolume>> eVolumes = {};
0052 
0053 auto portalGenerator = defaultPortalGenerator();
0054 
0055 BOOST_AUTO_TEST_SUITE(Experimental)
0056 
0057 BOOST_AUTO_TEST_CASE(ConnectVolumeExceptions) {
0058   ACTS_LOCAL_LOGGER(getDefaultLogger("Faulty setups", logLevel));
0059 
0060   auto cBounds0 = std::make_unique<CylinderVolumeBounds>(0., 100., 100);
0061   auto volume0 = DetectorVolumeFactory::construct(
0062       portalGenerator, tContext, "Volume0", Transform3::Identity(),
0063       std::move(cBounds0), tryAllPortals());
0064 
0065   auto cBounds1 =
0066       std::make_unique<CylinderVolumeBounds>(0., 100., 100, 0.2, 1.);
0067   auto volume1 = DetectorVolumeFactory::construct(
0068       portalGenerator, tContext, "Volume0", Transform3::Identity(),
0069       std::move(cBounds1), tryAllPortals());
0070 
0071   ACTS_INFO("*** Test: nullptr in the list of volumes");
0072 
0073   // Invalid arguments: nullptr
0074   std::vector<std::shared_ptr<DetectorVolume>> volumesWithNullptr = {
0075       volume0, nullptr, volume1};
0076   BOOST_CHECK_THROW(connectInR(tContext, volumesWithNullptr, {}, logLevel),
0077                     std::invalid_argument);
0078 
0079   ACTS_INFO("*** Test: non-cylinder in the list of volumes");
0080 
0081   auto cubeBounds = std::make_unique<CuboidVolumeBounds>(100., 100., 100);
0082   auto cube = DetectorVolumeFactory::construct(
0083       portalGenerator, tContext, "Cube", Transform3::Identity(),
0084       std::move(cubeBounds), tryAllPortals());
0085 
0086   // Invalid arguments: cube
0087   std::vector<std::shared_ptr<DetectorVolume>> volumesWithCube = {
0088       volume0, volume1, cube};
0089   BOOST_CHECK_THROW(connectInR(tContext, volumesWithCube, {}, logLevel),
0090                     std::invalid_argument);
0091 
0092   ACTS_INFO("*** Test: non-aligned volume in the list of volumes");
0093   Transform3 rotated = Transform3::Identity();
0094   AngleAxis3 rotX(0.1234, Vector3::UnitX());
0095   rotated *= rotX;
0096 
0097   auto cBounds2 = std::make_unique<CylinderVolumeBounds>(0., 100., 100);
0098   auto volume2 = DetectorVolumeFactory::construct(
0099       portalGenerator, tContext, "Volume2", rotated, std::move(cBounds2),
0100       tryAllPortals());
0101 
0102   // Invalid arguments: non-aligned
0103   std::vector<std::shared_ptr<DetectorVolume>> volumesWithNonaligned = {
0104       volume0, volume1, volume2};
0105   BOOST_CHECK_THROW(connectInR(tContext, volumesWithNonaligned, {}, logLevel),
0106                     std::invalid_argument);
0107 }
0108 
0109 BOOST_AUTO_TEST_CASE(ConnectInR) {
0110   ACTS_LOCAL_LOGGER(getDefaultLogger("Connect: R", logLevel));
0111   ACTS_INFO("*** Test: connect DetectorVolumes in R, create proto container");
0112   // Test with different opening angles
0113   std::vector<ActsScalar> testOpenings = {M_PI, 0.5 * M_PI};
0114 
0115   std::vector<ActsScalar> radii = {0., 10., 100., 200.};
0116   ActsScalar halfZ = 100.;
0117 
0118   // This should work for full cylinder and sector openings
0119   for (auto [io, opening] : enumerate(testOpenings)) {
0120     ACTS_INFO("    -> test  with phi opening: " << opening);
0121     std::string opStr = "opening_" + std::to_string(io);
0122     std::vector<std::shared_ptr<DetectorVolume>> rVolumes = {};
0123     // Create the voluems
0124     for (auto [i, r] : enumerate(radii)) {
0125       if (i > 0) {
0126         auto cBounds = std::make_unique<CylinderVolumeBounds>(
0127             radii[i - 1u], r, halfZ, opening, 0.);
0128         rVolumes.push_back(DetectorVolumeFactory::construct(
0129             portalGenerator, tContext, "Cylinder_r" + std::to_string(i),
0130             Transform3::Identity(), std::move(cBounds), tryAllPortals()));
0131       }
0132     }
0133 
0134     auto protoContainer = connectInR(tContext, rVolumes, {}, logLevel);
0135     // Check the portal setup
0136     BOOST_CHECK_EQUAL(rVolumes[0u]->portalPtrs()[2u],
0137                       rVolumes[1u]->portalPtrs()[3u]);
0138     BOOST_CHECK_EQUAL(rVolumes[1u]->portalPtrs()[2u],
0139                       rVolumes[2u]->portalPtrs()[3u]);
0140     BOOST_CHECK_EQUAL(rVolumes[0u]->portalPtrs()[0u],
0141                       rVolumes[1u]->portalPtrs()[0u]);
0142     BOOST_CHECK_EQUAL(rVolumes[1u]->portalPtrs()[0u],
0143                       rVolumes[2u]->portalPtrs()[0u]);
0144     BOOST_CHECK_EQUAL(rVolumes[0u]->portalPtrs()[1u],
0145                       rVolumes[1u]->portalPtrs()[1u]);
0146     BOOST_CHECK_EQUAL(rVolumes[1u]->portalPtrs()[1u],
0147                       rVolumes[2u]->portalPtrs()[1u]);
0148     BOOST_CHECK_EQUAL(rVolumes[0u]->portalPtrs()[0u], protoContainer[0u]);
0149     BOOST_CHECK_EQUAL(rVolumes[0u]->portalPtrs()[1u], protoContainer[1u]);
0150 
0151     // Assign geometry ids to the volumes
0152     Acts::Experimental::GeometryIdGenerator::Config generatorConfig;
0153     GeometryIdGenerator generator(
0154         generatorConfig, Acts::getDefaultLogger("SequentialIdGenerator",
0155                                                 Acts::Logging::VERBOSE));
0156     auto cache = generator.generateCache();
0157     for (auto& vol : rVolumes) {
0158       generator.assignGeometryId(cache, *vol);
0159     }
0160 
0161     // A detector construction that should work
0162     auto detector =
0163         Detector::makeShared("DetectorInR", rVolumes, tryRootVolumes());
0164 
0165     // Make a rzphi grid
0166     const auto& volumes = detector->volumes();
0167     auto boundaries = rzphiBoundaries(tContext, volumes);
0168     const auto& rBoundaries = boundaries[0u];
0169     const auto& zBoundaries = boundaries[1u];
0170 
0171     // Check the radii
0172     std::vector<ActsScalar> zvalues = {-halfZ, halfZ};
0173     BOOST_CHECK(radii == rBoundaries);
0174     BOOST_CHECK(zvalues == zBoundaries);
0175   }
0176 
0177   // Invalid arguments
0178   ACTS_INFO("*** Test: faulty empty vector");
0179   BOOST_CHECK_THROW(connectInR(tContext, eVolumes, {}, logLevel),
0180                     std::invalid_argument);
0181 
0182   // Faulty setups, not matchint in R
0183   ACTS_INFO("*** Test: volumes are not matching in R");
0184 
0185   auto cBounds00 = std::make_unique<CylinderVolumeBounds>(0., 100., 100);
0186   auto volume00 = DetectorVolumeFactory::construct(
0187       portalGenerator, tContext, "Volume00", Transform3::Identity(),
0188       std::move(cBounds00), tryAllPortals());
0189 
0190   auto cBounds01 = std::make_unique<CylinderVolumeBounds>(101., 200., 100);
0191   auto volume01 = DetectorVolumeFactory::construct(
0192       portalGenerator, tContext, "Volume01", Transform3::Identity(),
0193       std::move(cBounds01), tryAllPortals());
0194 
0195   std::vector<std::shared_ptr<DetectorVolume>> volumesNotMatching = {volume00,
0196                                                                      volume01};
0197   BOOST_CHECK_THROW(connectInR(tContext, volumesNotMatching, {}, logLevel),
0198                     std::runtime_error);
0199 
0200   ACTS_INFO("*** Test: volume bounds are not aligned");
0201   Transform3 shifted = Transform3::Identity();
0202   shifted.pretranslate(Vector3(0., 0., 10.));
0203 
0204   auto cBounds10 = std::make_unique<CylinderVolumeBounds>(0., 100., 100);
0205   auto volume10 = DetectorVolumeFactory::construct(
0206       portalGenerator, tContext, "Volume10", shifted, std::move(cBounds10),
0207       tryAllPortals());
0208 
0209   auto cBounds11 = std::make_unique<CylinderVolumeBounds>(100., 200., 90);
0210   auto volume11 = DetectorVolumeFactory::construct(
0211       portalGenerator, tContext, "Volume11", shifted, std::move(cBounds11),
0212       tryAllPortals());
0213 
0214   std::vector<std::shared_ptr<DetectorVolume>> volumesNotAligned = {volume10,
0215                                                                     volume11};
0216   BOOST_CHECK_THROW(connectInR(tContext, volumesNotAligned, {}, logLevel),
0217                     std::runtime_error);
0218 }
0219 
0220 BOOST_AUTO_TEST_CASE(ConnectInZ) {
0221   ACTS_LOCAL_LOGGER(getDefaultLogger("Connect: Z", logLevel));
0222   ACTS_INFO("*** Test: connect DetectorVolumes in Z, create proto container");
0223 
0224   // @TODO: test with different transforms, this should work in, not used yet
0225   std::vector<Transform3> transforms = {Transform3::Identity()};
0226   std::vector<std::array<ActsScalar, 2>> radii = {{0., 100.}, {20., 120.}};
0227   std::vector<ActsScalar> zValues = {-100., -20, 10., 100., 200.};
0228 
0229   for (auto [it, t] : enumerate(transforms)) {
0230     ACTS_INFO("    -> test series with transform id " << it);
0231 
0232     std::string trfStr = "_transform_" + std::to_string(it);
0233     for (auto [ir, r] : enumerate(radii)) {
0234       ACTS_INFO("        -> test series with radii setup "
0235                 << radii[ir][0u] << ", " << radii[ir][1u]);
0236 
0237       std::string radStr = "_radii_" + std::to_string(ir);
0238       std::vector<std::shared_ptr<DetectorVolume>> zVolumes = {};
0239       for (auto [i, z] : enumerate(zValues)) {
0240         if (i > 0) {
0241           auto cBounds = std::make_unique<CylinderVolumeBounds>(
0242               r[0], r[1], 0.5 * (z - zValues[i - 1u]));
0243           // z center
0244           ActsScalar zCenter = 0.5 * (z + zValues[i - 1u]);
0245           Transform3 ti = Transform3::Identity();
0246           ti.pretranslate(t.translation() +
0247                           zCenter * t.rotation().matrix().col(2));
0248           ti.prerotate(t.rotation());
0249           // create the volume
0250           zVolumes.push_back(DetectorVolumeFactory::construct(
0251               portalGenerator, tContext,
0252               "Cylinder_z" + std::to_string(i) + trfStr + radStr, ti,
0253               std::move(cBounds), tryAllPortals()));
0254         }
0255       }
0256       // Now call the connector
0257       auto protoContainer = connectInZ(tContext, zVolumes, {}, logLevel);
0258 
0259       // Check the portal setup.
0260       // Glued, remainders are outside skin
0261       BOOST_CHECK_EQUAL(zVolumes[0u]->portalPtrs()[1u],
0262                         zVolumes[1u]->portalPtrs()[0u]);
0263       BOOST_CHECK_EQUAL(zVolumes[1u]->portalPtrs()[1u],
0264                         zVolumes[2u]->portalPtrs()[0u]);
0265       BOOST_CHECK_EQUAL(zVolumes[2u]->portalPtrs()[1u],
0266                         zVolumes[3u]->portalPtrs()[0u]);
0267       BOOST_CHECK_EQUAL(protoContainer[0u], zVolumes[0u]->portalPtrs()[0u]);
0268       BOOST_CHECK_EQUAL(protoContainer[1u], zVolumes[3u]->portalPtrs()[1u]);
0269 
0270       // Covered with the same surface, shich is the outside skin
0271       std::vector<unsigned int> checkShared = {2u};
0272       if (radii[ir][0u] > 0.) {
0273         checkShared.push_back(3u);
0274       }
0275 
0276       for (const auto& ip : checkShared) {
0277         BOOST_CHECK_EQUAL(zVolumes[0u]->portalPtrs()[ip],
0278                           zVolumes[1u]->portalPtrs()[ip]);
0279         BOOST_CHECK_EQUAL(zVolumes[1u]->portalPtrs()[ip],
0280                           zVolumes[2u]->portalPtrs()[ip]);
0281         BOOST_CHECK_EQUAL(zVolumes[2u]->portalPtrs()[ip],
0282                           zVolumes[3u]->portalPtrs()[ip]);
0283         BOOST_CHECK_EQUAL(protoContainer[ip], zVolumes[0u]->portalPtrs()[ip]);
0284       }
0285 
0286       // Assign geometry ids to the volumes
0287       Acts::Experimental::GeometryIdGenerator::Config generatorConfig;
0288       GeometryIdGenerator generator(
0289           generatorConfig, Acts::getDefaultLogger("SequentialIdGenerator",
0290                                                   Acts::Logging::VERBOSE));
0291       auto cache = generator.generateCache();
0292       for (auto& vol : zVolumes) {
0293         generator.assignGeometryId(cache, *vol);
0294       }
0295 
0296       auto detector =
0297           Detector::makeShared("DetectorInZ", zVolumes, tryRootVolumes());
0298     }
0299   }
0300 
0301   // Invalid arguments
0302   BOOST_CHECK_THROW(connectInZ(tContext, eVolumes, {}, logLevel),
0303                     std::invalid_argument);
0304 
0305   // Volumes have different radii - other bounds will be the same
0306   auto cBounds00 = std::make_unique<CylinderVolumeBounds>(0., 100., 100);
0307   auto volume00 = DetectorVolumeFactory::construct(
0308       portalGenerator, tContext, "Volume00",
0309       Transform3::Identity() * Translation3(0., 0., -100.),
0310       std::move(cBounds00), tryAllPortals());
0311 
0312   auto cBounds01 = std::make_unique<CylinderVolumeBounds>(0., 105., 100);
0313   auto volume01 = DetectorVolumeFactory::construct(
0314       portalGenerator, tContext, "Volume01",
0315       Transform3::Identity() * Translation3(0., 0., 100.), std::move(cBounds01),
0316       tryAllPortals());
0317 
0318   std::vector<std::shared_ptr<DetectorVolume>> volumesNonalignedBounds = {
0319       volume00, volume01};
0320   BOOST_CHECK_THROW(connectInZ(tContext, volumesNonalignedBounds, {}, logLevel),
0321                     std::runtime_error);
0322 
0323   // Volumes are not attached
0324   auto cBounds10 = std::make_unique<CylinderVolumeBounds>(0., 100., 100);
0325   auto volume10 = DetectorVolumeFactory::construct(
0326       portalGenerator, tContext, "Volume00",
0327       Transform3::Identity() * Translation3(0., 0., -105.),
0328       std::move(cBounds10), tryAllPortals());
0329 
0330   auto cBounds11 = std::make_unique<CylinderVolumeBounds>(0., 100., 100);
0331   auto volume11 = DetectorVolumeFactory::construct(
0332       portalGenerator, tContext, "Volume01",
0333       Transform3::Identity() * Translation3(0., 0., 100.), std::move(cBounds11),
0334       tryAllPortals());
0335 
0336   std::vector<std::shared_ptr<DetectorVolume>> volumesNotAttached = {volume10,
0337                                                                      volume11};
0338   BOOST_CHECK_THROW(connectInZ(tContext, volumesNotAttached, {}, logLevel),
0339                     std::runtime_error);
0340 }
0341 
0342 BOOST_AUTO_TEST_CASE(ConnectInPhi) {
0343   ACTS_LOCAL_LOGGER(getDefaultLogger("Connect: Phi", logLevel));
0344   ACTS_INFO("*** Test: connect DetectorVolumes in Phi, create proto container");
0345 
0346   std::vector<Transform3> transforms = {Transform3::Identity()};
0347   unsigned int phiSectors = 5;
0348   ActsScalar phiHalfSector = M_PI / phiSectors;
0349 
0350   for (auto [it, t] : enumerate(transforms)) {
0351     ACTS_INFO("    -> test series with transform id " << it);
0352 
0353     std::vector<std::shared_ptr<DetectorVolume>> phiVolumes = {};
0354     for (unsigned int i = 0; i < phiSectors; ++i) {
0355       auto cBounds = std::make_unique<CylinderVolumeBounds>(
0356           10., 100., 100., phiHalfSector,
0357           -M_PI + (2u * i + 1u) * phiHalfSector);
0358 
0359       // create the volume
0360       phiVolumes.push_back(DetectorVolumeFactory::construct(
0361           portalGenerator, tContext, "Cylinder_phi" + std::to_string(i), t,
0362           std::move(cBounds), tryAllPortals()));
0363     }
0364 
0365     auto protoContainer = connectInPhi(tContext, phiVolumes, {}, logLevel);
0366 
0367     // All phiVolumes share : inner tube, outer cover, negative & positive disc
0368     std::vector<unsigned int> checkShared = {0u, 1u, 2u, 3u};
0369     for (auto [iv, v] : enumerate(phiVolumes)) {
0370       if (iv > 0u) {
0371         auto current = v;
0372         auto last = phiVolumes[iv - 1u];
0373         for (const auto& ch : checkShared) {
0374           BOOST_CHECK_EQUAL(current->portalPtrs()[ch], last->portalPtrs()[ch]);
0375         }
0376       }
0377     }
0378 
0379     // Assign geometry ids to the volumes
0380     Acts::Experimental::GeometryIdGenerator::Config generatorConfig;
0381     GeometryIdGenerator generator(
0382         generatorConfig, Acts::getDefaultLogger("SequentialIdGenerator",
0383                                                 Acts::Logging::VERBOSE));
0384     auto cache = generator.generateCache();
0385     for (auto& vol : phiVolumes) {
0386       generator.assignGeometryId(cache, *vol);
0387     }
0388 
0389     auto detector =
0390         Detector::makeShared("DetectorInPhi", phiVolumes, tryRootVolumes());
0391   }
0392 
0393   // Invalid arguments
0394   BOOST_CHECK_THROW(connectInPhi(tContext, eVolumes, {}, logLevel),
0395                     std::invalid_argument);
0396 }
0397 
0398 BOOST_AUTO_TEST_CASE(WrapVolumeinRZ) {
0399   ACTS_LOCAL_LOGGER(getDefaultLogger("Wrap: Z-R", logLevel));
0400   ACTS_INFO(
0401       "*** Test: wrap volume in Z-R with CutoutCylinderVolume, create proto "
0402       "container");
0403 
0404   // @TODO: test with different transforms, this should work in, not used yet
0405   std::vector<Transform3> transforms = {Transform3::Identity()};
0406 
0407   // Test with different inner radii
0408   std::vector<std::array<ActsScalar, 3u>> radii = {{0., 100., 500.},
0409                                                    {20., 120., 500.}};
0410 
0411   ActsScalar innerHalfZ = 150.;
0412   ActsScalar outerHalfZ = 175.;
0413 
0414   // Set up all the different tests
0415   for (auto [it, tf] : enumerate(transforms)) {
0416     ACTS_INFO("    Test series with transform id " << it);
0417 
0418     std::string trfStr = "_transform_" + std::to_string(it);
0419     for (auto [ir, r] : enumerate(radii)) {
0420       ACTS_INFO("    -> test series with radii setup " << radii[ir][0u] << ", "
0421                                                        << radii[ir][1u]);
0422 
0423       std::vector<std::shared_ptr<DetectorVolume>> volumes = {};
0424 
0425       std::string radStr = "_radii_" + std::to_string(ir);
0426       // Create the inner bounds
0427       auto iBounds = std::make_unique<CylinderVolumeBounds>(
0428           radii[ir][0u], radii[ir][1u], innerHalfZ);
0429       volumes.push_back(DetectorVolumeFactory::construct(
0430           portalGenerator, tContext, "InnerCylinder" + radStr + trfStr, tf,
0431           std::move(iBounds), tryAllPortals()));
0432 
0433       // Create the wrapping bounds
0434       auto wBounds = std::make_unique<CutoutCylinderVolumeBounds>(
0435           radii[ir][0u], radii[ir][1u], radii[ir][2u], outerHalfZ, innerHalfZ);
0436 
0437       volumes.push_back(DetectorVolumeFactory::construct(
0438           portalGenerator, tContext, "WrappingCylinder" + radStr + trfStr, tf,
0439           std::move(wBounds), tryAllPortals()));
0440 
0441       wrapInZR(tContext, volumes, logLevel);
0442     }
0443   }
0444 
0445   // Invalid arguments
0446   BOOST_CHECK_THROW(wrapInZR(tContext, eVolumes, logLevel),
0447                     std::invalid_argument);
0448 }
0449 
0450 BOOST_AUTO_TEST_CASE(ProtoContainerZR) {
0451   ACTS_LOCAL_LOGGER(getDefaultLogger("Container: Z-R", logLevel));
0452   ACTS_INFO("*** Test: create a container in Z-R.");
0453 
0454   auto transform = Transform3::Identity();
0455 
0456   std::vector<ActsScalar> innerMostRadii = {0., 2.};
0457 
0458   for (auto [ir, imr] : enumerate(innerMostRadii)) {
0459     ACTS_INFO("    -> test series innermost radius setup "
0460               << innerMostRadii[ir]);
0461 
0462     // A container in R
0463     std::vector<ActsScalar> radii = {25., 100., 200.};
0464     ActsScalar halfZ = 200;
0465 
0466     // An innermost Pipe
0467     auto bBounds =
0468         std::make_unique<CylinderVolumeBounds>(imr, radii[0u], halfZ);
0469 
0470     auto innerPipe = DetectorVolumeFactory::construct(
0471         portalGenerator, tContext, "InnerPipe", transform, std::move(bBounds),
0472         tryAllPortals());
0473 
0474     // Make a container representation out of it
0475     std::map<unsigned int, std::shared_ptr<Portal>> ipContainer;
0476     for (auto [ip, p] : enumerate(innerPipe->portalPtrs())) {
0477       ipContainer[ip] = p;
0478     }
0479 
0480     // Create the r - sorted volumes
0481     std::vector<std::shared_ptr<DetectorVolume>> rVolumes = {};
0482     // Create the voluems
0483     for (auto [i, r] : enumerate(radii)) {
0484       if (i > 0) {
0485         auto cBounds =
0486             std::make_unique<CylinderVolumeBounds>(radii[i - 1u], r, halfZ);
0487         rVolumes.push_back(DetectorVolumeFactory::construct(
0488             portalGenerator, tContext, "Cylinder_r" + std::to_string(i),
0489             transform, std::move(cBounds), tryAllPortals()));
0490       }
0491     }
0492 
0493     auto protoContainerInR = connectInR(tContext, rVolumes, {}, logLevel);
0494 
0495     std::vector<ActsScalar> zValues = {-200., -120, 10., 100., 200.};
0496     std::vector<std::shared_ptr<DetectorVolume>> zVolumes = {};
0497     for (auto [i, z] : enumerate(zValues)) {
0498       if (i > 0) {
0499         auto cBounds = std::make_unique<CylinderVolumeBounds>(
0500             200., 300., 0.5 * (z - zValues[i - 1u]));
0501         // z center
0502         ActsScalar zCenter = 0.5 * (z + zValues[i - 1u]);
0503         Transform3 ti = transform;
0504         ti.pretranslate(transform.translation() +
0505                         zCenter * transform.rotation().matrix().col(2));
0506 
0507         // create the volume
0508         zVolumes.push_back(DetectorVolumeFactory::construct(
0509             portalGenerator, tContext, "Cylinder_z" + std::to_string(i), ti,
0510             std::move(cBounds), tryAllPortals()));
0511       }
0512     }
0513     // Now call the connector
0514     auto protoContainerInZ = connectInZ(tContext, zVolumes, {}, logLevel);
0515     auto centralContainer = connectInR(
0516         tContext, {ipContainer, protoContainerInR, protoContainerInZ}, {},
0517         logLevel);
0518 
0519     // Let's make two endcaps
0520     // Nec
0521     auto necBounds = std::make_unique<CylinderVolumeBounds>(imr, 300., 50.);
0522 
0523     auto necTransform = Transform3::Identity();
0524     necTransform.pretranslate(Vector3(0., 0., -250));
0525     auto necVolume = DetectorVolumeFactory::construct(
0526         portalGenerator, tContext, "Nec", necTransform, std::move(necBounds),
0527         tryAllPortals());
0528 
0529     std::map<unsigned int, std::shared_ptr<Portal>> necContainer;
0530     for (auto [ip, p] : enumerate(necVolume->portalPtrs())) {
0531       necContainer[ip] = p;
0532     }
0533 
0534     // Pec container
0535     auto pecInnerBounds =
0536         std::make_unique<CylinderVolumeBounds>(imr, 175., 100.);
0537 
0538     auto pecOuterBounds =
0539         std::make_unique<CylinderVolumeBounds>(175., 300., 100.);
0540 
0541     auto pecTransform = Transform3::Identity();
0542     pecTransform.pretranslate(Vector3(0., 0., 300));
0543     auto pecInner = DetectorVolumeFactory::construct(
0544         portalGenerator, tContext, "PecInner", pecTransform,
0545         std::move(pecInnerBounds), tryAllPortals());
0546     auto pecOuter = DetectorVolumeFactory::construct(
0547         portalGenerator, tContext, "PecOuter", pecTransform,
0548         std::move(pecOuterBounds), tryAllPortals());
0549 
0550     std::vector<std::shared_ptr<DetectorVolume>> pecVolumes = {pecInner,
0551                                                                pecOuter};
0552     auto pecContainer = connectInR(tContext, pecVolumes, {}, logLevel);
0553 
0554     auto overallContainer = connectInZ(
0555         tContext, {necContainer, centralContainer, pecContainer}, {}, logLevel);
0556 
0557     //  Add them together
0558     std::vector<std::shared_ptr<DetectorVolume>> dVolumes;
0559     dVolumes.push_back(innerPipe);
0560     dVolumes.push_back(necVolume);
0561     dVolumes.insert(dVolumes.end(), rVolumes.begin(), rVolumes.end());
0562     dVolumes.insert(dVolumes.end(), zVolumes.begin(), zVolumes.end());
0563     dVolumes.push_back(pecInner);
0564     dVolumes.push_back(pecOuter);
0565 
0566     // Assign geometry ids to the volumes
0567     Acts::Experimental::GeometryIdGenerator::Config generatorConfig;
0568     GeometryIdGenerator generator(
0569         generatorConfig, Acts::getDefaultLogger("SequentialIdGenerator",
0570                                                 Acts::Logging::VERBOSE));
0571     auto cache = generator.generateCache();
0572     for (auto& vol : dVolumes) {
0573       generator.assignGeometryId(cache, *vol);
0574     }
0575 
0576     auto detector = Detector::makeShared("DetectorFromProtoContainer", dVolumes,
0577                                          tryRootVolumes());
0578   }  // test with different innermost radii
0579 }
0580 
0581 BOOST_AUTO_TEST_CASE(WrapContainernRZ) {
0582   ACTS_LOCAL_LOGGER(getDefaultLogger("Container: Wrap", logLevel));
0583   ACTS_INFO("*** Test: create a container in Z-R by wrapping.");
0584 
0585   // Test with different inner radii
0586   std::vector<std::array<ActsScalar, 3u>> radii = {{0., 100., 500.},
0587                                                    {20., 120., 500.}};
0588 
0589   ActsScalar innerHalfZ = 150.;
0590   ActsScalar innerBarrelHalfZ = 75.;
0591   ActsScalar innerEndcapHalfZ = 0.5 * (innerHalfZ - innerBarrelHalfZ);
0592   ActsScalar outerHalfZ = 175.;
0593 
0594   Transform3 tf = Transform3::Identity();
0595 
0596   // Set up all the different tests
0597   for (auto [ir, r] : enumerate(radii)) {
0598     std::string radStr = "_radii_" + std::to_string(ir);
0599     ACTS_INFO("    -> test series innermost radius setup " << radii[ir][0u]);
0600 
0601     // Let's create the inner container first
0602     std::vector<std::shared_ptr<DetectorVolume>> iVolumes = {};
0603 
0604     auto iNecBounds = std::make_unique<CylinderVolumeBounds>(
0605         radii[ir][0u], radii[ir][1u], innerEndcapHalfZ);
0606     Transform3 ntf = tf;
0607     ntf.pretranslate(Vector3(0., 0., -innerBarrelHalfZ - innerEndcapHalfZ));
0608     iVolumes.push_back(DetectorVolumeFactory::construct(
0609         portalGenerator, tContext, "InnerNec" + radStr, ntf,
0610         std::move(iNecBounds), tryAllPortals()));
0611 
0612     auto iBarrelBounds = std::make_unique<CylinderVolumeBounds>(
0613         radii[ir][0u], radii[ir][1u], innerBarrelHalfZ);
0614     iVolumes.push_back(DetectorVolumeFactory::construct(
0615         portalGenerator, tContext, "InnerBarrel" + radStr, tf,
0616         std::move(iBarrelBounds), tryAllPortals()));
0617 
0618     auto iPecBounds = std::make_unique<CylinderVolumeBounds>(
0619         radii[ir][0u], radii[ir][1u], innerEndcapHalfZ);
0620     Transform3 ptf = tf;
0621     ptf.pretranslate(Vector3(0., 0., innerBarrelHalfZ + innerEndcapHalfZ));
0622     iVolumes.push_back(DetectorVolumeFactory::construct(
0623         portalGenerator, tContext, "InnerPec" + radStr, ptf,
0624         std::move(iPecBounds), tryAllPortals()));
0625 
0626     auto innerContainer = connectInZ(tContext, iVolumes, {}, logLevel);
0627 
0628     // Create the wrapping volume
0629     auto wBounds = std::make_unique<CutoutCylinderVolumeBounds>(
0630         radii[ir][0u], radii[ir][1u], radii[ir][2u], outerHalfZ, innerHalfZ);
0631     auto wVolume = DetectorVolumeFactory::construct(
0632         portalGenerator, tContext, "WrappingVolume" + radStr, tf,
0633         std::move(wBounds), tryAllPortals());
0634 
0635     std::vector<DetectorComponent::PortalContainer> containers;
0636     containers.push_back(innerContainer);
0637 
0638     DetectorComponent::PortalContainer outerContainer;
0639     for (auto [ip, p] : enumerate(wVolume->portalPtrs())) {
0640       outerContainer[ip] = p;
0641     }
0642     containers.push_back(outerContainer);
0643 
0644     auto detector = wrapInZR(tContext, containers, logLevel);
0645   }
0646 }
0647 
0648 BOOST_AUTO_TEST_CASE(RZPhiBoundaries) {
0649   auto portalGenerator = defaultPortalGenerator();
0650 
0651   auto innerB = std::make_unique<CylinderVolumeBounds>(0., 20., 100);
0652   auto innerV = DetectorVolumeFactory::construct(
0653       portalGenerator, tContext, "Inner", Transform3::Identity(),
0654       std::move(innerB), tryAllPortals());
0655 
0656   auto middleLB = std::make_unique<CylinderVolumeBounds>(20., 60., 5);
0657   auto middleLT = Transform3::Identity();
0658   middleLT.pretranslate(Vector3(0., 0., -95));
0659   auto middleLV = DetectorVolumeFactory::construct(
0660       portalGenerator, tContext, "MiddleLeft", middleLT, std::move(middleLB),
0661       tryAllPortals());
0662 
0663   auto middleDB = std::make_unique<CylinderVolumeBounds>(20., 40., 90);
0664   auto middleDV = DetectorVolumeFactory::construct(
0665       portalGenerator, tContext, "MiddleDown", Transform3::Identity(),
0666       std::move(middleDB), tryAllPortals());
0667 
0668   auto middleUB = std::make_unique<CylinderVolumeBounds>(40., 60., 90);
0669   auto middleUV = DetectorVolumeFactory::construct(
0670       portalGenerator, tContext, "MiddleUp", Transform3::Identity(),
0671       std::move(middleUB), tryAllPortals());
0672 
0673   auto middleRB = std::make_unique<CylinderVolumeBounds>(20., 60., 5);
0674   auto middleRT = Transform3::Identity();
0675   middleRT.pretranslate(Vector3(0., 0., 95));
0676   auto middleRV = DetectorVolumeFactory::construct(
0677       portalGenerator, tContext, "middleRight", middleRT, std::move(middleRB),
0678       tryAllPortals());
0679 
0680   auto outerB = std::make_unique<CylinderVolumeBounds>(60., 120., 100);
0681   auto outerV = DetectorVolumeFactory::construct(
0682       portalGenerator, tContext, "Outer", Transform3::Identity(),
0683       std::move(outerB), tryAllPortals());
0684 
0685   std::vector<std::shared_ptr<DetectorVolume>> volumes = {
0686       innerV, middleLV, middleDV, middleUV, middleRV, outerV};
0687 
0688   auto boundaries =
0689       rzphiBoundaries(tContext, volumes, 0., Acts::Logging::VERBOSE);
0690   BOOST_CHECK_EQUAL(boundaries.size(), 3u);
0691   // Check the r boundaries
0692   std::vector<ActsScalar> rBoundaries = {0., 20., 40., 60., 120.};
0693   BOOST_CHECK(boundaries[0u] == rBoundaries);
0694   // Check the z boundaries
0695   std::vector<ActsScalar> zBoundaries = {-100., -90., 90., 100.};
0696   BOOST_CHECK(boundaries[1u] == zBoundaries);
0697   BOOST_CHECK_EQUAL(boundaries[2u].size(), 2u);
0698 }
0699 
0700 BOOST_AUTO_TEST_CASE(RZPhiBoundariesWithTolerance) {
0701   auto innerB = std::make_unique<CylinderVolumeBounds>(0., 20., 100);
0702   auto innerV = DetectorVolumeFactory::construct(
0703       portalGenerator, tContext, "Inner", Transform3::Identity(),
0704       std::move(innerB), tryAllPortals());
0705 
0706   auto outerB = std::make_unique<CylinderVolumeBounds>(20.001, 100., 100);
0707   auto outerV = DetectorVolumeFactory::construct(
0708       portalGenerator, tContext, "Inner", Transform3::Identity(),
0709       std::move(outerB), tryAllPortals());
0710 
0711   std::vector<std::shared_ptr<DetectorVolume>> volumes = {innerV, outerV};
0712 
0713   auto boundariesWoTol =
0714       rzphiBoundaries(tContext, volumes, 0., Acts::Logging::VERBOSE);
0715   BOOST_CHECK_EQUAL(boundariesWoTol[0u].size(), 4u);
0716 
0717   auto boundariesWTol =
0718       rzphiBoundaries(tContext, volumes, 0.01, Acts::Logging::VERBOSE);
0719   BOOST_CHECK_EQUAL(boundariesWTol[0u].size(), 3u);
0720 }
0721 
0722 BOOST_AUTO_TEST_SUITE_END()