File indexing completed on 2026-09-26 08:04:35
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <boost/test/data/test_case.hpp>
0010 #include <boost/test/detail/log_level.hpp>
0011 #include <boost/test/tools/context.hpp>
0012 #include <boost/test/tools/old/interface.hpp>
0013 #include <boost/test/unit_test.hpp>
0014 #include <boost/test/unit_test_log.hpp>
0015 #include <boost/test/unit_test_parameters.hpp>
0016 #include <boost/test/unit_test_suite.hpp>
0017
0018 #include "Acts/Definitions/Algebra.hpp"
0019 #include "Acts/Definitions/Tolerance.hpp"
0020 #include "Acts/Definitions/Units.hpp"
0021 #include "Acts/Geometry/CylinderVolumeBounds.hpp"
0022 #include "Acts/Geometry/CylinderVolumeStack.hpp"
0023 #include "Acts/Geometry/VolumeAttachmentStrategy.hpp"
0024 #include "Acts/Geometry/VolumeResizeStrategy.hpp"
0025 #include "Acts/Utilities/BinningType.hpp"
0026 #include "Acts/Utilities/Logger.hpp"
0027 #include "Acts/Utilities/Zip.hpp"
0028 #include "ActsTests/CommonHelpers/FloatComparisons.hpp"
0029
0030 #include <numbers>
0031
0032 using namespace Acts;
0033 using namespace Acts::UnitLiterals;
0034
0035 namespace ActsTests {
0036
0037 auto logger = getDefaultLogger("UnitTests", Logging::VERBOSE);
0038
0039 struct Fixture {
0040 Logging::Level m_level;
0041 Fixture() {
0042 m_level = Logging::getFailureThreshold();
0043 Logging::setFailureThreshold(Logging::FATAL);
0044 }
0045
0046 ~Fixture() { Logging::setFailureThreshold(m_level); }
0047 };
0048
0049 BOOST_FIXTURE_TEST_SUITE(GeometrySuite, Fixture)
0050
0051 static const std::vector<VolumeAttachmentStrategy> strategies = {
0052 VolumeAttachmentStrategy::Gap,
0053 VolumeAttachmentStrategy::First,
0054 VolumeAttachmentStrategy::Second,
0055 VolumeAttachmentStrategy::Midpoint,
0056 };
0057
0058 static const std::vector<VolumeResizeStrategy> resizeStrategies = {
0059 VolumeResizeStrategy::Expand,
0060 VolumeResizeStrategy::Gap,
0061 };
0062
0063 BOOST_AUTO_TEST_SUITE(CylinderVolumeStackTest)
0064 BOOST_AUTO_TEST_SUITE(ZDirection)
0065
0066 BOOST_DATA_TEST_CASE(Baseline,
0067 (boost::unit_test::data::xrange(-135, 180, 45) *
0068 boost::unit_test::data::xrange(0, 2, 1) *
0069 boost::unit_test::data::make(0.8, 1.0, 1.2) *
0070 boost::unit_test::data::make(Vector3{0_mm, 0_mm, 0_mm},
0071 Vector3{20_mm, 0_mm, 0_mm},
0072 Vector3{0_mm, 20_mm, 0_mm},
0073 Vector3{20_mm, 20_mm, 0_mm},
0074 Vector3{0_mm, 0_mm, 20_mm}) *
0075 boost::unit_test::data::make(strategies)),
0076 angle, rotate, shift, offset, strategy) {
0077 double hlZ = 400_mm;
0078
0079 const auto gctx = Acts::GeometryContext::dangerouslyDefaultConstruct();
0080
0081
0082 auto bounds1 = std::make_shared<CylinderVolumeBounds>(100_mm, 400_mm, hlZ);
0083 auto bounds2 = std::make_shared<CylinderVolumeBounds>(200_mm, 600_mm, hlZ);
0084 auto bounds3 = std::make_shared<CylinderVolumeBounds>(300_mm, 500_mm, hlZ);
0085
0086 Transform3 base =
0087 AngleAxis3(angle * 1_degree, Vector3::UnitX()) * Translation3(offset);
0088
0089 Transform3 transform1 = base;
0090 transform1.translate(Vector3{0_mm, 0_mm, -2 * hlZ * shift});
0091 auto vol1 = std::make_shared<Volume>(transform1, bounds1);
0092
0093 Transform3 transform2 = base;
0094 transform2.translate(Vector3{0_mm, 0_mm, 0_mm});
0095 auto vol2 = std::make_shared<Volume>(transform2, bounds2);
0096
0097 Transform3 transform3 = base;
0098 transform3.translate(Vector3{0_mm, 0_mm, 2 * hlZ * shift});
0099 auto vol3 = std::make_shared<Volume>(transform3, bounds3);
0100
0101 std::vector<Volume*> volumes = {vol1.get(), vol2.get(), vol3.get()};
0102
0103 std::rotate(volumes.begin(), volumes.begin() + rotate, volumes.end());
0104
0105 auto origVolumes = volumes;
0106
0107 std::vector<CylinderVolumeBounds> originalBounds;
0108 std::transform(
0109 volumes.begin(), volumes.end(), std::back_inserter(originalBounds),
0110 [](const auto& vol) {
0111 return *dynamic_cast<const CylinderVolumeBounds*>(&vol->volumeBounds());
0112 });
0113
0114 if (shift < 1.0) {
0115 BOOST_CHECK_THROW(
0116 CylinderVolumeStack(gctx, volumes, AxisDirection::AxisZ, strategy,
0117 VolumeResizeStrategy::Gap, *logger),
0118 std::invalid_argument);
0119 return;
0120 }
0121 CylinderVolumeStack cylStack(gctx, volumes, AxisDirection::AxisZ, strategy,
0122 VolumeResizeStrategy::Gap, *logger);
0123
0124 auto stackBounds =
0125 dynamic_cast<const CylinderVolumeBounds*>(&cylStack.volumeBounds());
0126 BOOST_REQUIRE(stackBounds != nullptr);
0127
0128 BOOST_CHECK_EQUAL(stackBounds->get(CylinderVolumeBounds::eMinR), 100_mm);
0129 BOOST_CHECK_EQUAL(stackBounds->get(CylinderVolumeBounds::eMaxR), 600_mm);
0130 BOOST_CHECK_EQUAL(stackBounds->get(CylinderVolumeBounds::eHalfLengthZ),
0131 hlZ + 2 * hlZ * shift);
0132 CHECK_CLOSE_OR_SMALL(cylStack.localToGlobalTransform(gctx).matrix(),
0133 base.matrix(), 1e-10, 1e-14);
0134
0135
0136 for (const auto& volume : volumes) {
0137 const auto* cylinderBounds =
0138 dynamic_cast<const CylinderVolumeBounds*>(&volume->volumeBounds());
0139 BOOST_REQUIRE(cylinderBounds != nullptr);
0140 BOOST_CHECK_EQUAL(cylinderBounds->get(CylinderVolumeBounds::eMinR), 100_mm);
0141 BOOST_CHECK_EQUAL(cylinderBounds->get(CylinderVolumeBounds::eMaxR), 600_mm);
0142 }
0143
0144
0145 for (std::size_t i = 0; i < volumes.size() - 1; ++i) {
0146 const auto& a = volumes.at(i);
0147 const auto& b = volumes.at(i + 1);
0148
0149 BOOST_CHECK_LT((base.inverse() * a->center(gctx))[eZ],
0150 (base.inverse() * b->center(gctx))[eZ]);
0151 }
0152
0153 if (shift <= 1.0) {
0154
0155 BOOST_CHECK_EQUAL(volumes.size(), 3);
0156
0157
0158 BOOST_CHECK_EQUAL(vol1->localToGlobalTransform(gctx).matrix(),
0159 transform1.matrix());
0160 BOOST_CHECK_EQUAL(vol2->localToGlobalTransform(gctx).matrix(),
0161 transform2.matrix());
0162 BOOST_CHECK_EQUAL(vol3->localToGlobalTransform(gctx).matrix(),
0163 transform3.matrix());
0164
0165 for (const auto& [volume, bounds] : zip(origVolumes, originalBounds)) {
0166 const auto* newBounds =
0167 dynamic_cast<const CylinderVolumeBounds*>(&volume->volumeBounds());
0168 BOOST_CHECK_EQUAL(newBounds->get(CylinderVolumeBounds::eHalfLengthZ),
0169 bounds.get(CylinderVolumeBounds::eHalfLengthZ));
0170 }
0171 } else {
0172 if (strategy == VolumeAttachmentStrategy::Gap) {
0173
0174 BOOST_CHECK_EQUAL(volumes.size(), 5);
0175 auto gap1 = volumes.at(1);
0176 auto gap2 = volumes.at(3);
0177
0178 BOOST_TEST_MESSAGE(
0179 "Gap 1: " << gap1->localToGlobalTransform(gctx).matrix());
0180 BOOST_TEST_MESSAGE(
0181 "Gap 2: " << gap2->localToGlobalTransform(gctx).matrix());
0182
0183 const auto* gapBounds1 =
0184 dynamic_cast<const CylinderVolumeBounds*>(&gap1->volumeBounds());
0185 const auto* gapBounds2 =
0186 dynamic_cast<const CylinderVolumeBounds*>(&gap2->volumeBounds());
0187
0188 double gapHlZ = (shift - 1.0) * hlZ;
0189
0190 BOOST_CHECK(std::abs(gapBounds1->get(CylinderVolumeBounds::eHalfLengthZ) -
0191 gapHlZ) < 1e-10);
0192 BOOST_CHECK(std::abs(gapBounds2->get(CylinderVolumeBounds::eHalfLengthZ) -
0193 gapHlZ) < 1e-10);
0194
0195 double gap1Z = (-2 * hlZ * shift) + hlZ + gapHlZ;
0196 double gap2Z = (2 * hlZ * shift) - hlZ - gapHlZ;
0197
0198 Transform3 gap1Transform = base * Translation3{0_mm, 0_mm, gap1Z};
0199 Transform3 gap2Transform = base * Translation3{0_mm, 0_mm, gap2Z};
0200
0201 CHECK_CLOSE_OR_SMALL(gap1->localToGlobalTransform(gctx).matrix(),
0202 gap1Transform.matrix(), 1e-10, 1e-14);
0203 CHECK_CLOSE_OR_SMALL(gap2->localToGlobalTransform(gctx).matrix(),
0204 gap2Transform.matrix(), 1e-10, 1e-14);
0205
0206
0207 for (const auto& [volume, bounds] : zip(origVolumes, originalBounds)) {
0208 const auto* newBounds =
0209 dynamic_cast<const CylinderVolumeBounds*>(&volume->volumeBounds());
0210 BOOST_CHECK_EQUAL(newBounds->get(CylinderVolumeBounds::eHalfLengthZ),
0211 bounds.get(CylinderVolumeBounds::eHalfLengthZ));
0212 }
0213
0214
0215 BOOST_CHECK_EQUAL(vol1->localToGlobalTransform(gctx).matrix(),
0216 transform1.matrix());
0217 BOOST_CHECK_EQUAL(vol2->localToGlobalTransform(gctx).matrix(),
0218 transform2.matrix());
0219 BOOST_CHECK_EQUAL(vol3->localToGlobalTransform(gctx).matrix(),
0220 transform3.matrix());
0221
0222 } else if (strategy == VolumeAttachmentStrategy::First) {
0223
0224 BOOST_CHECK_EQUAL(volumes.size(), 3);
0225
0226 double wGap = (shift - 1.0) * hlZ * 2;
0227
0228
0229 auto newBounds1 =
0230 dynamic_cast<const CylinderVolumeBounds*>(&vol1->volumeBounds());
0231 BOOST_CHECK_EQUAL(newBounds1->get(CylinderVolumeBounds::eHalfLengthZ),
0232 hlZ + wGap / 2.0);
0233 double pZ1 = -2 * hlZ * shift + wGap / 2.0;
0234 Transform3 expectedTransform1 = base * Translation3{0_mm, 0_mm, pZ1};
0235 CHECK_CLOSE_OR_SMALL(vol1->localToGlobalTransform(gctx).matrix(),
0236 expectedTransform1.matrix(), 1e-10, 1e-14);
0237
0238
0239 auto newBounds2 =
0240 dynamic_cast<const CylinderVolumeBounds*>(&vol2->volumeBounds());
0241 BOOST_CHECK_EQUAL(newBounds2->get(CylinderVolumeBounds::eHalfLengthZ),
0242 hlZ + wGap / 2.0);
0243 double pZ2 = wGap / 2.0;
0244 Transform3 expectedTransform2 = base * Translation3{0_mm, 0_mm, pZ2};
0245 CHECK_CLOSE_OR_SMALL(vol2->localToGlobalTransform(gctx).matrix(),
0246 expectedTransform2.matrix(), 1e-10, 1e-14);
0247
0248
0249 auto newBounds3 =
0250 dynamic_cast<const CylinderVolumeBounds*>(&vol3->volumeBounds());
0251 BOOST_CHECK_EQUAL(newBounds3->get(CylinderVolumeBounds::eHalfLengthZ),
0252 hlZ);
0253 double pZ3 = 2 * hlZ * shift;
0254 Transform3 expectedTransform3 = base * Translation3{0_mm, 0_mm, pZ3};
0255 CHECK_CLOSE_OR_SMALL(vol3->localToGlobalTransform(gctx).matrix(),
0256 expectedTransform3.matrix(), 1e-10, 1e-14);
0257 } else if (strategy == VolumeAttachmentStrategy::Second) {
0258
0259 BOOST_CHECK_EQUAL(volumes.size(), 3);
0260
0261 double wGap = (shift - 1.0) * hlZ * 2;
0262
0263
0264 auto newBounds1 =
0265 dynamic_cast<const CylinderVolumeBounds*>(&vol1->volumeBounds());
0266 BOOST_CHECK_EQUAL(newBounds1->get(CylinderVolumeBounds::eHalfLengthZ),
0267 hlZ);
0268 double pZ1 = -2 * hlZ * shift;
0269 Transform3 expectedTransform1 = base * Translation3{0_mm, 0_mm, pZ1};
0270 CHECK_CLOSE_OR_SMALL(vol1->localToGlobalTransform(gctx).matrix(),
0271 expectedTransform1.matrix(), 1e-10, 1e-14);
0272
0273
0274 auto newBounds2 =
0275 dynamic_cast<const CylinderVolumeBounds*>(&vol2->volumeBounds());
0276 BOOST_CHECK_EQUAL(newBounds2->get(CylinderVolumeBounds::eHalfLengthZ),
0277 hlZ + wGap / 2.0);
0278 double pZ2 = -wGap / 2.0;
0279 Transform3 expectedTransform2 = base * Translation3{0_mm, 0_mm, pZ2};
0280 CHECK_CLOSE_OR_SMALL(vol2->localToGlobalTransform(gctx).matrix(),
0281 expectedTransform2.matrix(), 1e-10, 1e-14);
0282
0283
0284 auto newBounds3 =
0285 dynamic_cast<const CylinderVolumeBounds*>(&vol3->volumeBounds());
0286 BOOST_CHECK_EQUAL(newBounds3->get(CylinderVolumeBounds::eHalfLengthZ),
0287 hlZ + wGap / 2.0);
0288 double pZ3 = 2 * hlZ * shift - wGap / 2.0;
0289 Transform3 expectedTransform3 = base * Translation3{0_mm, 0_mm, pZ3};
0290 CHECK_CLOSE_OR_SMALL(vol3->localToGlobalTransform(gctx).matrix(),
0291 expectedTransform3.matrix(), 1e-10, 1e-14);
0292 } else if (strategy == VolumeAttachmentStrategy::Midpoint) {
0293
0294 BOOST_CHECK_EQUAL(volumes.size(), 3);
0295
0296 double wGap = (shift - 1.0) * hlZ * 2;
0297
0298
0299 auto newBounds1 =
0300 dynamic_cast<const CylinderVolumeBounds*>(&vol1->volumeBounds());
0301 BOOST_CHECK_EQUAL(newBounds1->get(CylinderVolumeBounds::eHalfLengthZ),
0302 hlZ + wGap / 4.0);
0303 double pZ1 = -2 * hlZ * shift + wGap / 4.0;
0304 Transform3 expectedTransform1 = base * Translation3{0_mm, 0_mm, pZ1};
0305 CHECK_CLOSE_OR_SMALL(vol1->localToGlobalTransform(gctx).matrix(),
0306 expectedTransform1.matrix(), 1e-10, 1e-14);
0307
0308
0309 auto newBounds2 =
0310 dynamic_cast<const CylinderVolumeBounds*>(&vol2->volumeBounds());
0311 BOOST_CHECK_EQUAL(newBounds2->get(CylinderVolumeBounds::eHalfLengthZ),
0312 hlZ + wGap / 2.0);
0313 CHECK_CLOSE_OR_SMALL(vol2->localToGlobalTransform(gctx).matrix(),
0314 base.matrix(), 1e-10, 1e-14);
0315
0316
0317 auto newBounds3 =
0318 dynamic_cast<const CylinderVolumeBounds*>(&vol3->volumeBounds());
0319 BOOST_CHECK_EQUAL(newBounds3->get(CylinderVolumeBounds::eHalfLengthZ),
0320 hlZ + wGap / 4.0);
0321 double pZ3 = 2 * hlZ * shift - wGap / 4.0;
0322 Transform3 expectedTransform3 = base * Translation3{0_mm, 0_mm, pZ3};
0323 CHECK_CLOSE_OR_SMALL(vol3->localToGlobalTransform(gctx).matrix(),
0324 expectedTransform3.matrix(), 1e-10, 1e-14);
0325 }
0326 }
0327 }
0328
0329 BOOST_AUTO_TEST_CASE(Asymmetric) {
0330 const auto gctx = Acts::GeometryContext::dangerouslyDefaultConstruct();
0331 double hlZ1 = 200_mm;
0332 double pZ1 = -1100_mm;
0333 double hlZ2 = 600_mm;
0334 double pZ2 = -200_mm;
0335 double hlZ3 = 400_mm;
0336 double pZ3 = 850_mm;
0337
0338
0339 auto bounds1 = std::make_shared<CylinderVolumeBounds>(100_mm, 400_mm, hlZ1);
0340 auto bounds2 = std::make_shared<CylinderVolumeBounds>(200_mm, 600_mm, hlZ2);
0341 auto bounds3 = std::make_shared<CylinderVolumeBounds>(300_mm, 500_mm, hlZ3);
0342
0343 Transform3 transform1 = Transform3::Identity();
0344 transform1.translate(Vector3{0_mm, 0_mm, pZ1});
0345 auto vol1 = std::make_shared<Volume>(transform1, bounds1);
0346
0347 Transform3 transform2 = Transform3::Identity();
0348 transform2.translate(Vector3{0_mm, 0_mm, pZ2});
0349 auto vol2 = std::make_shared<Volume>(transform2, bounds2);
0350
0351 Transform3 transform3 = Transform3::Identity();
0352 transform3.translate(Vector3{0_mm, 0_mm, pZ3});
0353 auto vol3 = std::make_shared<Volume>(transform3, bounds3);
0354
0355 std::vector<Volume*> volumes = {vol2.get(), vol1.get(), vol3.get()};
0356
0357 CylinderVolumeStack cylStack(gctx, volumes, AxisDirection::AxisZ,
0358 VolumeAttachmentStrategy::Gap,
0359 VolumeResizeStrategy::Gap, *logger);
0360 BOOST_CHECK_EQUAL(volumes.size(), 5);
0361
0362 auto stackBounds =
0363 dynamic_cast<const CylinderVolumeBounds*>(&cylStack.volumeBounds());
0364 BOOST_REQUIRE(stackBounds != nullptr);
0365
0366 BOOST_CHECK_EQUAL(stackBounds->get(CylinderVolumeBounds::eMinR), 100_mm);
0367 BOOST_CHECK_EQUAL(stackBounds->get(CylinderVolumeBounds::eMaxR), 600_mm);
0368 BOOST_CHECK_EQUAL(stackBounds->get(CylinderVolumeBounds::eHalfLengthZ),
0369 (std::abs(pZ1 - hlZ1) + pZ3 + hlZ3) / 2.0);
0370
0371 double midZ = (pZ1 - hlZ1 + pZ3 + hlZ3) / 2.0;
0372 Transform3 expectedTransform{Translation3{0_mm, 0_mm, midZ}};
0373 CHECK_CLOSE_OR_SMALL(cylStack.localToGlobalTransform(gctx).matrix(),
0374 expectedTransform.matrix(), 1e-10, 1e-14);
0375 }
0376
0377 BOOST_DATA_TEST_CASE(RotationInZ, boost::unit_test::data::make(strategies),
0378 strategy) {
0379 const auto gctx = Acts::GeometryContext::dangerouslyDefaultConstruct();
0380 double hlZ = 400_mm;
0381 double gap = 100_mm;
0382 double shift = 300_mm;
0383
0384 auto bounds1 = std::make_shared<CylinderVolumeBounds>(100_mm, 400_mm, hlZ);
0385 auto bounds2 = std::make_shared<CylinderVolumeBounds>(200_mm, 300_mm, hlZ);
0386
0387 auto vol1 = std::make_shared<Volume>(
0388 Transform3::Identity() *
0389 Translation3{0_mm, 0_mm, -hlZ - gap / 2.0 + shift},
0390 bounds1);
0391
0392 auto vol2 = std::make_shared<Volume>(
0393 Transform3::Identity() *
0394 Translation3{0_mm, 0_mm, hlZ + gap / 2.0 + shift} *
0395 AngleAxis3{30_degree, Vector3::UnitZ()},
0396 bounds2);
0397
0398 std::vector<Volume*> volumes = {vol1.get(), vol2.get()};
0399
0400 CylinderVolumeStack cylStack(gctx, volumes, AxisDirection::AxisZ, strategy,
0401 VolumeResizeStrategy::Gap, *logger);
0402
0403 auto stackBounds =
0404 dynamic_cast<const CylinderVolumeBounds*>(&cylStack.volumeBounds());
0405 BOOST_REQUIRE(stackBounds != nullptr);
0406 BOOST_CHECK_EQUAL(stackBounds->get(CylinderVolumeBounds::eMinR), 100_mm);
0407 BOOST_CHECK_EQUAL(stackBounds->get(CylinderVolumeBounds::eMaxR), 400_mm);
0408 BOOST_CHECK_EQUAL(stackBounds->get(CylinderVolumeBounds::eHalfLengthZ),
0409 2 * hlZ + gap / 2.0);
0410
0411 auto newBounds1 =
0412 dynamic_cast<const CylinderVolumeBounds*>(&vol1->volumeBounds());
0413 auto newBounds2 =
0414 dynamic_cast<const CylinderVolumeBounds*>(&vol2->volumeBounds());
0415
0416 for (const auto& bounds : {newBounds1, newBounds2}) {
0417 BOOST_CHECK_EQUAL(bounds->get(CylinderVolumeBounds::eMinR), 100_mm);
0418 BOOST_CHECK_EQUAL(bounds->get(CylinderVolumeBounds::eMaxR), 400_mm);
0419 }
0420
0421 if (strategy == VolumeAttachmentStrategy::Gap) {
0422
0423 BOOST_CHECK_EQUAL(vol1->center(gctx)[eZ], -hlZ - gap / 2.0 + shift);
0424 BOOST_CHECK_EQUAL(vol2->center(gctx)[eZ], hlZ + gap / 2.0 + shift);
0425 BOOST_CHECK_EQUAL(newBounds1->get(CylinderVolumeBounds::eHalfLengthZ), hlZ);
0426 BOOST_CHECK_EQUAL(newBounds2->get(CylinderVolumeBounds::eHalfLengthZ), hlZ);
0427 } else if (strategy == VolumeAttachmentStrategy::First) {
0428
0429 BOOST_CHECK_EQUAL(vol1->center(gctx)[eZ], -hlZ + shift);
0430 BOOST_CHECK_EQUAL(newBounds1->get(CylinderVolumeBounds::eHalfLengthZ),
0431 hlZ + gap / 2.0);
0432
0433 BOOST_CHECK_EQUAL(vol2->center(gctx)[eZ], hlZ + gap / 2.0 + shift);
0434 BOOST_CHECK_EQUAL(newBounds2->get(CylinderVolumeBounds::eHalfLengthZ), hlZ);
0435 } else if (strategy == VolumeAttachmentStrategy::Second) {
0436
0437 BOOST_CHECK_EQUAL(vol1->center(gctx)[eZ], -hlZ - gap / 2.0 + shift);
0438 BOOST_CHECK_EQUAL(newBounds1->get(CylinderVolumeBounds::eHalfLengthZ), hlZ);
0439
0440 BOOST_CHECK_EQUAL(vol2->center(gctx)[eZ], hlZ + shift);
0441 BOOST_CHECK_EQUAL(newBounds2->get(CylinderVolumeBounds::eHalfLengthZ),
0442 hlZ + gap / 2.0);
0443 } else if (strategy == VolumeAttachmentStrategy::Midpoint) {
0444
0445 BOOST_CHECK_EQUAL(vol1->center(gctx)[eZ], -hlZ - gap / 4.0 + shift);
0446 BOOST_CHECK_EQUAL(newBounds1->get(CylinderVolumeBounds::eHalfLengthZ),
0447 hlZ + gap / 4.0);
0448
0449
0450 BOOST_CHECK_EQUAL(vol2->center(gctx)[eZ], hlZ + gap / 4.0 + shift);
0451 BOOST_CHECK_EQUAL(newBounds2->get(CylinderVolumeBounds::eHalfLengthZ),
0452 hlZ + gap / 4.0);
0453 }
0454 }
0455
0456 BOOST_DATA_TEST_CASE(UpdateStack,
0457 (boost::unit_test::data::xrange(-135, 180, 45) *
0458 boost::unit_test::data::make(Vector3{0_mm, 0_mm, 0_mm},
0459 Vector3{20_mm, 0_mm, 0_mm},
0460 Vector3{0_mm, 20_mm, 0_mm},
0461 Vector3{20_mm, 20_mm, 0_mm},
0462 Vector3{0_mm, 0_mm, 20_mm}) *
0463 boost::unit_test::data::make(-100_mm, 0_mm, 100_mm) *
0464 boost::unit_test::data::make(resizeStrategies)),
0465 angle, offset, zshift, strategy) {
0466 const auto gctx = Acts::GeometryContext::dangerouslyDefaultConstruct();
0467 double hlZ = 400_mm;
0468
0469
0470 auto bounds1 = std::make_shared<CylinderVolumeBounds>(100_mm, 600_mm, hlZ);
0471 auto bounds2 = std::make_shared<CylinderVolumeBounds>(100_mm, 600_mm, hlZ);
0472 auto bounds3 = std::make_shared<CylinderVolumeBounds>(100_mm, 600_mm, hlZ);
0473
0474 Transform3 base = AngleAxis3(angle * 1_degree, Vector3::UnitX()) *
0475 Translation3(offset + Vector3{0_mm, 0_mm, zshift});
0476
0477 Transform3 transform1 = base;
0478 transform1.translate(Vector3{0_mm, 0_mm, -2 * hlZ});
0479 auto vol1 = std::make_shared<Volume>(transform1, bounds1);
0480
0481 Transform3 transform2 = base;
0482 transform2.translate(Vector3{0_mm, 0_mm, 0_mm});
0483 auto vol2 = std::make_shared<Volume>(transform2, bounds2);
0484
0485 Transform3 transform3 = base;
0486 transform3.translate(Vector3{0_mm, 0_mm, 2 * hlZ});
0487 auto vol3 = std::make_shared<Volume>(transform3, bounds3);
0488
0489 std::vector<Volume*> volumes = {vol1.get(), vol2.get(), vol3.get()};
0490 std::vector<Volume*> originalVolumes = volumes;
0491
0492 std::vector<Transform3> originalTransforms = {transform1, transform2,
0493 transform3};
0494
0495 CylinderVolumeStack cylStack(
0496 gctx, volumes, AxisDirection::AxisZ,
0497 VolumeAttachmentStrategy::Gap,
0498
0499 strategy, *logger);
0500
0501 const auto* originalBounds =
0502 dynamic_cast<const CylinderVolumeBounds*>(&cylStack.volumeBounds());
0503
0504 auto assertOriginalBounds = [&]() {
0505 const auto* cylBounds =
0506 dynamic_cast<const CylinderVolumeBounds*>(&cylStack.volumeBounds());
0507 BOOST_REQUIRE(cylBounds != nullptr);
0508 BOOST_CHECK_EQUAL(cylBounds, originalBounds);
0509 BOOST_CHECK_EQUAL(cylBounds->get(CylinderVolumeBounds::eMinR), 100_mm);
0510 BOOST_CHECK_EQUAL(cylBounds->get(CylinderVolumeBounds::eMaxR), 600_mm);
0511 BOOST_CHECK_EQUAL(cylBounds->get(CylinderVolumeBounds::eHalfLengthZ),
0512 3 * hlZ);
0513 };
0514
0515 assertOriginalBounds();
0516
0517 {
0518
0519 auto bounds = std::make_shared<CylinderVolumeBounds>(
0520 dynamic_cast<const CylinderVolumeBounds&>(cylStack.volumeBounds()));
0521 cylStack.update(gctx, bounds, std::nullopt, *logger);
0522 assertOriginalBounds();
0523 }
0524
0525 {
0526
0527 auto bounds = std::make_shared<CylinderVolumeBounds>(
0528 dynamic_cast<const CylinderVolumeBounds&>(cylStack.volumeBounds()));
0529 bounds->set(CylinderVolumeBounds::eMinR, 200_mm);
0530 BOOST_CHECK_THROW(cylStack.update(gctx, bounds, std::nullopt, *logger),
0531 std::invalid_argument);
0532 assertOriginalBounds();
0533 }
0534
0535 {
0536
0537 auto bounds = std::make_shared<CylinderVolumeBounds>(
0538 dynamic_cast<const CylinderVolumeBounds&>(cylStack.volumeBounds()));
0539 bounds->set(CylinderVolumeBounds::eMaxR, 500_mm);
0540 BOOST_CHECK_THROW(cylStack.update(gctx, bounds, std::nullopt, *logger),
0541 std::invalid_argument);
0542 assertOriginalBounds();
0543 }
0544
0545 {
0546
0547 auto bounds = std::make_shared<CylinderVolumeBounds>(
0548 dynamic_cast<const CylinderVolumeBounds&>(cylStack.volumeBounds()));
0549 bounds->set(CylinderVolumeBounds::eHalfLengthZ, 2 * hlZ);
0550 BOOST_CHECK_THROW(cylStack.update(gctx, bounds, std::nullopt, *logger),
0551 std::invalid_argument);
0552 assertOriginalBounds();
0553 }
0554
0555 {
0556
0557 auto bounds = std::make_shared<CylinderVolumeBounds>(
0558 dynamic_cast<const CylinderVolumeBounds&>(cylStack.volumeBounds()));
0559 bounds->set(CylinderVolumeBounds::eMinR, 50_mm);
0560 cylStack.update(gctx, bounds, std::nullopt, *logger);
0561 const auto* cylBounds =
0562 dynamic_cast<const CylinderVolumeBounds*>(&cylStack.volumeBounds());
0563 BOOST_REQUIRE(cylBounds != nullptr);
0564 BOOST_CHECK_EQUAL(cylBounds->get(CylinderVolumeBounds::eMinR), 50_mm);
0565
0566 BOOST_CHECK_EQUAL(cylBounds->get(CylinderVolumeBounds::eMaxR), 600_mm);
0567 BOOST_CHECK_EQUAL(cylBounds->get(CylinderVolumeBounds::eHalfLengthZ),
0568 3 * hlZ);
0569
0570
0571 BOOST_CHECK_EQUAL(volumes.size(), 3);
0572
0573
0574 for (const auto& [volume, origTransform] :
0575 zip(volumes, originalTransforms)) {
0576 const auto* newBounds =
0577 dynamic_cast<const CylinderVolumeBounds*>(&volume->volumeBounds());
0578 BOOST_CHECK_EQUAL(newBounds->get(CylinderVolumeBounds::eMinR), 50_mm);
0579 BOOST_CHECK_EQUAL(newBounds->get(CylinderVolumeBounds::eMaxR), 600_mm);
0580 BOOST_CHECK_EQUAL(newBounds->get(CylinderVolumeBounds::eHalfLengthZ),
0581 hlZ);
0582
0583
0584 BOOST_CHECK_EQUAL(volume->localToGlobalTransform(gctx).matrix(),
0585 origTransform.matrix());
0586 }
0587 }
0588
0589 {
0590
0591 auto bounds = std::make_shared<CylinderVolumeBounds>(
0592 dynamic_cast<const CylinderVolumeBounds&>(cylStack.volumeBounds()));
0593 bounds->set(CylinderVolumeBounds::eMaxR, 700_mm);
0594 cylStack.update(gctx, bounds, std::nullopt, *logger);
0595 const auto* cylBounds =
0596 dynamic_cast<const CylinderVolumeBounds*>(&cylStack.volumeBounds());
0597 BOOST_REQUIRE(cylBounds != nullptr);
0598 BOOST_CHECK_EQUAL(cylBounds->get(CylinderVolumeBounds::eMaxR), 700_mm);
0599
0600 BOOST_CHECK_EQUAL(cylBounds->get(CylinderVolumeBounds::eMinR), 50_mm);
0601 BOOST_CHECK_EQUAL(cylBounds->get(CylinderVolumeBounds::eHalfLengthZ),
0602 3 * hlZ);
0603
0604
0605 BOOST_CHECK_EQUAL(volumes.size(), 3);
0606
0607
0608 for (const auto& [volume, origTransform] :
0609 zip(volumes, originalTransforms)) {
0610 const auto* newBounds =
0611 dynamic_cast<const CylinderVolumeBounds*>(&volume->volumeBounds());
0612 BOOST_CHECK_EQUAL(newBounds->get(CylinderVolumeBounds::eMinR), 50_mm);
0613 BOOST_CHECK_EQUAL(newBounds->get(CylinderVolumeBounds::eMaxR), 700_mm);
0614 BOOST_CHECK_EQUAL(newBounds->get(CylinderVolumeBounds::eHalfLengthZ),
0615 hlZ);
0616
0617
0618 BOOST_CHECK_EQUAL(volume->localToGlobalTransform(gctx).matrix(),
0619 origTransform.matrix());
0620 }
0621 }
0622
0623 {
0624
0625 auto bounds = std::make_shared<CylinderVolumeBounds>(
0626 dynamic_cast<const CylinderVolumeBounds&>(cylStack.volumeBounds()));
0627 bounds->set(CylinderVolumeBounds::eHalfLengthZ, 4 * hlZ);
0628 cylStack.update(gctx, bounds, std::nullopt, *logger);
0629 const auto* cylBounds =
0630 dynamic_cast<const CylinderVolumeBounds*>(&cylStack.volumeBounds());
0631 BOOST_REQUIRE(cylBounds != nullptr);
0632 BOOST_CHECK_EQUAL(cylBounds->get(CylinderVolumeBounds::eHalfLengthZ),
0633 4 * hlZ);
0634
0635
0636 BOOST_CHECK_EQUAL(cylBounds->get(CylinderVolumeBounds::eMinR), 50_mm);
0637 BOOST_CHECK_EQUAL(cylBounds->get(CylinderVolumeBounds::eMaxR), 700_mm);
0638
0639 if (strategy == VolumeResizeStrategy::Expand) {
0640
0641 BOOST_CHECK_EQUAL(volumes.size(), 3);
0642
0643
0644 auto newBounds1 =
0645 dynamic_cast<const CylinderVolumeBounds*>(&vol1->volumeBounds());
0646 BOOST_CHECK_EQUAL(newBounds1->get(CylinderVolumeBounds::eHalfLengthZ),
0647 hlZ + hlZ / 2.0);
0648 Transform3 expectedTransform1 =
0649 base * Translation3{0_mm, 0_mm, -2 * hlZ - hlZ / 2.0};
0650 BOOST_CHECK_EQUAL(vol1->localToGlobalTransform(gctx).matrix(),
0651 expectedTransform1.matrix());
0652
0653
0654 auto newBounds2 =
0655 dynamic_cast<const CylinderVolumeBounds*>(&vol2->volumeBounds());
0656 BOOST_CHECK_EQUAL(newBounds2->get(CylinderVolumeBounds::eHalfLengthZ),
0657 hlZ);
0658 BOOST_CHECK_EQUAL(vol2->localToGlobalTransform(gctx).matrix(),
0659 transform2.matrix());
0660
0661
0662 auto newBounds3 =
0663 dynamic_cast<const CylinderVolumeBounds*>(&vol3->volumeBounds());
0664 BOOST_CHECK_EQUAL(newBounds3->get(CylinderVolumeBounds::eHalfLengthZ),
0665 hlZ + hlZ / 2.0);
0666 Transform3 expectedTransform3 =
0667 base * Translation3{0_mm, 0_mm, 2 * hlZ + hlZ / 2.0};
0668 BOOST_CHECK_EQUAL(vol3->localToGlobalTransform(gctx).matrix(),
0669 expectedTransform3.matrix());
0670 } else if (strategy == VolumeResizeStrategy::Gap) {
0671
0672 BOOST_CHECK_EQUAL(volumes.size(), 5);
0673
0674 for (const auto& [volume, origTransform] :
0675 zip(originalVolumes, originalTransforms)) {
0676 const auto* newBounds =
0677 dynamic_cast<const CylinderVolumeBounds*>(&volume->volumeBounds());
0678 BOOST_CHECK_EQUAL(newBounds->get(CylinderVolumeBounds::eMinR), 50_mm);
0679 BOOST_CHECK_EQUAL(newBounds->get(CylinderVolumeBounds::eMaxR), 700_mm);
0680 BOOST_CHECK_EQUAL(newBounds->get(CylinderVolumeBounds::eHalfLengthZ),
0681 hlZ);
0682
0683 BOOST_CHECK_EQUAL(volume->localToGlobalTransform(gctx).matrix(),
0684 origTransform.matrix());
0685 }
0686
0687 auto gap1 = volumes.front();
0688 auto gap2 = volumes.back();
0689
0690 const auto* gapBounds1 =
0691 dynamic_cast<const CylinderVolumeBounds*>(&gap1->volumeBounds());
0692 const auto* gapBounds2 =
0693 dynamic_cast<const CylinderVolumeBounds*>(&gap2->volumeBounds());
0694
0695 BOOST_CHECK_EQUAL(gapBounds1->get(CylinderVolumeBounds::eHalfLengthZ),
0696 hlZ / 2.0);
0697 BOOST_CHECK_EQUAL(gapBounds2->get(CylinderVolumeBounds::eHalfLengthZ),
0698 hlZ / 2.0);
0699
0700 Transform3 gap1Transform =
0701 base * Translation3{0_mm, 0_mm, -3 * hlZ - hlZ / 2.0};
0702 Transform3 gap2Transform =
0703 base * Translation3{0_mm, 0_mm, 3 * hlZ + hlZ / 2.0};
0704
0705 CHECK_CLOSE_OR_SMALL(gap1->localToGlobalTransform(gctx).matrix(),
0706 gap1Transform.matrix(), 1e-10, 1e-14);
0707 CHECK_CLOSE_OR_SMALL(gap2->localToGlobalTransform(gctx).matrix(),
0708 gap2Transform.matrix(), 1e-10, 1e-14);
0709 }
0710 }
0711 }
0712
0713 BOOST_DATA_TEST_CASE(
0714 UpdateStackOneSided,
0715 (boost::unit_test::data::make(-1.0, 1.0) ^
0716 boost::unit_test::data::make(VolumeResizeStrategy::Gap,
0717 VolumeResizeStrategy::Expand)),
0718 f, strategy) {
0719 const auto gctx = Acts::GeometryContext::dangerouslyDefaultConstruct();
0720
0721 auto trf = Transform3::Identity();
0722
0723 auto trf1 = trf * Translation3{Vector3{0_mm, 0_mm, -500_mm}};
0724 auto vol1 = std::make_shared<Volume>(
0725 trf1, std::make_shared<CylinderVolumeBounds>(100_mm, 300_mm, 400_mm));
0726
0727 auto trf2 = trf * Translation3{Vector3{0_mm, 0_mm, 500_mm}};
0728 auto vol2 = std::make_shared<Volume>(
0729 trf2, std::make_shared<CylinderVolumeBounds>(100_mm, 300_mm, 400_mm));
0730
0731 std::vector<Volume*> volumes = {vol1.get(), vol2.get()};
0732
0733 CylinderVolumeStack cylStack{
0734 gctx, volumes, AxisDirection::AxisZ, VolumeAttachmentStrategy::Gap,
0735 strategy, *logger};
0736 const auto* originalBounds =
0737 dynamic_cast<const CylinderVolumeBounds*>(&cylStack.volumeBounds());
0738
0739
0740 auto newBounds = std::make_shared<CylinderVolumeBounds>(
0741 dynamic_cast<const CylinderVolumeBounds&>(cylStack.volumeBounds()));
0742 newBounds->set(CylinderVolumeBounds::eHalfLengthZ, 950_mm);
0743
0744 trf *= Translation3{Vector3{0_mm, 0_mm, f * 50_mm}};
0745
0746
0747
0748 auto checkUnchanged = [&]() {
0749 const auto* cylBounds =
0750 dynamic_cast<const CylinderVolumeBounds*>(&cylStack.volumeBounds());
0751 BOOST_REQUIRE(cylBounds != nullptr);
0752 BOOST_CHECK_EQUAL(*cylBounds, *originalBounds);
0753 };
0754
0755
0756 BOOST_CHECK_THROW(
0757 cylStack.update(gctx, newBounds,
0758 trf * Translation3{Vector3{0, 0, f * 20_mm}}, *logger),
0759 std::invalid_argument);
0760 checkUnchanged();
0761
0762
0763 BOOST_CHECK_THROW(
0764 cylStack.update(gctx, newBounds, trf * Translation3{Vector3{10_mm, 0, 0}},
0765 *logger),
0766 std::invalid_argument);
0767 checkUnchanged();
0768
0769
0770 BOOST_CHECK_THROW(
0771 cylStack.update(gctx, newBounds, trf * Translation3{Vector3{0, 10_mm, 0}},
0772 *logger),
0773 std::invalid_argument);
0774 checkUnchanged();
0775
0776
0777 BOOST_CHECK_THROW(
0778 cylStack.update(gctx, newBounds,
0779 trf * AngleAxis3{10_degree, Vector3::UnitY()}, *logger),
0780 std::invalid_argument);
0781 checkUnchanged();
0782
0783 cylStack.update(gctx, newBounds, trf, *logger);
0784
0785 BOOST_CHECK_EQUAL(cylStack.localToGlobalTransform(gctx).matrix(),
0786 trf.matrix());
0787 const auto* cylBounds =
0788 dynamic_cast<const CylinderVolumeBounds*>(&cylStack.volumeBounds());
0789 BOOST_REQUIRE(cylBounds != nullptr);
0790 BOOST_CHECK_EQUAL(cylBounds->get(CylinderVolumeBounds::eHalfLengthZ), 950_mm);
0791
0792
0793 for (const auto* vol : volumes) {
0794 const auto* volBounds =
0795 dynamic_cast<const CylinderVolumeBounds*>(&vol->volumeBounds());
0796 BOOST_REQUIRE(volBounds != nullptr);
0797 BOOST_CHECK_EQUAL(volBounds->get(CylinderVolumeBounds::eMinR), 100_mm);
0798 BOOST_CHECK_EQUAL(volBounds->get(CylinderVolumeBounds::eMaxR), 300_mm);
0799 }
0800
0801 if (strategy == VolumeResizeStrategy::Expand) {
0802
0803 BOOST_CHECK_EQUAL(volumes.size(), 3);
0804 const Volume* vol = nullptr;
0805 if (f < 0.0) {
0806
0807 vol = volumes.front();
0808 } else {
0809
0810 vol = volumes.back();
0811 }
0812
0813 const auto* volBounds =
0814 dynamic_cast<const CylinderVolumeBounds*>(&vol->volumeBounds());
0815 BOOST_REQUIRE(volBounds != nullptr);
0816 BOOST_CHECK_EQUAL(volBounds->get(CylinderVolumeBounds::eHalfLengthZ),
0817 450_mm);
0818 BOOST_CHECK_EQUAL(vol->center(gctx)[eZ], f * 550_mm);
0819 } else if (strategy == VolumeResizeStrategy::Gap) {
0820
0821 BOOST_CHECK_EQUAL(volumes.size(), 4);
0822
0823 const Volume* gap = nullptr;
0824 if (f < 0.0) {
0825 gap = volumes.front();
0826 } else {
0827 gap = volumes.back();
0828 }
0829 const auto* gapBounds =
0830 dynamic_cast<const CylinderVolumeBounds*>(&gap->volumeBounds());
0831 BOOST_REQUIRE(gapBounds != nullptr);
0832
0833 BOOST_CHECK_EQUAL(gapBounds->get(CylinderVolumeBounds::eHalfLengthZ),
0834 50_mm);
0835 BOOST_CHECK_EQUAL(gap->center(gctx)[eZ], f * 950_mm);
0836 }
0837 }
0838
0839 BOOST_AUTO_TEST_CASE(ResizeReproduction1) {
0840 const auto gctx = Acts::GeometryContext::dangerouslyDefaultConstruct();
0841 Transform3 trf1 =
0842 Transform3::Identity() * Translation3{Vector3::UnitZ() * -2000};
0843 auto bounds1 = std::make_shared<CylinderVolumeBounds>(70, 100, 100.0);
0844 Volume vol1{trf1, bounds1};
0845
0846 std::vector<Volume*> volumes = {&vol1};
0847 CylinderVolumeStack stack(gctx, volumes, AxisDirection::AxisZ,
0848 VolumeAttachmentStrategy::Gap,
0849 VolumeResizeStrategy::Gap, *logger);
0850
0851 Transform3 trf2 =
0852 Transform3::Identity() * Translation3{Vector3::UnitZ() * -1500};
0853 stack.update(gctx, std::make_shared<CylinderVolumeBounds>(30.0, 100, 600),
0854 trf2, *logger);
0855
0856 std::cout << stack.volumeBounds() << std::endl;
0857 std::cout << stack.localToGlobalTransform(gctx).matrix() << std::endl;
0858
0859 Transform3 trf3 =
0860 Transform3::Identity() * Translation3{Vector3::UnitZ() * -1600};
0861 stack.update(gctx, std::make_shared<CylinderVolumeBounds>(30.0, 100, 700),
0862 trf3, *logger);
0863 }
0864
0865 BOOST_AUTO_TEST_CASE(ResizeReproduction2) {
0866 const auto gctx = Acts::GeometryContext::dangerouslyDefaultConstruct();
0867
0868 Transform3 trf1 =
0869 Transform3::Identity() * Translation3{Vector3::UnitZ() * 263};
0870 auto bounds1 = std::make_shared<CylinderVolumeBounds>(30, 100, 4.075);
0871 Volume vol1{trf1, bounds1};
0872
0873 std::vector<Volume*> volumes = {&vol1};
0874 CylinderVolumeStack stack(gctx, volumes, AxisDirection::AxisZ,
0875 VolumeAttachmentStrategy::Gap,
0876 VolumeResizeStrategy::Gap, *logger);
0877
0878 Transform3 trf2 =
0879 Transform3::Identity() * Translation3{Vector3::UnitZ() * 260.843};
0880 stack.update(gctx, std::make_shared<CylinderVolumeBounds>(30.0, 100, 6.232),
0881 trf2, *logger);
0882
0883 std::cout << stack.volumeBounds() << std::endl;
0884 std::cout << stack.localToGlobalTransform(gctx).matrix() << std::endl;
0885
0886 Transform3 trf3 =
0887 Transform3::Identity() * Translation3{Vector3::UnitZ() * 1627.31};
0888 stack.update(gctx,
0889 std::make_shared<CylinderVolumeBounds>(30.0, 100, 1372.699),
0890 trf3, *logger);
0891 }
0892
0893
0894
0895
0896
0897
0898
0899
0900
0901
0902
0903
0904
0905
0906
0907
0908
0909
0910
0911
0912
0913
0914
0915
0916
0917
0918
0919
0920
0921 BOOST_AUTO_TEST_CASE(ResizeGapMultiple) {
0922 const auto gctx = Acts::GeometryContext::dangerouslyDefaultConstruct();
0923 Transform3 trf = Transform3::Identity();
0924 auto bounds = std::make_shared<CylinderVolumeBounds>(70, 100, 100.0);
0925 Volume vol{trf, bounds};
0926
0927 BOOST_TEST_CONTEXT("Positive") {
0928 std::vector<Volume*> volumes = {&vol};
0929 CylinderVolumeStack stack(gctx, volumes, AxisDirection::AxisZ,
0930 VolumeAttachmentStrategy::Gap,
0931 VolumeResizeStrategy::Gap, *logger);
0932
0933 BOOST_CHECK_EQUAL(volumes.size(), 1);
0934 BOOST_CHECK(stack.gaps().empty());
0935
0936 stack.update(gctx, std::make_shared<CylinderVolumeBounds>(30.0, 100, 200),
0937 trf * Translation3{Vector3::UnitZ() * 100}, *logger);
0938 BOOST_CHECK_EQUAL(volumes.size(), 2);
0939 BOOST_CHECK_EQUAL(stack.gaps().size(), 1);
0940
0941 BOOST_CHECK_EQUAL(stack.gaps().front()->center(gctx)[eZ], 200.0);
0942 const auto* cylBounds = dynamic_cast<const CylinderVolumeBounds*>(
0943 &stack.gaps().front()->volumeBounds());
0944 BOOST_REQUIRE_NE(cylBounds, nullptr);
0945 BOOST_CHECK_EQUAL(cylBounds->get(CylinderVolumeBounds::eHalfLengthZ),
0946 100.0);
0947
0948 stack.update(gctx, std::make_shared<CylinderVolumeBounds>(30.0, 100, 300),
0949 trf * Translation3{Vector3::UnitZ() * 200}, *logger);
0950
0951 BOOST_CHECK_EQUAL(volumes.size(), 2);
0952
0953 BOOST_CHECK_EQUAL(stack.gaps().size(), 1);
0954
0955 BOOST_CHECK_EQUAL(stack.gaps().front()->center(gctx)[eZ], 300.0);
0956 cylBounds = dynamic_cast<const CylinderVolumeBounds*>(
0957 &stack.gaps().front()->volumeBounds());
0958 BOOST_REQUIRE_NE(cylBounds, nullptr);
0959 BOOST_CHECK_EQUAL(cylBounds->get(CylinderVolumeBounds::eHalfLengthZ),
0960 200.0);
0961 }
0962
0963 BOOST_TEST_CONTEXT("Negative") {
0964 std::vector<Volume*> volumes = {&vol};
0965 CylinderVolumeStack stack(gctx, volumes, AxisDirection::AxisZ,
0966 VolumeAttachmentStrategy::Gap,
0967 VolumeResizeStrategy::Gap, *logger);
0968
0969 BOOST_CHECK_EQUAL(volumes.size(), 1);
0970 BOOST_CHECK(stack.gaps().empty());
0971
0972 stack.update(gctx, std::make_shared<CylinderVolumeBounds>(30.0, 100, 200),
0973 trf * Translation3{Vector3::UnitZ() * -100}, *logger);
0974 BOOST_CHECK_EQUAL(volumes.size(), 2);
0975 BOOST_CHECK_EQUAL(stack.gaps().size(), 1);
0976
0977 BOOST_CHECK_EQUAL(stack.gaps().front()->center(gctx)[eZ], -200.0);
0978 const auto* cylBounds = dynamic_cast<const CylinderVolumeBounds*>(
0979 &stack.gaps().front()->volumeBounds());
0980 BOOST_REQUIRE_NE(cylBounds, nullptr);
0981 BOOST_CHECK_EQUAL(cylBounds->get(CylinderVolumeBounds::eHalfLengthZ),
0982 100.0);
0983
0984 stack.update(gctx, std::make_shared<CylinderVolumeBounds>(30.0, 100, 300),
0985 trf * Translation3{Vector3::UnitZ() * -200}, *logger);
0986
0987 BOOST_CHECK_EQUAL(volumes.size(), 2);
0988
0989 BOOST_CHECK_EQUAL(stack.gaps().size(), 1);
0990
0991 BOOST_CHECK_EQUAL(stack.gaps().front()->center(gctx)[eZ], -300.0);
0992 cylBounds = dynamic_cast<const CylinderVolumeBounds*>(
0993 &stack.gaps().front()->volumeBounds());
0994 BOOST_REQUIRE_NE(cylBounds, nullptr);
0995 BOOST_CHECK_EQUAL(cylBounds->get(CylinderVolumeBounds::eHalfLengthZ),
0996 200.0);
0997 }
0998 }
0999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010 BOOST_AUTO_TEST_CASE(ResizeSubToleranceBoundaryNoDegenerateGap) {
1011 const auto gctx = Acts::GeometryContext::dangerouslyDefaultConstruct();
1012
1013
1014 const double subTol = 1e-6;
1015
1016 auto checkNoDegenerateGap = [&](const CylinderVolumeStack& stack) {
1017 for (const auto& gap : stack.gaps()) {
1018 const auto* cylBounds =
1019 dynamic_cast<const CylinderVolumeBounds*>(&gap->volumeBounds());
1020 BOOST_REQUIRE_NE(cylBounds, nullptr);
1021 BOOST_CHECK_GT(cylBounds->get(CylinderVolumeBounds::eHalfLengthZ),
1022 s_onSurfaceTolerance);
1023 }
1024 };
1025
1026
1027
1028 BOOST_TEST_CONTEXT("NegativeBoundaryCoincides") {
1029
1030 Volume vol{Transform3::Identity(),
1031 std::make_shared<CylinderVolumeBounds>(70, 100, 100.0)};
1032 std::vector<Volume*> volumes = {&vol};
1033 CylinderVolumeStack stack(gctx, volumes, AxisDirection::AxisZ,
1034 VolumeAttachmentStrategy::Gap,
1035 VolumeResizeStrategy::Gap, *logger);
1036 BOOST_CHECK(stack.gaps().empty());
1037
1038
1039
1040 const double newMinZ = -100.0 - subTol;
1041 const double newMaxZ = 500.0;
1042 const double newHlZ = (newMaxZ - newMinZ) / 2.0;
1043 const double newMidZ = (newMaxZ + newMinZ) / 2.0;
1044 stack.update(
1045 gctx, std::make_shared<CylinderVolumeBounds>(70, 100, newHlZ),
1046 Transform3::Identity() * Translation3{Vector3::UnitZ() * newMidZ},
1047 *logger);
1048
1049
1050
1051 BOOST_CHECK_EQUAL(stack.gaps().size(), 1);
1052 checkNoDegenerateGap(stack);
1053 }
1054
1055
1056 BOOST_TEST_CONTEXT("PositiveBoundaryCoincides") {
1057 Volume vol{Transform3::Identity(),
1058 std::make_shared<CylinderVolumeBounds>(70, 100, 100.0)};
1059 std::vector<Volume*> volumes = {&vol};
1060 CylinderVolumeStack stack(gctx, volumes, AxisDirection::AxisZ,
1061 VolumeAttachmentStrategy::Gap,
1062 VolumeResizeStrategy::Gap, *logger);
1063 BOOST_CHECK(stack.gaps().empty());
1064
1065
1066 const double newMinZ = -500.0;
1067 const double newMaxZ = 100.0 + subTol;
1068 const double newHlZ = (newMaxZ - newMinZ) / 2.0;
1069 const double newMidZ = (newMaxZ + newMinZ) / 2.0;
1070 stack.update(
1071 gctx, std::make_shared<CylinderVolumeBounds>(70, 100, newHlZ),
1072 Transform3::Identity() * Translation3{Vector3::UnitZ() * newMidZ},
1073 *logger);
1074
1075 BOOST_CHECK_EQUAL(stack.gaps().size(), 1);
1076 checkNoDegenerateGap(stack);
1077 }
1078 }
1079
1080 BOOST_AUTO_TEST_SUITE_END()
1081
1082 BOOST_AUTO_TEST_SUITE(RDirection)
1083
1084 BOOST_DATA_TEST_CASE(Baseline,
1085 (boost::unit_test::data::xrange(-135, 180, 45) *
1086 boost::unit_test::data::xrange(0, 2, 1) *
1087 boost::unit_test::data::make(-0.1, 0.0, 0.1) *
1088 boost::unit_test::data::make(Vector3{0_mm, 0_mm, 0_mm},
1089 Vector3{20_mm, 0_mm, 0_mm},
1090 Vector3{0_mm, 20_mm, 0_mm},
1091 Vector3{20_mm, 20_mm, 0_mm},
1092 Vector3{0_mm, 0_mm, 20_mm}) *
1093 boost::unit_test::data::make(strategies)),
1094 angle, rotate, f, offset, strategy) {
1095 const auto gctx = Acts::GeometryContext::dangerouslyDefaultConstruct();
1096 double hlZ = 400_mm;
1097
1098 double fInner = 1.0 + f;
1099 double fOuter = 1.0 - f;
1100
1101
1102 auto bounds1 = std::make_shared<CylinderVolumeBounds>(fInner * 100_mm,
1103 fOuter * 300_mm, hlZ);
1104 auto bounds2 = std::make_shared<CylinderVolumeBounds>(fInner * 300_mm,
1105 fOuter * 600_mm, hlZ);
1106 auto bounds3 = std::make_shared<CylinderVolumeBounds>(fInner * 600_mm,
1107 fOuter * 900_mm, hlZ);
1108
1109 Transform3 base =
1110 AngleAxis3(angle * 1_degree, Vector3::UnitX()) * Translation3(offset);
1111
1112
1113
1114 Transform3 transform1 = base;
1115 transform1.translate(Vector3{0_mm, 0_mm, 20_mm});
1116 auto vol1 = std::make_shared<Volume>(transform1, bounds1);
1117
1118 Transform3 transform2 = base;
1119 transform2.translate(Vector3{0_mm, 0_mm, -30_mm});
1120 auto vol2 = std::make_shared<Volume>(transform2, bounds2);
1121
1122 Transform3 transform3 = base;
1123 transform3.translate(Vector3{0_mm, 0_mm, 40_mm});
1124 auto vol3 = std::make_shared<Volume>(transform3, bounds3);
1125
1126 std::vector<Volume*> volumes = {vol1.get(), vol2.get(), vol3.get()};
1127
1128 std::rotate(volumes.begin(), volumes.begin() + rotate, volumes.end());
1129
1130 std::vector<Volume*> origVolumes = volumes;
1131
1132 std::vector<CylinderVolumeBounds> originalBounds;
1133 std::transform(
1134 volumes.begin(), volumes.end(), std::back_inserter(originalBounds),
1135 [](const auto& vol) {
1136 return dynamic_cast<const CylinderVolumeBounds&>(vol->volumeBounds());
1137 });
1138
1139 if (f < 0.0) {
1140 BOOST_CHECK_THROW(
1141 CylinderVolumeStack(gctx, volumes, AxisDirection::AxisR, strategy,
1142 VolumeResizeStrategy::Gap, *logger),
1143 std::invalid_argument);
1144 return;
1145 }
1146
1147 CylinderVolumeStack cylStack(gctx, volumes, AxisDirection::AxisR, strategy,
1148 VolumeResizeStrategy::Gap, *logger);
1149
1150 auto stackBounds =
1151 dynamic_cast<const CylinderVolumeBounds*>(&cylStack.volumeBounds());
1152 BOOST_REQUIRE(stackBounds != nullptr);
1153
1154 BOOST_CHECK_EQUAL(stackBounds->get(CylinderVolumeBounds::eMinR),
1155 fInner * 100_mm);
1156 BOOST_CHECK_EQUAL(stackBounds->get(CylinderVolumeBounds::eMaxR),
1157 fOuter * 900_mm);
1158 double expectedHalfLengthZ = (40_mm + 30_mm + 2 * hlZ) / 2.0;
1159 BOOST_CHECK_EQUAL(stackBounds->get(CylinderVolumeBounds::eHalfLengthZ),
1160 expectedHalfLengthZ);
1161
1162
1163
1164
1165 Transform3 commonTransform = base * Translation3{0_mm, 0_mm, 5_mm};
1166
1167 CHECK_CLOSE_OR_SMALL(cylStack.localToGlobalTransform(gctx).matrix(),
1168 commonTransform.matrix(), 1e-10, 1e-14);
1169
1170 for (const auto& volume : volumes) {
1171 const auto* cylinderBounds =
1172 dynamic_cast<const CylinderVolumeBounds*>(&volume->volumeBounds());
1173 BOOST_REQUIRE(cylinderBounds != nullptr);
1174 BOOST_CHECK_EQUAL(cylinderBounds->get(CylinderVolumeBounds::eHalfLengthZ),
1175 expectedHalfLengthZ);
1176 }
1177
1178 BOOST_CHECK_EQUAL(
1179 dynamic_cast<const CylinderVolumeBounds&>(vol1->volumeBounds())
1180 .get(CylinderVolumeBounds::eMinR),
1181 fInner * 100_mm);
1182
1183 BOOST_CHECK_EQUAL(
1184 dynamic_cast<const CylinderVolumeBounds&>(vol3->volumeBounds())
1185 .get(CylinderVolumeBounds::eMaxR),
1186 fOuter * 900_mm);
1187
1188
1189 for (std::size_t i = 0; i < volumes.size() - 1; ++i) {
1190 const auto& a = volumes.at(i);
1191 const auto& b = volumes.at(i + 1);
1192
1193 const auto* aBounds =
1194 dynamic_cast<const CylinderVolumeBounds*>(&a->volumeBounds());
1195 const auto* bBounds =
1196 dynamic_cast<const CylinderVolumeBounds*>(&b->volumeBounds());
1197
1198 double aMidR = (aBounds->get(CylinderVolumeBounds::eMinR) +
1199 aBounds->get(CylinderVolumeBounds::eMaxR)) /
1200 2.0;
1201
1202 double bMidR = (bBounds->get(CylinderVolumeBounds::eMinR) +
1203 bBounds->get(CylinderVolumeBounds::eMaxR)) /
1204 2.0;
1205
1206 BOOST_CHECK_LT(aMidR, bMidR);
1207 }
1208
1209 if (f == 0.0) {
1210
1211 BOOST_CHECK_EQUAL(volumes.size(), 3);
1212
1213
1214 for (const auto& [volume, origCylBounds] :
1215 zip(origVolumes, originalBounds)) {
1216 const auto* newBounds =
1217 dynamic_cast<const CylinderVolumeBounds*>(&volume->volumeBounds());
1218 BOOST_CHECK_EQUAL(newBounds->get(CylinderVolumeBounds::eMinR),
1219 origCylBounds.get(CylinderVolumeBounds::eMinR));
1220 BOOST_CHECK_EQUAL(newBounds->get(CylinderVolumeBounds::eMaxR),
1221 origCylBounds.get(CylinderVolumeBounds::eMaxR));
1222 }
1223 } else {
1224 const auto* newBounds1 =
1225 dynamic_cast<const CylinderVolumeBounds*>(&vol1->volumeBounds());
1226 const auto* newBounds2 =
1227 dynamic_cast<const CylinderVolumeBounds*>(&vol2->volumeBounds());
1228 const auto* newBounds3 =
1229 dynamic_cast<const CylinderVolumeBounds*>(&vol3->volumeBounds());
1230 if (strategy == VolumeAttachmentStrategy::Gap) {
1231
1232 BOOST_CHECK_EQUAL(volumes.size(), 5);
1233
1234
1235 BOOST_CHECK_EQUAL(newBounds1->get(CylinderVolumeBounds::eMinR),
1236 fInner * 100_mm);
1237 BOOST_CHECK_EQUAL(newBounds1->get(CylinderVolumeBounds::eMaxR),
1238 fOuter * 300_mm);
1239 BOOST_CHECK_EQUAL(newBounds2->get(CylinderVolumeBounds::eMinR),
1240 fInner * 300_mm);
1241 BOOST_CHECK_EQUAL(newBounds2->get(CylinderVolumeBounds::eMaxR),
1242 fOuter * 600_mm);
1243 BOOST_CHECK_EQUAL(newBounds3->get(CylinderVolumeBounds::eMinR),
1244 fInner * 600_mm);
1245 BOOST_CHECK_EQUAL(newBounds3->get(CylinderVolumeBounds::eMaxR),
1246 fOuter * 900_mm);
1247
1248 auto gap1 = volumes.at(1);
1249 auto gap2 = volumes.at(3);
1250
1251 const auto* gapBounds1 =
1252 dynamic_cast<const CylinderVolumeBounds*>(&gap1->volumeBounds());
1253 const auto* gapBounds2 =
1254 dynamic_cast<const CylinderVolumeBounds*>(&gap2->volumeBounds());
1255
1256 BOOST_CHECK_EQUAL(gapBounds1->get(CylinderVolumeBounds::eMinR),
1257 fOuter * 300_mm);
1258 BOOST_CHECK_EQUAL(gapBounds1->get(CylinderVolumeBounds::eMaxR),
1259 fInner * 300_mm);
1260 BOOST_CHECK_EQUAL(gapBounds2->get(CylinderVolumeBounds::eMinR),
1261 fOuter * 600_mm);
1262 BOOST_CHECK_EQUAL(gapBounds2->get(CylinderVolumeBounds::eMaxR),
1263 fInner * 600_mm);
1264
1265 } else if (strategy == VolumeAttachmentStrategy::First) {
1266
1267 BOOST_CHECK_EQUAL(volumes.size(), 3);
1268
1269
1270 BOOST_CHECK_EQUAL(newBounds1->get(CylinderVolumeBounds::eMinR),
1271 fInner * 100_mm);
1272 BOOST_CHECK_EQUAL(newBounds1->get(CylinderVolumeBounds::eMaxR),
1273 fInner * 300_mm);
1274
1275
1276 BOOST_CHECK_EQUAL(newBounds2->get(CylinderVolumeBounds::eMinR),
1277 fInner * 300_mm);
1278 BOOST_CHECK_EQUAL(newBounds2->get(CylinderVolumeBounds::eMaxR),
1279 fInner * 600_mm);
1280
1281
1282 BOOST_CHECK_EQUAL(newBounds3->get(CylinderVolumeBounds::eMinR),
1283 fInner * 600_mm);
1284 BOOST_CHECK_EQUAL(newBounds3->get(CylinderVolumeBounds::eMaxR),
1285 fOuter * 900_mm);
1286
1287 } else if (strategy == VolumeAttachmentStrategy::Second) {
1288
1289 BOOST_CHECK_EQUAL(volumes.size(), 3);
1290
1291
1292 BOOST_CHECK_EQUAL(newBounds1->get(CylinderVolumeBounds::eMinR),
1293 fInner * 100_mm);
1294 BOOST_CHECK_EQUAL(newBounds1->get(CylinderVolumeBounds::eMaxR),
1295 fOuter * 300_mm);
1296
1297
1298 BOOST_CHECK_EQUAL(newBounds2->get(CylinderVolumeBounds::eMinR),
1299 fOuter * 300_mm);
1300 BOOST_CHECK_EQUAL(newBounds2->get(CylinderVolumeBounds::eMaxR),
1301 fOuter * 600_mm);
1302
1303
1304 BOOST_CHECK_EQUAL(newBounds3->get(CylinderVolumeBounds::eMinR),
1305 fOuter * 600_mm);
1306 BOOST_CHECK_EQUAL(newBounds3->get(CylinderVolumeBounds::eMaxR),
1307 fOuter * 900_mm);
1308 } else if (strategy == VolumeAttachmentStrategy::Midpoint) {
1309
1310 BOOST_CHECK_EQUAL(volumes.size(), 3);
1311
1312
1313 BOOST_CHECK_EQUAL(newBounds1->get(CylinderVolumeBounds::eMinR),
1314 fInner * 100_mm);
1315 BOOST_CHECK_EQUAL(newBounds1->get(CylinderVolumeBounds::eMaxR),
1316 (fOuter * 300_mm + fInner * 300_mm) / 2.0);
1317
1318
1319 BOOST_CHECK_EQUAL(newBounds2->get(CylinderVolumeBounds::eMinR),
1320 (fOuter * 300_mm + fInner * 300_mm) / 2.0);
1321 BOOST_CHECK_EQUAL(newBounds2->get(CylinderVolumeBounds::eMaxR),
1322 (fOuter * 600_mm + fInner * 600_mm) / 2.0);
1323
1324
1325 BOOST_CHECK_EQUAL(newBounds3->get(CylinderVolumeBounds::eMinR),
1326 (fOuter * 600_mm + fInner * 600_mm) / 2.0);
1327 BOOST_CHECK_EQUAL(newBounds3->get(CylinderVolumeBounds::eMaxR),
1328 fOuter * 900_mm);
1329 }
1330 }
1331 }
1332
1333 BOOST_DATA_TEST_CASE(UpdateStack,
1334 (boost::unit_test::data::xrange(-135, 180, 45) *
1335 boost::unit_test::data::make(Vector3{0_mm, 0_mm, 0_mm},
1336 Vector3{20_mm, 0_mm, 0_mm},
1337 Vector3{0_mm, 20_mm, 0_mm},
1338 Vector3{20_mm, 20_mm, 0_mm},
1339 Vector3{0_mm, 0_mm, 20_mm}) *
1340 boost::unit_test::data::make(-100_mm, 0_mm, 100_mm) *
1341 boost::unit_test::data::make(resizeStrategies)),
1342 angle, offset, zshift, strategy) {
1343 const auto gctx = Acts::GeometryContext::dangerouslyDefaultConstruct();
1344 double hlZ = 400_mm;
1345
1346
1347 auto bounds1 = std::make_shared<CylinderVolumeBounds>(100_mm, 300_mm, hlZ);
1348 auto bounds2 = std::make_shared<CylinderVolumeBounds>(300_mm, 600_mm, hlZ);
1349 auto bounds3 = std::make_shared<CylinderVolumeBounds>(600_mm, 900_mm, hlZ);
1350
1351 Transform3 base = AngleAxis3(angle * 1_degree, Vector3::UnitX()) *
1352 Translation3(offset + Vector3{0, 0, zshift});
1353
1354
1355 auto vol1 = std::make_shared<Volume>(base, bounds1);
1356 auto vol2 = std::make_shared<Volume>(base, bounds2);
1357 auto vol3 = std::make_shared<Volume>(base, bounds3);
1358
1359 std::vector<Volume*> volumes = {vol1.get(), vol2.get(), vol3.get()};
1360 std::vector<Volume*> originalVolumes = volumes;
1361
1362 std::vector<CylinderVolumeBounds> originalBounds;
1363
1364 std::transform(
1365 volumes.begin(), volumes.end(), std::back_inserter(originalBounds),
1366 [](const auto& vol) {
1367 return *dynamic_cast<const CylinderVolumeBounds*>(&vol->volumeBounds());
1368 });
1369
1370 const CylinderVolumeBounds* originalOuterBounds = nullptr;
1371
1372 std::unique_ptr<CylinderVolumeStack> cylStack;
1373
1374 auto resetCylStack = [&]() {
1375 volumes = originalVolumes;
1376
1377 for (const auto& [volume, origBounds] : zip(volumes, originalBounds)) {
1378 volume->assignVolumeBounds(
1379 std::make_shared<CylinderVolumeBounds>(origBounds));
1380 }
1381
1382 cylStack = std::make_unique<CylinderVolumeStack>(
1383 gctx, volumes, AxisDirection::AxisR,
1384 VolumeAttachmentStrategy::Gap,
1385
1386 strategy, *logger);
1387
1388 originalOuterBounds =
1389 dynamic_cast<const CylinderVolumeBounds*>(&cylStack->volumeBounds());
1390 };
1391
1392 resetCylStack();
1393
1394 auto assertInitialVolumesUnchanged = [&]() {
1395 for (const auto& [volume, origCylBounds] :
1396 zip(originalVolumes, originalBounds)) {
1397 const auto* newBounds =
1398 dynamic_cast<const CylinderVolumeBounds*>(&volume->volumeBounds());
1399 BOOST_CHECK_EQUAL(newBounds->get(CylinderVolumeBounds::eMinR),
1400 origCylBounds.get(CylinderVolumeBounds::eMinR));
1401 BOOST_CHECK_EQUAL(newBounds->get(CylinderVolumeBounds::eMaxR),
1402 origCylBounds.get(CylinderVolumeBounds::eMaxR));
1403 BOOST_CHECK_EQUAL(newBounds->get(CylinderVolumeBounds::eHalfLengthZ),
1404 origCylBounds.get(CylinderVolumeBounds::eHalfLengthZ));
1405 BOOST_CHECK_EQUAL(volume->localToGlobalTransform(gctx).matrix(),
1406 base.matrix());
1407 }
1408 };
1409
1410 auto assertOriginalBounds = [&]() {
1411 const auto* cylBounds =
1412 dynamic_cast<const CylinderVolumeBounds*>(&cylStack->volumeBounds());
1413 BOOST_REQUIRE(cylBounds != nullptr);
1414 BOOST_CHECK_EQUAL(cylBounds, originalOuterBounds);
1415 BOOST_CHECK_EQUAL(cylBounds->get(CylinderVolumeBounds::eMinR), 100_mm);
1416 BOOST_CHECK_EQUAL(cylBounds->get(CylinderVolumeBounds::eMaxR), 900_mm);
1417 BOOST_CHECK_EQUAL(cylBounds->get(CylinderVolumeBounds::eHalfLengthZ), hlZ);
1418 };
1419
1420 assertOriginalBounds();
1421
1422 {
1423
1424 auto bounds = std::make_shared<CylinderVolumeBounds>(
1425 dynamic_cast<const CylinderVolumeBounds&>(cylStack->volumeBounds()));
1426 cylStack->update(gctx, bounds, std::nullopt, *logger);
1427 assertOriginalBounds();
1428 }
1429
1430 {
1431
1432 auto bounds = std::make_shared<CylinderVolumeBounds>(
1433 dynamic_cast<const CylinderVolumeBounds&>(cylStack->volumeBounds()));
1434 bounds->set(CylinderVolumeBounds::eMinR, 200_mm);
1435 BOOST_CHECK_THROW(cylStack->update(gctx, bounds, std::nullopt, *logger),
1436 std::invalid_argument);
1437 assertOriginalBounds();
1438 }
1439
1440 {
1441
1442 auto bounds = std::make_shared<CylinderVolumeBounds>(
1443 dynamic_cast<const CylinderVolumeBounds&>(cylStack->volumeBounds()));
1444 bounds->set(CylinderVolumeBounds::eMaxR, 500_mm);
1445 BOOST_CHECK_THROW(cylStack->update(gctx, bounds, std::nullopt, *logger),
1446 std::invalid_argument);
1447 assertOriginalBounds();
1448 }
1449
1450 {
1451
1452 auto bounds = std::make_shared<CylinderVolumeBounds>(
1453 dynamic_cast<const CylinderVolumeBounds&>(cylStack->volumeBounds()));
1454 bounds->set(CylinderVolumeBounds::eHalfLengthZ, 0.5 * hlZ);
1455 BOOST_CHECK_THROW(cylStack->update(gctx, bounds, std::nullopt, *logger),
1456 std::invalid_argument);
1457 assertOriginalBounds();
1458 }
1459
1460 {
1461
1462 auto bounds = std::make_shared<CylinderVolumeBounds>(
1463 dynamic_cast<const CylinderVolumeBounds&>(cylStack->volumeBounds()));
1464 bounds->set(CylinderVolumeBounds::eMinR, 50_mm);
1465 cylStack->update(gctx, bounds, std::nullopt, *logger);
1466 const auto* cylBounds =
1467 dynamic_cast<const CylinderVolumeBounds*>(&cylStack->volumeBounds());
1468 BOOST_REQUIRE(cylBounds != nullptr);
1469 BOOST_CHECK_EQUAL(cylBounds->get(CylinderVolumeBounds::eMinR), 50_mm);
1470
1471 BOOST_CHECK_EQUAL(cylBounds->get(CylinderVolumeBounds::eMaxR), 900_mm);
1472 BOOST_CHECK_EQUAL(cylBounds->get(CylinderVolumeBounds::eHalfLengthZ), hlZ);
1473
1474 if (strategy == VolumeResizeStrategy::Expand) {
1475
1476 BOOST_CHECK_EQUAL(volumes.size(), 3);
1477
1478
1479 const auto* newBounds1 =
1480 dynamic_cast<const CylinderVolumeBounds*>(&vol1->volumeBounds());
1481 BOOST_CHECK_EQUAL(newBounds1->get(CylinderVolumeBounds::eMinR), 50_mm);
1482
1483 BOOST_CHECK_EQUAL(vol1->localToGlobalTransform(gctx).matrix(),
1484 base.matrix());
1485
1486
1487 const auto* newBounds2 =
1488 dynamic_cast<const CylinderVolumeBounds*>(&vol2->volumeBounds());
1489 BOOST_CHECK_EQUAL(*newBounds2, originalBounds[1]);
1490 BOOST_CHECK_EQUAL(vol2->localToGlobalTransform(gctx).matrix(),
1491 base.matrix());
1492
1493 const auto* newBounds3 =
1494 dynamic_cast<const CylinderVolumeBounds*>(&vol3->volumeBounds());
1495 BOOST_CHECK_EQUAL(*newBounds3, originalBounds[2]);
1496 BOOST_CHECK_EQUAL(vol3->localToGlobalTransform(gctx).matrix(),
1497 base.matrix());
1498
1499 } else if (strategy == VolumeResizeStrategy::Gap) {
1500
1501 BOOST_CHECK_EQUAL(volumes.size(), 4);
1502
1503 auto gap = volumes.front();
1504 auto gapBounds =
1505 dynamic_cast<const CylinderVolumeBounds*>(&gap->volumeBounds());
1506 BOOST_REQUIRE(gapBounds != nullptr);
1507 BOOST_CHECK_EQUAL(gapBounds->get(CylinderVolumeBounds::eMinR), 50_mm);
1508 BOOST_CHECK_EQUAL(gapBounds->get(CylinderVolumeBounds::eMaxR), 100_mm);
1509 BOOST_CHECK_EQUAL(gapBounds->get(CylinderVolumeBounds::eHalfLengthZ),
1510 hlZ);
1511 BOOST_CHECK_EQUAL(gap->localToGlobalTransform(gctx).matrix(),
1512 base.matrix());
1513
1514
1515 assertInitialVolumesUnchanged();
1516 }
1517 }
1518
1519 resetCylStack();
1520
1521 {
1522
1523 auto bounds = std::make_shared<CylinderVolumeBounds>(
1524 dynamic_cast<const CylinderVolumeBounds&>(cylStack->volumeBounds()));
1525 bounds->set(CylinderVolumeBounds::eMaxR, 1000_mm);
1526 cylStack->update(gctx, bounds, std::nullopt, *logger);
1527 const auto* cylBounds =
1528 dynamic_cast<const CylinderVolumeBounds*>(&cylStack->volumeBounds());
1529 BOOST_REQUIRE(cylBounds != nullptr);
1530 BOOST_CHECK_EQUAL(cylBounds->get(CylinderVolumeBounds::eMaxR), 1000_mm);
1531
1532 BOOST_CHECK_EQUAL(cylBounds->get(CylinderVolumeBounds::eMinR), 100_mm);
1533 BOOST_CHECK_EQUAL(cylBounds->get(CylinderVolumeBounds::eHalfLengthZ), hlZ);
1534
1535 if (strategy == VolumeResizeStrategy::Expand) {
1536
1537 BOOST_CHECK_EQUAL(volumes.size(), 3);
1538
1539
1540 const auto* newBounds3 =
1541 dynamic_cast<const CylinderVolumeBounds*>(&vol3->volumeBounds());
1542 BOOST_CHECK_EQUAL(newBounds3->get(CylinderVolumeBounds::eMaxR), 1000_mm);
1543
1544 BOOST_CHECK_EQUAL(vol3->localToGlobalTransform(gctx).matrix(),
1545 base.matrix());
1546
1547
1548 const auto* newBounds1 =
1549 dynamic_cast<const CylinderVolumeBounds*>(&vol1->volumeBounds());
1550 BOOST_CHECK_EQUAL(*newBounds1, originalBounds[0]);
1551 BOOST_CHECK_EQUAL(vol1->localToGlobalTransform(gctx).matrix(),
1552 base.matrix());
1553
1554 const auto* newBounds2 =
1555 dynamic_cast<const CylinderVolumeBounds*>(&vol2->volumeBounds());
1556 BOOST_CHECK_EQUAL(*newBounds2, originalBounds[1]);
1557 BOOST_CHECK_EQUAL(vol2->localToGlobalTransform(gctx).matrix(),
1558 base.matrix());
1559
1560 } else if (strategy == VolumeResizeStrategy::Gap) {
1561
1562 BOOST_CHECK_EQUAL(volumes.size(), 4);
1563
1564 auto gap = volumes.back();
1565 auto gapBounds =
1566 dynamic_cast<const CylinderVolumeBounds*>(&gap->volumeBounds());
1567 BOOST_REQUIRE(gapBounds != nullptr);
1568 BOOST_CHECK_EQUAL(gapBounds->get(CylinderVolumeBounds::eMinR), 900_mm);
1569 BOOST_CHECK_EQUAL(gapBounds->get(CylinderVolumeBounds::eMaxR), 1000_mm);
1570 BOOST_CHECK_EQUAL(gapBounds->get(CylinderVolumeBounds::eHalfLengthZ),
1571 hlZ);
1572 BOOST_CHECK_EQUAL(gap->localToGlobalTransform(gctx).matrix(),
1573 base.matrix());
1574
1575
1576 assertInitialVolumesUnchanged();
1577 }
1578 }
1579
1580 resetCylStack();
1581
1582 {
1583
1584 auto bounds = std::make_shared<CylinderVolumeBounds>(
1585 dynamic_cast<const CylinderVolumeBounds&>(cylStack->volumeBounds()));
1586 bounds->set({
1587 {CylinderVolumeBounds::eMinR, 0_mm},
1588 {CylinderVolumeBounds::eMaxR, 1100_mm},
1589 });
1590
1591 cylStack->update(gctx, bounds, std::nullopt, *logger);
1592 const auto* cylBounds =
1593 dynamic_cast<const CylinderVolumeBounds*>(&cylStack->volumeBounds());
1594 BOOST_REQUIRE(cylBounds != nullptr);
1595 BOOST_CHECK_EQUAL(cylBounds->get(CylinderVolumeBounds::eMaxR), 1100_mm);
1596
1597 BOOST_CHECK_EQUAL(cylBounds->get(CylinderVolumeBounds::eMinR), 0_mm);
1598 BOOST_CHECK_EQUAL(cylBounds->get(CylinderVolumeBounds::eHalfLengthZ), hlZ);
1599
1600 if (strategy == VolumeResizeStrategy::Expand) {
1601
1602 BOOST_CHECK_EQUAL(volumes.size(), 3);
1603
1604
1605 const auto* newBounds1 =
1606 dynamic_cast<const CylinderVolumeBounds*>(&vol1->volumeBounds());
1607 BOOST_CHECK_EQUAL(newBounds1->get(CylinderVolumeBounds::eMinR), 0_mm);
1608
1609 BOOST_CHECK_EQUAL(vol1->localToGlobalTransform(gctx).matrix(),
1610 base.matrix());
1611
1612
1613 const auto* newBounds2 =
1614 dynamic_cast<const CylinderVolumeBounds*>(&vol2->volumeBounds());
1615 BOOST_CHECK_EQUAL(*newBounds2, originalBounds[1]);
1616 BOOST_CHECK_EQUAL(vol2->localToGlobalTransform(gctx).matrix(),
1617 base.matrix());
1618
1619
1620 const auto* newBounds3 =
1621 dynamic_cast<const CylinderVolumeBounds*>(&vol3->volumeBounds());
1622 BOOST_CHECK_EQUAL(newBounds3->get(CylinderVolumeBounds::eMaxR), 1100_mm);
1623
1624 BOOST_CHECK_EQUAL(vol3->localToGlobalTransform(gctx).matrix(),
1625 base.matrix());
1626
1627 } else if (strategy == VolumeResizeStrategy::Gap) {
1628
1629 BOOST_CHECK_EQUAL(volumes.size(), 5);
1630
1631 auto gap1 = volumes.front();
1632 auto gapBounds1 =
1633 dynamic_cast<const CylinderVolumeBounds*>(&gap1->volumeBounds());
1634 BOOST_REQUIRE(gapBounds1 != nullptr);
1635 BOOST_CHECK_EQUAL(gapBounds1->get(CylinderVolumeBounds::eMinR), 0_mm);
1636 BOOST_CHECK_EQUAL(gapBounds1->get(CylinderVolumeBounds::eMaxR), 100_mm);
1637 BOOST_CHECK_EQUAL(gapBounds1->get(CylinderVolumeBounds::eHalfLengthZ),
1638 hlZ);
1639 BOOST_CHECK_EQUAL(gap1->localToGlobalTransform(gctx).matrix(),
1640 base.matrix());
1641
1642 auto gap2 = volumes.back();
1643 auto gapBounds2 =
1644 dynamic_cast<const CylinderVolumeBounds*>(&gap2->volumeBounds());
1645 BOOST_REQUIRE(gapBounds2 != nullptr);
1646 BOOST_CHECK_EQUAL(gapBounds2->get(CylinderVolumeBounds::eMinR), 900_mm);
1647 BOOST_CHECK_EQUAL(gapBounds2->get(CylinderVolumeBounds::eMaxR), 1100_mm);
1648 BOOST_CHECK_EQUAL(gapBounds2->get(CylinderVolumeBounds::eHalfLengthZ),
1649 hlZ);
1650
1651
1652 assertInitialVolumesUnchanged();
1653 }
1654 }
1655
1656 resetCylStack();
1657
1658 {
1659
1660 auto bounds = std::make_shared<CylinderVolumeBounds>(
1661 dynamic_cast<const CylinderVolumeBounds&>(cylStack->volumeBounds()));
1662 bounds->set(CylinderVolumeBounds::eHalfLengthZ, 2 * hlZ);
1663 cylStack->update(gctx, bounds, std::nullopt, *logger);
1664 const auto* cylBounds =
1665 dynamic_cast<const CylinderVolumeBounds*>(&cylStack->volumeBounds());
1666 BOOST_REQUIRE(cylBounds != nullptr);
1667 BOOST_CHECK_EQUAL(cylBounds->get(CylinderVolumeBounds::eHalfLengthZ),
1668 2 * hlZ);
1669
1670
1671 BOOST_CHECK_EQUAL(cylBounds->get(CylinderVolumeBounds::eMinR), 100_mm);
1672 BOOST_CHECK_EQUAL(cylBounds->get(CylinderVolumeBounds::eMaxR), 900_mm);
1673
1674
1675 BOOST_CHECK_EQUAL(volumes.size(), 3);
1676
1677 for (const auto& [volume, origCylBounds] :
1678 zip(originalVolumes, originalBounds)) {
1679 const auto* newBounds =
1680 dynamic_cast<const CylinderVolumeBounds*>(&volume->volumeBounds());
1681
1682 BOOST_CHECK_EQUAL(newBounds->get(CylinderVolumeBounds::eMinR),
1683 origCylBounds.get(CylinderVolumeBounds::eMinR));
1684 BOOST_CHECK_EQUAL(newBounds->get(CylinderVolumeBounds::eMaxR),
1685 origCylBounds.get(CylinderVolumeBounds::eMaxR));
1686
1687
1688 BOOST_CHECK_EQUAL(newBounds->get(CylinderVolumeBounds::eHalfLengthZ),
1689 2 * hlZ);
1690
1691
1692 BOOST_CHECK_EQUAL(volume->localToGlobalTransform(gctx).matrix(),
1693 base.matrix());
1694 }
1695 }
1696 }
1697
1698 BOOST_DATA_TEST_CASE(
1699 UpdateStackOneSided,
1700 (boost::unit_test::data::make(-1.0, 1.0) ^
1701 boost::unit_test::data::make(VolumeResizeStrategy::Gap,
1702 VolumeResizeStrategy::Expand)),
1703 f, strategy) {
1704 const auto gctx = Acts::GeometryContext::dangerouslyDefaultConstruct();
1705
1706
1707 auto trf = Transform3::Identity();
1708
1709 auto vol1 = std::make_shared<Volume>(
1710 trf, std::make_shared<CylinderVolumeBounds>(100_mm, 300_mm, 400_mm));
1711
1712 auto vol2 = std::make_shared<Volume>(
1713 trf, std::make_shared<CylinderVolumeBounds>(400_mm, 600_mm, 400_mm));
1714
1715 std::vector<Volume*> volumes = {vol1.get(), vol2.get()};
1716
1717 CylinderVolumeStack cylStack{
1718 gctx, volumes, AxisDirection::AxisR, VolumeAttachmentStrategy::Gap,
1719 strategy, *logger};
1720 const auto* originalBounds =
1721 dynamic_cast<const CylinderVolumeBounds*>(&cylStack.volumeBounds());
1722
1723
1724 auto newBounds = std::make_shared<CylinderVolumeBounds>(
1725 dynamic_cast<const CylinderVolumeBounds&>(cylStack.volumeBounds()));
1726 newBounds->set(CylinderVolumeBounds::eHalfLengthZ, 450_mm);
1727
1728 trf *= Translation3{Vector3{0_mm, 0_mm, f * 50_mm}};
1729
1730
1731 auto checkUnchanged = [&]() {
1732 const auto* cylBounds =
1733 dynamic_cast<const CylinderVolumeBounds*>(&cylStack.volumeBounds());
1734 BOOST_REQUIRE(cylBounds != nullptr);
1735 BOOST_CHECK_EQUAL(*cylBounds, *originalBounds);
1736 };
1737
1738
1739 BOOST_CHECK_THROW(
1740 cylStack.update(gctx, newBounds,
1741 trf * Translation3{Vector3{0, 0, f * 20_mm}}, *logger),
1742 std::invalid_argument);
1743 checkUnchanged();
1744
1745
1746 BOOST_CHECK_THROW(
1747 cylStack.update(gctx, newBounds, trf * Translation3{Vector3{10_mm, 0, 0}},
1748 *logger),
1749 std::invalid_argument);
1750 checkUnchanged();
1751
1752
1753 BOOST_CHECK_THROW(
1754 cylStack.update(gctx, newBounds, trf * Translation3{Vector3{0, 10_mm, 0}},
1755 *logger),
1756 std::invalid_argument);
1757 checkUnchanged();
1758
1759
1760 BOOST_CHECK_THROW(
1761 cylStack.update(gctx, newBounds,
1762 trf * AngleAxis3{10_degree, Vector3::UnitY()}, *logger),
1763 std::invalid_argument);
1764 checkUnchanged();
1765
1766 cylStack.update(gctx, newBounds, trf, *logger);
1767
1768 BOOST_CHECK_EQUAL(cylStack.localToGlobalTransform(gctx).matrix(),
1769 trf.matrix());
1770 const auto* cylBounds =
1771 dynamic_cast<const CylinderVolumeBounds*>(&cylStack.volumeBounds());
1772 BOOST_REQUIRE(cylBounds != nullptr);
1773 BOOST_CHECK_EQUAL(cylBounds->get(CylinderVolumeBounds::eMinR), 100_mm);
1774 BOOST_CHECK_EQUAL(cylBounds->get(CylinderVolumeBounds::eMaxR), 600_mm);
1775
1776
1777 for (const auto* vol : volumes) {
1778 const auto* volBounds =
1779 dynamic_cast<const CylinderVolumeBounds*>(&vol->volumeBounds());
1780 BOOST_REQUIRE(volBounds != nullptr);
1781 BOOST_CHECK_EQUAL(vol->localToGlobalTransform(gctx).matrix(), trf.matrix());
1782 BOOST_CHECK_EQUAL(volBounds->get(CylinderVolumeBounds::eHalfLengthZ),
1783 450_mm);
1784 }
1785 }
1786
1787 BOOST_AUTO_TEST_CASE(ResizeGapMultiple) {
1788 const auto gctx = Acts::GeometryContext::dangerouslyDefaultConstruct();
1789 Transform3 trf = Transform3::Identity();
1790 auto bounds = std::make_shared<CylinderVolumeBounds>(100, 200, 100);
1791 Volume vol{trf, bounds};
1792
1793 BOOST_TEST_CONTEXT("Outer") {
1794 std::vector<Volume*> volumes = {&vol};
1795 CylinderVolumeStack stack(gctx, volumes, AxisDirection::AxisR,
1796 VolumeAttachmentStrategy::Gap,
1797 VolumeResizeStrategy::Gap, *logger);
1798
1799 BOOST_CHECK_EQUAL(volumes.size(), 1);
1800 BOOST_CHECK(stack.gaps().empty());
1801
1802 stack.update(gctx, std::make_shared<CylinderVolumeBounds>(100, 250, 100),
1803 trf, *logger);
1804 BOOST_CHECK_EQUAL(volumes.size(), 2);
1805 BOOST_CHECK_EQUAL(stack.gaps().size(), 1);
1806
1807 const auto* cylBounds = dynamic_cast<const CylinderVolumeBounds*>(
1808 &stack.gaps().front()->volumeBounds());
1809 BOOST_REQUIRE_NE(cylBounds, nullptr);
1810 BOOST_CHECK_EQUAL(cylBounds->get(CylinderVolumeBounds::eMinR), 200);
1811 BOOST_CHECK_EQUAL(cylBounds->get(CylinderVolumeBounds::eMaxR), 250);
1812
1813 stack.update(gctx, std::make_shared<CylinderVolumeBounds>(100, 300, 100),
1814 trf, *logger);
1815
1816 BOOST_CHECK_EQUAL(volumes.size(), 2);
1817
1818 BOOST_CHECK_EQUAL(stack.gaps().size(), 1);
1819
1820 cylBounds = dynamic_cast<const CylinderVolumeBounds*>(
1821 &stack.gaps().front()->volumeBounds());
1822 BOOST_REQUIRE_NE(cylBounds, nullptr);
1823 BOOST_CHECK_EQUAL(cylBounds->get(CylinderVolumeBounds::eMinR), 200);
1824 BOOST_CHECK_EQUAL(cylBounds->get(CylinderVolumeBounds::eMaxR), 300);
1825 }
1826
1827 BOOST_TEST_CONTEXT("Inner") {
1828 std::vector<Volume*> volumes = {&vol};
1829 CylinderVolumeStack stack(gctx, volumes, AxisDirection::AxisR,
1830 VolumeAttachmentStrategy::Gap,
1831 VolumeResizeStrategy::Gap, *logger);
1832
1833 BOOST_CHECK_EQUAL(volumes.size(), 1);
1834 BOOST_CHECK(stack.gaps().empty());
1835
1836 stack.update(gctx, std::make_shared<CylinderVolumeBounds>(50, 200, 100),
1837 trf, *logger);
1838 BOOST_CHECK_EQUAL(volumes.size(), 2);
1839 BOOST_CHECK_EQUAL(stack.gaps().size(), 1);
1840
1841 const auto* cylBounds = dynamic_cast<const CylinderVolumeBounds*>(
1842 &stack.gaps().front()->volumeBounds());
1843 BOOST_REQUIRE_NE(cylBounds, nullptr);
1844 BOOST_CHECK_EQUAL(cylBounds->get(CylinderVolumeBounds::eMinR), 50);
1845 BOOST_CHECK_EQUAL(cylBounds->get(CylinderVolumeBounds::eMaxR), 100);
1846
1847 stack.update(gctx, std::make_shared<CylinderVolumeBounds>(0, 200, 100), trf,
1848 *logger);
1849
1850 BOOST_CHECK_EQUAL(volumes.size(), 2);
1851
1852 BOOST_CHECK_EQUAL(stack.gaps().size(), 1);
1853
1854 cylBounds = dynamic_cast<const CylinderVolumeBounds*>(
1855 &stack.gaps().front()->volumeBounds());
1856 BOOST_REQUIRE_NE(cylBounds, nullptr);
1857 BOOST_CHECK_EQUAL(cylBounds->get(CylinderVolumeBounds::eMinR), 0);
1858 BOOST_CHECK_EQUAL(cylBounds->get(CylinderVolumeBounds::eMaxR), 100);
1859 }
1860 }
1861
1862 BOOST_AUTO_TEST_SUITE_END()
1863
1864 BOOST_AUTO_TEST_SUITE(Common)
1865
1866 BOOST_DATA_TEST_CASE(JoinCylinderVolumesInvalidDirection,
1867 boost::unit_test::data::make(strategies), strategy) {
1868 const auto gctx = Acts::GeometryContext::dangerouslyDefaultConstruct();
1869 std::vector<Volume*> volumes;
1870 auto vol1 = std::make_shared<Volume>(
1871 Transform3::Identity(),
1872 std::make_shared<CylinderVolumeBounds>(100_mm, 400_mm, 400_mm));
1873 volumes.push_back(vol1.get());
1874
1875
1876 BOOST_CHECK_THROW(
1877 CylinderVolumeStack(gctx, volumes, AxisDirection::AxisY, strategy),
1878 std::invalid_argument);
1879
1880 auto vol2 = std::make_shared<Volume>(
1881 Transform3::Identity(),
1882 std::make_shared<CylinderVolumeBounds>(100_mm, 400_mm, 400_mm));
1883 volumes.push_back(vol2.get());
1884
1885 BOOST_CHECK_THROW(
1886 CylinderVolumeStack(gctx, volumes, AxisDirection::AxisY, strategy),
1887 std::invalid_argument);
1888 }
1889
1890 BOOST_DATA_TEST_CASE(JoinCylinderVolumesInvalidInput,
1891 (boost::unit_test::data::make(strategies) *
1892 boost::unit_test::data::make(AxisDirection::AxisZ,
1893 AxisDirection::AxisR)),
1894 strategy, direction) {
1895 const auto gctx = Acts::GeometryContext::dangerouslyDefaultConstruct();
1896 BOOST_TEST_CONTEXT("Empty Volume") {
1897 std::vector<Volume*> volumes;
1898 BOOST_CHECK_THROW(CylinderVolumeStack(gctx, volumes, direction, strategy),
1899 std::invalid_argument);
1900 }
1901
1902 BOOST_TEST_CONTEXT("Volumes rotated relative to each other") {
1903
1904 for (const Vector3 axis : {Vector3::UnitX(), Vector3::UnitY()}) {
1905 std::vector<Volume*> volumes;
1906 auto vol1 = std::make_shared<Volume>(
1907 Transform3{Translation3{Vector3{0_mm, 0_mm, -500_mm}}},
1908 std::make_shared<CylinderVolumeBounds>(100_mm, 400_mm, 400_mm));
1909 volumes.push_back(vol1.get());
1910
1911 BOOST_TEST_MESSAGE("Axis: " << axis);
1912 auto vol2 = std::make_shared<Volume>(
1913 Transform3{Translation3{Vector3{0_mm, 0_mm, 500_mm}} *
1914 AngleAxis3(1_degree, axis)},
1915 std::make_shared<CylinderVolumeBounds>(100_mm, 400_mm, 400_mm));
1916 volumes.push_back(vol2.get());
1917
1918 BOOST_CHECK_THROW(CylinderVolumeStack(gctx, volumes, direction, strategy,
1919 VolumeResizeStrategy::Gap, *logger),
1920 std::invalid_argument);
1921 }
1922 }
1923
1924 BOOST_TEST_CONTEXT("Volumes shifted in the xy plane relative to each other") {
1925 for (const Vector3& shift :
1926 {Vector3{5_mm, 0, 0}, Vector3{0, -5_mm, 0}, Vector3{2_mm, -2_mm, 0}}) {
1927 std::vector<Volume*> volumes;
1928 auto vol1 = std::make_shared<Volume>(
1929 Transform3{Translation3{Vector3{0_mm, 0_mm, -500_mm}}},
1930 std::make_shared<CylinderVolumeBounds>(100_mm, 400_mm, 400_mm));
1931 volumes.push_back(vol1.get());
1932
1933 auto vol2 = std::make_shared<Volume>(
1934 Transform3{Translation3{Vector3{0_mm, 0_mm, 500_mm} + shift}},
1935 std::make_shared<CylinderVolumeBounds>(100_mm, 400_mm, 400_mm));
1936 volumes.push_back(vol2.get());
1937
1938 BOOST_CHECK_THROW(CylinderVolumeStack(gctx, volumes, direction, strategy,
1939 VolumeResizeStrategy::Gap, *logger),
1940 std::invalid_argument);
1941 }
1942 }
1943
1944 BOOST_TEST_CONTEXT("Volume has phi values") {
1945 std::vector<std::shared_ptr<CylinderVolumeBounds>> invalidVolumeBounds = {
1946 std::make_shared<CylinderVolumeBounds>(100_mm, 400_mm, 400_mm,
1947 0.2 * std::numbers::pi),
1948
1949 std::make_shared<CylinderVolumeBounds>(
1950 100_mm, 400_mm, 400_mm, std::numbers::pi, 0.3 * std::numbers::pi),
1951 };
1952
1953 for (const auto& invalid : invalidVolumeBounds) {
1954 std::stringstream ss;
1955 ss << "Invalid bounds: " << *invalid;
1956 BOOST_TEST_CONTEXT(ss.str()) {
1957 std::vector<Volume*> volumes;
1958 auto vol1 = std::make_shared<Volume>(
1959 Transform3{Translation3{Vector3{0_mm, 0_mm, -500_mm}}},
1960 std::make_shared<CylinderVolumeBounds>(100_mm, 400_mm, 400_mm));
1961 volumes.push_back(vol1.get());
1962
1963 {
1964
1965 CylinderVolumeStack cylStack(gctx, volumes, direction, strategy,
1966 VolumeResizeStrategy::Gap, *logger);
1967 BOOST_CHECK_THROW(
1968 cylStack.update(gctx, invalid, std::nullopt, *logger),
1969 std::invalid_argument);
1970 }
1971
1972 {
1973 std::shared_ptr<Volume> vol;
1974 if (direction == AxisDirection::AxisZ) {
1975 vol = std::make_shared<Volume>(
1976 Transform3{Translation3{Vector3{0_mm, 0_mm, 500_mm}}}, invalid);
1977 } else {
1978 invalid->set({
1979 {CylinderVolumeBounds::eMinR, 400_mm},
1980 {CylinderVolumeBounds::eMaxR, 600_mm},
1981 });
1982 vol = std::make_shared<Volume>(
1983 Transform3{Translation3{Vector3{0_mm, 0_mm, 0_mm}}}, invalid);
1984 }
1985 volumes.push_back(vol.get());
1986 BOOST_CHECK_THROW(
1987 CylinderVolumeStack(gctx, volumes, direction, strategy,
1988 VolumeResizeStrategy::Gap, *logger),
1989 std::invalid_argument);
1990 }
1991 }
1992 }
1993 }
1994 }
1995
1996 BOOST_DATA_TEST_CASE(JoinCylinderVolumeSingle,
1997 (boost::unit_test::data::make(AxisDirection::AxisZ,
1998 AxisDirection::AxisR) *
1999 boost::unit_test::data::make(strategies)),
2000 direction, strategy) {
2001 const auto gctx = Acts::GeometryContext::dangerouslyDefaultConstruct();
2002 auto vol = std::make_shared<Volume>(
2003 Transform3::Identity() * Translation3{14_mm, 24_mm, 0_mm} *
2004 AngleAxis3(73_degree, Vector3::UnitX()),
2005 std::make_shared<CylinderVolumeBounds>(100_mm, 400_mm, 400_mm));
2006
2007 std::vector<Volume*> volumes{vol.get()};
2008
2009 CylinderVolumeStack cylStack(gctx, volumes, direction, strategy,
2010 VolumeResizeStrategy::Gap, *logger);
2011
2012
2013
2014 BOOST_CHECK_EQUAL(volumes.size(), 1);
2015 BOOST_CHECK_EQUAL(volumes.at(0), vol.get());
2016 BOOST_CHECK_EQUAL(vol->localToGlobalTransform(gctx).matrix(),
2017 cylStack.localToGlobalTransform(gctx).matrix());
2018 BOOST_CHECK_EQUAL(vol->volumeBounds(), cylStack.volumeBounds());
2019 }
2020
2021 BOOST_AUTO_TEST_SUITE_END()
2022 BOOST_AUTO_TEST_SUITE_END()
2023 BOOST_AUTO_TEST_CASE(AsymmetricResizeZ) {
2024 const auto gctx = Acts::GeometryContext::dangerouslyDefaultConstruct();
2025 double hlZ = 400_mm;
2026 double rMin = 100_mm;
2027 double rMax = 200_mm;
2028
2029
2030 auto bounds1 = std::make_shared<CylinderVolumeBounds>(rMin, rMax, hlZ);
2031 auto bounds2 = std::make_shared<CylinderVolumeBounds>(rMin, rMax, hlZ);
2032 auto bounds3 = std::make_shared<CylinderVolumeBounds>(rMin, rMax, hlZ);
2033
2034 Transform3 transform1 = Transform3::Identity();
2035 transform1.translate(Vector3{0_mm, 0_mm, -2 * hlZ});
2036 auto vol1 = std::make_shared<Volume>(transform1, bounds1);
2037
2038 Transform3 transform2 = Transform3::Identity();
2039 transform2.translate(Vector3{0_mm, 0_mm, 0_mm});
2040 auto vol2 = std::make_shared<Volume>(transform2, bounds2);
2041
2042 Transform3 transform3 = Transform3::Identity();
2043 transform3.translate(Vector3{0_mm, 0_mm, 2 * hlZ});
2044 auto vol3 = std::make_shared<Volume>(transform3, bounds3);
2045
2046 std::vector<Volume*> volumes = {vol1.get(), vol2.get(), vol3.get()};
2047
2048 CylinderVolumeStack cylStack(
2049 gctx, volumes, AxisDirection::AxisZ, VolumeAttachmentStrategy::Gap,
2050 {VolumeResizeStrategy::Gap, VolumeResizeStrategy::Expand}, *logger);
2051
2052
2053
2054 auto newBounds = std::make_shared<CylinderVolumeBounds>(rMin, rMax, 4 * hlZ);
2055 Transform3 newTransform =
2056 Transform3::Identity() * Translation3{0_mm, 0_mm, 0_mm};
2057
2058 cylStack.update(gctx, newBounds, newTransform, *logger);
2059
2060
2061 BOOST_CHECK_EQUAL(volumes.size(), 4);
2062
2063
2064 auto gapVol = volumes.front();
2065 auto gapBounds =
2066 dynamic_cast<const CylinderVolumeBounds*>(&gapVol->volumeBounds());
2067 BOOST_REQUIRE(gapBounds != nullptr);
2068 BOOST_CHECK_EQUAL(
2069 gapBounds->get(CylinderVolumeBounds::eHalfLengthZ),
2070 hlZ / 2);
2071 BOOST_CHECK_EQUAL(gapBounds->get(CylinderVolumeBounds::eMinR), rMin);
2072 BOOST_CHECK_EQUAL(gapBounds->get(CylinderVolumeBounds::eMaxR), rMax);
2073 BOOST_CHECK_CLOSE(gapVol->center(gctx)[eZ], -3.5 * hlZ,
2074 1e-10);
2075
2076
2077 auto* lastVol = volumes.back();
2078 BOOST_CHECK_EQUAL(lastVol, vol3.get());
2079 auto lastBounds =
2080 dynamic_cast<const CylinderVolumeBounds*>(&lastVol->volumeBounds());
2081 BOOST_REQUIRE(lastBounds != nullptr);
2082 BOOST_CHECK_EQUAL(lastBounds->get(CylinderVolumeBounds::eHalfLengthZ),
2083 1.5 * hlZ);
2084 BOOST_CHECK_EQUAL(lastBounds->get(CylinderVolumeBounds::eMinR), rMin);
2085 BOOST_CHECK_EQUAL(lastBounds->get(CylinderVolumeBounds::eMaxR), rMax);
2086 BOOST_CHECK_CLOSE(lastVol->center(gctx)[eZ], 2.5 * hlZ,
2087 1e-10);
2088
2089
2090 for (std::size_t i = 1; i < volumes.size() - 1; i++) {
2091 auto volBounds =
2092 dynamic_cast<const CylinderVolumeBounds*>(&volumes[i]->volumeBounds());
2093 BOOST_REQUIRE(volBounds != nullptr);
2094 BOOST_CHECK_EQUAL(volBounds->get(CylinderVolumeBounds::eHalfLengthZ), hlZ);
2095 BOOST_CHECK_EQUAL(volBounds->get(CylinderVolumeBounds::eMinR), rMin);
2096 BOOST_CHECK_EQUAL(volBounds->get(CylinderVolumeBounds::eMaxR), rMax);
2097 }
2098 BOOST_CHECK_CLOSE(volumes[1]->center(gctx)[eZ], -2 * hlZ, 1e-10);
2099 BOOST_CHECK_CLOSE(volumes[2]->center(gctx)[eZ], 0, 1e-10);
2100 }
2101
2102 BOOST_AUTO_TEST_CASE(AsymmetricResizeZFlipped) {
2103 const auto gctx = Acts::GeometryContext::dangerouslyDefaultConstruct();
2104
2105 double hlZ = 400_mm;
2106 double rMin = 100_mm;
2107 double rMax = 200_mm;
2108
2109
2110 auto bounds1 = std::make_shared<CylinderVolumeBounds>(rMin, rMax, hlZ);
2111 auto bounds2 = std::make_shared<CylinderVolumeBounds>(rMin, rMax, hlZ);
2112 auto bounds3 = std::make_shared<CylinderVolumeBounds>(rMin, rMax, hlZ);
2113
2114 Transform3 transform1 = Transform3::Identity() * Translation3(0, 0, -2 * hlZ);
2115 Transform3 transform2 = Transform3::Identity();
2116 Transform3 transform3 = Transform3::Identity() * Translation3(0, 0, 2 * hlZ);
2117
2118 auto vol1 = std::make_shared<Volume>(transform1, bounds1);
2119 auto vol2 = std::make_shared<Volume>(transform2, bounds2);
2120 auto vol3 = std::make_shared<Volume>(transform3, bounds3);
2121
2122 std::vector<Volume*> volumes = {vol1.get(), vol2.get(), vol3.get()};
2123
2124 CylinderVolumeStack cylStack(
2125 gctx, volumes, AxisDirection::AxisZ, VolumeAttachmentStrategy::Gap,
2126 {VolumeResizeStrategy::Expand, VolumeResizeStrategy::Gap}, *logger);
2127
2128
2129 auto newBounds = std::make_shared<CylinderVolumeBounds>(rMin, rMax, 4 * hlZ);
2130 cylStack.update(gctx, newBounds, std::nullopt, *logger);
2131
2132 BOOST_CHECK_EQUAL(volumes.size(), 4);
2133
2134
2135 auto gapVol = volumes.back();
2136 auto gapBounds =
2137 dynamic_cast<const CylinderVolumeBounds*>(&gapVol->volumeBounds());
2138 BOOST_REQUIRE(gapBounds != nullptr);
2139 BOOST_CHECK_EQUAL(
2140 gapBounds->get(CylinderVolumeBounds::eHalfLengthZ),
2141 hlZ / 2);
2142 BOOST_CHECK_EQUAL(gapBounds->get(CylinderVolumeBounds::eMinR), rMin);
2143 BOOST_CHECK_EQUAL(gapBounds->get(CylinderVolumeBounds::eMaxR), rMax);
2144 BOOST_CHECK_CLOSE(gapVol->center(gctx)[eZ], 3.5 * hlZ,
2145 1e-10);
2146
2147
2148 auto* firstVol = volumes.front();
2149 BOOST_CHECK_EQUAL(firstVol, vol1.get());
2150 auto firstBounds =
2151 dynamic_cast<const CylinderVolumeBounds*>(&firstVol->volumeBounds());
2152 BOOST_REQUIRE(firstBounds != nullptr);
2153 BOOST_CHECK_EQUAL(firstBounds->get(CylinderVolumeBounds::eHalfLengthZ),
2154 1.5 * hlZ);
2155 BOOST_CHECK_EQUAL(firstBounds->get(CylinderVolumeBounds::eMinR), rMin);
2156 BOOST_CHECK_EQUAL(firstBounds->get(CylinderVolumeBounds::eMaxR), rMax);
2157 BOOST_CHECK_CLOSE(firstVol->center(gctx)[eZ], -2.5 * hlZ,
2158 1e-10);
2159
2160
2161 for (std::size_t i = 1; i < volumes.size() - 1; i++) {
2162 auto volBounds =
2163 dynamic_cast<const CylinderVolumeBounds*>(&volumes[i]->volumeBounds());
2164 BOOST_REQUIRE(volBounds != nullptr);
2165 BOOST_CHECK_EQUAL(volBounds->get(CylinderVolumeBounds::eHalfLengthZ), hlZ);
2166 BOOST_CHECK_EQUAL(volBounds->get(CylinderVolumeBounds::eMinR), rMin);
2167 BOOST_CHECK_EQUAL(volBounds->get(CylinderVolumeBounds::eMaxR), rMax);
2168 }
2169 BOOST_CHECK_CLOSE(volumes[1]->center(gctx)[eZ], 0, 1e-10);
2170 BOOST_CHECK_CLOSE(volumes[2]->center(gctx)[eZ], 2 * hlZ, 1e-10);
2171 }
2172
2173 BOOST_AUTO_TEST_CASE(AsymmetricResizeR) {
2174 const auto gctx = Acts::GeometryContext::dangerouslyDefaultConstruct();
2175 double hlZ = 400_mm;
2176
2177
2178 auto bounds1 = std::make_shared<CylinderVolumeBounds>(100_mm, 200_mm, hlZ);
2179 auto bounds2 = std::make_shared<CylinderVolumeBounds>(200_mm, 300_mm, hlZ);
2180 auto bounds3 = std::make_shared<CylinderVolumeBounds>(300_mm, 400_mm, hlZ);
2181
2182 Transform3 transform = Transform3::Identity();
2183 auto vol1 = std::make_shared<Volume>(transform, bounds1);
2184 auto vol2 = std::make_shared<Volume>(transform, bounds2);
2185 auto vol3 = std::make_shared<Volume>(transform, bounds3);
2186
2187 std::vector<Volume*> volumes = {vol1.get(), vol2.get(), vol3.get()};
2188
2189 CylinderVolumeStack cylStack(
2190 gctx, volumes, AxisDirection::AxisR, VolumeAttachmentStrategy::Midpoint,
2191 {VolumeResizeStrategy::Gap, VolumeResizeStrategy::Expand}, *logger);
2192
2193
2194 auto newBounds = std::make_shared<CylinderVolumeBounds>(50_mm, 500_mm, hlZ);
2195 cylStack.update(gctx, newBounds, std::nullopt, *logger);
2196
2197 BOOST_CHECK_EQUAL(volumes.size(), 4);
2198
2199
2200 auto innerGap = volumes.front();
2201 auto innerGapBounds =
2202 dynamic_cast<const CylinderVolumeBounds*>(&innerGap->volumeBounds());
2203 BOOST_REQUIRE(innerGapBounds != nullptr);
2204 BOOST_CHECK_EQUAL(innerGapBounds->get(CylinderVolumeBounds::eMinR), 50_mm);
2205 BOOST_CHECK_EQUAL(innerGapBounds->get(CylinderVolumeBounds::eMaxR), 100_mm);
2206 BOOST_CHECK_EQUAL(innerGapBounds->get(CylinderVolumeBounds::eHalfLengthZ),
2207 hlZ);
2208
2209
2210 auto* outerVol = volumes.back();
2211 BOOST_CHECK_EQUAL(outerVol, vol3.get());
2212
2213 auto outerBounds =
2214 dynamic_cast<const CylinderVolumeBounds*>(&outerVol->volumeBounds());
2215 BOOST_REQUIRE(outerBounds != nullptr);
2216 BOOST_CHECK_EQUAL(outerBounds->get(CylinderVolumeBounds::eMinR), 300_mm);
2217 BOOST_CHECK_EQUAL(outerBounds->get(CylinderVolumeBounds::eMaxR), 500_mm);
2218 BOOST_CHECK_EQUAL(outerBounds->get(CylinderVolumeBounds::eHalfLengthZ), hlZ);
2219
2220
2221 for (std::size_t i = 1; i < volumes.size() - 1; i++) {
2222 auto volBounds =
2223 dynamic_cast<const CylinderVolumeBounds*>(&volumes[i]->volumeBounds());
2224 BOOST_REQUIRE(volBounds != nullptr);
2225 BOOST_CHECK_EQUAL(volBounds->get(CylinderVolumeBounds::eHalfLengthZ), hlZ);
2226 }
2227 }
2228
2229 BOOST_AUTO_TEST_CASE(AsymmetricResizeRFlipped) {
2230 const auto gctx = Acts::GeometryContext::dangerouslyDefaultConstruct();
2231 double hlZ = 400_mm;
2232
2233
2234 auto bounds1 = std::make_shared<CylinderVolumeBounds>(100_mm, 200_mm, hlZ);
2235 auto bounds2 = std::make_shared<CylinderVolumeBounds>(200_mm, 300_mm, hlZ);
2236 auto bounds3 = std::make_shared<CylinderVolumeBounds>(300_mm, 400_mm, hlZ);
2237
2238 Transform3 transform = Transform3::Identity();
2239 auto vol1 = std::make_shared<Volume>(transform, bounds1);
2240 auto vol2 = std::make_shared<Volume>(transform, bounds2);
2241 auto vol3 = std::make_shared<Volume>(transform, bounds3);
2242
2243 std::vector<Volume*> volumes = {vol1.get(), vol2.get(), vol3.get()};
2244
2245 CylinderVolumeStack cylStack(
2246 gctx, volumes, AxisDirection::AxisR, VolumeAttachmentStrategy::Gap,
2247 {VolumeResizeStrategy::Expand, VolumeResizeStrategy::Gap}, *logger);
2248
2249
2250 auto newBounds = std::make_shared<CylinderVolumeBounds>(50_mm, 500_mm, hlZ);
2251 cylStack.update(gctx, newBounds, std::nullopt, *logger);
2252
2253 BOOST_CHECK_EQUAL(volumes.size(), 4);
2254
2255
2256 auto outerGap = volumes.back();
2257 auto outerGapBounds =
2258 dynamic_cast<const CylinderVolumeBounds*>(&outerGap->volumeBounds());
2259 BOOST_REQUIRE(outerGapBounds != nullptr);
2260 BOOST_CHECK_EQUAL(outerGapBounds->get(CylinderVolumeBounds::eMinR), 400_mm);
2261 BOOST_CHECK_EQUAL(outerGapBounds->get(CylinderVolumeBounds::eMaxR), 500_mm);
2262 BOOST_CHECK_EQUAL(outerGapBounds->get(CylinderVolumeBounds::eHalfLengthZ),
2263 hlZ);
2264
2265
2266 auto* innerVol = volumes.front();
2267 BOOST_CHECK_EQUAL(innerVol, vol1.get());
2268
2269 auto innerBounds =
2270 dynamic_cast<const CylinderVolumeBounds*>(&innerVol->volumeBounds());
2271 BOOST_REQUIRE(innerBounds != nullptr);
2272 BOOST_CHECK_EQUAL(innerBounds->get(CylinderVolumeBounds::eMinR), 50_mm);
2273 BOOST_CHECK_EQUAL(innerBounds->get(CylinderVolumeBounds::eMaxR), 200_mm);
2274 BOOST_CHECK_EQUAL(innerBounds->get(CylinderVolumeBounds::eHalfLengthZ), hlZ);
2275
2276
2277 for (std::size_t i = 1; i < volumes.size() - 1; i++) {
2278 auto volBounds =
2279 dynamic_cast<const CylinderVolumeBounds*>(&volumes[i]->volumeBounds());
2280 BOOST_REQUIRE(volBounds != nullptr);
2281 BOOST_CHECK_EQUAL(volBounds->get(CylinderVolumeBounds::eHalfLengthZ), hlZ);
2282 }
2283 }
2284
2285 BOOST_AUTO_TEST_CASE(AsymmetricSingleSideResizeZ) {
2286 const auto gctx = Acts::GeometryContext::dangerouslyDefaultConstruct();
2287 double hlZ = 400_mm;
2288 double rMin = 100_mm;
2289 double rMax = 200_mm;
2290
2291
2292 auto bounds1 = std::make_shared<CylinderVolumeBounds>(rMin, rMax, hlZ);
2293 auto bounds2 = std::make_shared<CylinderVolumeBounds>(rMin, rMax, hlZ);
2294
2295 Transform3 transform1 = Transform3::Identity();
2296 transform1.translate(Vector3{0_mm, 0_mm, -hlZ});
2297 auto vol1 = std::make_shared<Volume>(transform1, bounds1);
2298
2299 Transform3 transform2 = Transform3::Identity();
2300 transform2.translate(Vector3{0_mm, 0_mm, hlZ});
2301 auto vol2 = std::make_shared<Volume>(transform2, bounds2);
2302
2303 std::vector<Volume*> volumes = {vol1.get(), vol2.get()};
2304
2305
2306 CylinderVolumeStack cylStack(
2307 gctx, volumes, AxisDirection::AxisZ, VolumeAttachmentStrategy::Gap,
2308 {VolumeResizeStrategy::Gap, VolumeResizeStrategy::Expand}, *logger);
2309
2310
2311 auto newBounds = std::make_shared<CylinderVolumeBounds>(rMin, rMax, 3 * hlZ);
2312 Transform3 newTransform =
2313 Transform3::Identity() * Translation3{0_mm, 0_mm, hlZ};
2314 cylStack.update(gctx, newBounds, newTransform, *logger);
2315
2316 auto* firstVol = volumes.front();
2317 BOOST_CHECK_EQUAL(firstVol, vol1.get());
2318 auto firstBounds =
2319 dynamic_cast<const CylinderVolumeBounds*>(&firstVol->volumeBounds());
2320 BOOST_REQUIRE(firstBounds != nullptr);
2321 BOOST_CHECK_EQUAL(firstBounds->get(CylinderVolumeBounds::eHalfLengthZ), hlZ);
2322 BOOST_CHECK_EQUAL(firstBounds->get(CylinderVolumeBounds::eMinR), rMin);
2323 BOOST_CHECK_EQUAL(firstBounds->get(CylinderVolumeBounds::eMaxR), rMax);
2324 BOOST_CHECK_CLOSE(firstVol->center(gctx)[eZ], -hlZ, 1e-10);
2325
2326
2327 auto* lastVol = volumes.back();
2328 BOOST_CHECK_EQUAL(lastVol, vol2.get());
2329 auto lastBounds =
2330 dynamic_cast<const CylinderVolumeBounds*>(&lastVol->volumeBounds());
2331 BOOST_REQUIRE(lastBounds != nullptr);
2332 BOOST_CHECK_EQUAL(lastBounds->get(CylinderVolumeBounds::eHalfLengthZ),
2333 2 * hlZ);
2334 BOOST_CHECK_EQUAL(lastBounds->get(CylinderVolumeBounds::eMinR), rMin);
2335 BOOST_CHECK_EQUAL(lastBounds->get(CylinderVolumeBounds::eMaxR), rMax);
2336 BOOST_CHECK_CLOSE(lastVol->center(gctx)[eZ], 2 * hlZ, 1e-10);
2337
2338
2339 BOOST_CHECK_EQUAL(volumes.size(), 2);
2340 }
2341
2342 BOOST_AUTO_TEST_CASE(AsymmetricSingleSideResizeZFlipped) {
2343 const auto gctx = Acts::GeometryContext::dangerouslyDefaultConstruct();
2344 double hlZ = 400_mm;
2345 double rMin = 100_mm;
2346 double rMax = 200_mm;
2347
2348
2349 auto bounds1 = std::make_shared<CylinderVolumeBounds>(rMin, rMax, hlZ);
2350 auto bounds2 = std::make_shared<CylinderVolumeBounds>(rMin, rMax, hlZ);
2351
2352 Transform3 transform1 = Transform3::Identity();
2353 transform1.translate(Vector3{0_mm, 0_mm, -hlZ});
2354 auto vol1 = std::make_shared<Volume>(transform1, bounds1);
2355
2356 Transform3 transform2 = Transform3::Identity();
2357 transform2.translate(Vector3{0_mm, 0_mm, hlZ});
2358 auto vol2 = std::make_shared<Volume>(transform2, bounds2);
2359
2360 std::vector<Volume*> volumes = {vol1.get(), vol2.get()};
2361
2362 CylinderVolumeStack cylStack(
2363 gctx, volumes, AxisDirection::AxisZ, VolumeAttachmentStrategy::Gap,
2364 {VolumeResizeStrategy::Expand, VolumeResizeStrategy::Gap}, *logger);
2365
2366
2367 auto newBounds = std::make_shared<CylinderVolumeBounds>(rMin, rMax, 3 * hlZ);
2368 Transform3 newTransform =
2369 Transform3::Identity() * Translation3{0_mm, 0_mm, hlZ};
2370 cylStack.update(gctx, newBounds, newTransform, *logger);
2371
2372 auto* firstVol = volumes.front();
2373 BOOST_CHECK_EQUAL(firstVol, vol1.get());
2374 auto firstBounds =
2375 dynamic_cast<const CylinderVolumeBounds*>(&firstVol->volumeBounds());
2376 BOOST_REQUIRE(firstBounds != nullptr);
2377 BOOST_CHECK_EQUAL(firstBounds->get(CylinderVolumeBounds::eHalfLengthZ), hlZ);
2378 BOOST_CHECK_EQUAL(firstBounds->get(CylinderVolumeBounds::eMinR), rMin);
2379 BOOST_CHECK_EQUAL(firstBounds->get(CylinderVolumeBounds::eMaxR), rMax);
2380 BOOST_CHECK_CLOSE(firstVol->center(gctx)[eZ], -hlZ, 1e-10);
2381
2382
2383 auto* midVol = volumes[1];
2384 BOOST_CHECK_EQUAL(midVol, vol2.get());
2385 auto midBounds =
2386 dynamic_cast<const CylinderVolumeBounds*>(&midVol->volumeBounds());
2387 BOOST_REQUIRE(midBounds != nullptr);
2388 BOOST_CHECK_EQUAL(midBounds->get(CylinderVolumeBounds::eHalfLengthZ), hlZ);
2389 BOOST_CHECK_EQUAL(midBounds->get(CylinderVolumeBounds::eMinR), rMin);
2390 BOOST_CHECK_EQUAL(midBounds->get(CylinderVolumeBounds::eMaxR), rMax);
2391 BOOST_CHECK_CLOSE(midVol->center(gctx)[eZ], hlZ, 1e-10);
2392
2393
2394 BOOST_CHECK_EQUAL(volumes.size(), 3);
2395
2396
2397 auto* gapVol = volumes.back();
2398 auto gapBounds =
2399 dynamic_cast<const CylinderVolumeBounds*>(&gapVol->volumeBounds());
2400 BOOST_REQUIRE(gapBounds != nullptr);
2401 BOOST_CHECK_EQUAL(gapBounds->get(CylinderVolumeBounds::eHalfLengthZ), hlZ);
2402 BOOST_CHECK_EQUAL(gapBounds->get(CylinderVolumeBounds::eMinR), rMin);
2403 BOOST_CHECK_EQUAL(gapBounds->get(CylinderVolumeBounds::eMaxR), rMax);
2404 BOOST_CHECK_CLOSE(gapVol->center(gctx)[eZ], 3 * hlZ, 1e-10);
2405 }
2406
2407 BOOST_AUTO_TEST_CASE(AsymmetricSingleSideResizeR) {
2408 const auto gctx = Acts::GeometryContext::dangerouslyDefaultConstruct();
2409 double hlZ = 400_mm;
2410 double rMin1 = 100_mm;
2411 double rMax1 = 200_mm;
2412 double rMin2 = 200_mm;
2413 double rMax2 = 300_mm;
2414
2415
2416 auto bounds1 = std::make_shared<CylinderVolumeBounds>(rMin1, rMax1, hlZ);
2417 auto bounds2 = std::make_shared<CylinderVolumeBounds>(rMin2, rMax2, hlZ);
2418
2419 Transform3 transform = Transform3::Identity();
2420 auto vol1 = std::make_shared<Volume>(transform, bounds1);
2421 auto vol2 = std::make_shared<Volume>(transform, bounds2);
2422
2423 std::vector<Volume*> volumes = {vol1.get(), vol2.get()};
2424
2425
2426 CylinderVolumeStack cylStack(
2427 gctx, volumes, AxisDirection::AxisR, VolumeAttachmentStrategy::Gap,
2428 {VolumeResizeStrategy::Gap, VolumeResizeStrategy::Expand}, *logger);
2429
2430
2431 auto newBounds = std::make_shared<CylinderVolumeBounds>(rMin1, 500_mm, hlZ);
2432 cylStack.update(gctx, newBounds, std::nullopt, *logger);
2433
2434
2435 auto* innerVol = volumes.front();
2436 BOOST_CHECK_EQUAL(innerVol, vol1.get());
2437 auto innerBounds =
2438 dynamic_cast<const CylinderVolumeBounds*>(&innerVol->volumeBounds());
2439 BOOST_REQUIRE(innerBounds != nullptr);
2440 BOOST_CHECK_EQUAL(innerBounds->get(CylinderVolumeBounds::eMinR), rMin1);
2441 BOOST_CHECK_EQUAL(innerBounds->get(CylinderVolumeBounds::eMaxR), rMax1);
2442 BOOST_CHECK_EQUAL(innerBounds->get(CylinderVolumeBounds::eHalfLengthZ), hlZ);
2443
2444
2445 auto* outerVol = volumes.back();
2446 BOOST_CHECK_EQUAL(outerVol, vol2.get());
2447 auto outerBounds =
2448 dynamic_cast<const CylinderVolumeBounds*>(&outerVol->volumeBounds());
2449 BOOST_REQUIRE(outerBounds != nullptr);
2450 BOOST_CHECK_EQUAL(outerBounds->get(CylinderVolumeBounds::eMinR), rMin2);
2451 BOOST_CHECK_EQUAL(outerBounds->get(CylinderVolumeBounds::eMaxR), 500_mm);
2452 BOOST_CHECK_EQUAL(outerBounds->get(CylinderVolumeBounds::eHalfLengthZ), hlZ);
2453
2454
2455 BOOST_CHECK_EQUAL(volumes.size(), 2);
2456 }
2457
2458 BOOST_AUTO_TEST_CASE(AsymmetricSingleSideResizeRFlipped) {
2459 const auto gctx = Acts::GeometryContext::dangerouslyDefaultConstruct();
2460 double hlZ = 400_mm;
2461 double rMin1 = 100_mm;
2462 double rMax1 = 200_mm;
2463 double rMin2 = 200_mm;
2464 double rMax2 = 300_mm;
2465
2466
2467 auto bounds1 = std::make_shared<CylinderVolumeBounds>(rMin1, rMax1, hlZ);
2468 auto bounds2 = std::make_shared<CylinderVolumeBounds>(rMin2, rMax2, hlZ);
2469
2470 Transform3 transform = Transform3::Identity();
2471 auto vol1 = std::make_shared<Volume>(transform, bounds1);
2472 auto vol2 = std::make_shared<Volume>(transform, bounds2);
2473
2474 std::vector<Volume*> volumes = {vol1.get(), vol2.get()};
2475
2476 CylinderVolumeStack cylStack(
2477 gctx, volumes, AxisDirection::AxisR, VolumeAttachmentStrategy::Gap,
2478 {VolumeResizeStrategy::Expand, VolumeResizeStrategy::Gap}, *logger);
2479
2480
2481 auto newBounds = std::make_shared<CylinderVolumeBounds>(rMin1, 500_mm, hlZ);
2482 cylStack.update(gctx, newBounds, std::nullopt, *logger);
2483
2484 auto* innerVol = volumes.front();
2485 BOOST_CHECK_EQUAL(innerVol, vol1.get());
2486 auto innerBounds =
2487 dynamic_cast<const CylinderVolumeBounds*>(&innerVol->volumeBounds());
2488 BOOST_REQUIRE(innerBounds != nullptr);
2489 BOOST_CHECK_EQUAL(innerBounds->get(CylinderVolumeBounds::eMinR), rMin1);
2490 BOOST_CHECK_EQUAL(innerBounds->get(CylinderVolumeBounds::eMaxR), rMax1);
2491 BOOST_CHECK_EQUAL(innerBounds->get(CylinderVolumeBounds::eHalfLengthZ), hlZ);
2492 BOOST_CHECK_CLOSE(innerVol->center(gctx)[eZ], 0, 1e-10);
2493
2494
2495 auto* midVol = volumes[1];
2496 BOOST_CHECK_EQUAL(midVol, vol2.get());
2497 auto midBounds =
2498 dynamic_cast<const CylinderVolumeBounds*>(&midVol->volumeBounds());
2499 BOOST_REQUIRE(midBounds != nullptr);
2500 BOOST_CHECK_EQUAL(midBounds->get(CylinderVolumeBounds::eMinR), rMin2);
2501 BOOST_CHECK_EQUAL(midBounds->get(CylinderVolumeBounds::eMaxR), rMax2);
2502 BOOST_CHECK_EQUAL(midBounds->get(CylinderVolumeBounds::eHalfLengthZ), hlZ);
2503 BOOST_CHECK_CLOSE(midVol->center(gctx)[eZ], 0, 1e-10);
2504
2505
2506 BOOST_CHECK_EQUAL(volumes.size(), 3);
2507
2508
2509 auto* gapVol = volumes.back();
2510 auto gapBounds =
2511 dynamic_cast<const CylinderVolumeBounds*>(&gapVol->volumeBounds());
2512 BOOST_REQUIRE(gapBounds != nullptr);
2513 BOOST_CHECK_EQUAL(gapBounds->get(CylinderVolumeBounds::eHalfLengthZ), hlZ);
2514 BOOST_CHECK_EQUAL(gapBounds->get(CylinderVolumeBounds::eMinR), rMax2);
2515 BOOST_CHECK_EQUAL(gapBounds->get(CylinderVolumeBounds::eMaxR), 500_mm);
2516 BOOST_CHECK_CLOSE(gapVol->center(gctx)[eZ], 0, 1e-10);
2517 }
2518
2519 BOOST_AUTO_TEST_CASE(AsymmetricSingleSideResizeZNegative) {
2520 const auto gctx = Acts::GeometryContext::dangerouslyDefaultConstruct();
2521 double hlZ = 400_mm;
2522 double rMin = 100_mm;
2523 double rMax = 200_mm;
2524
2525
2526 auto bounds1 = std::make_shared<CylinderVolumeBounds>(rMin, rMax, hlZ);
2527 auto bounds2 = std::make_shared<CylinderVolumeBounds>(rMin, rMax, hlZ);
2528
2529 Transform3 transform1 = Transform3::Identity();
2530 transform1.translate(Vector3{0_mm, 0_mm, -hlZ});
2531 auto vol1 = std::make_shared<Volume>(transform1, bounds1);
2532
2533 Transform3 transform2 = Transform3::Identity();
2534 transform2.translate(Vector3{0_mm, 0_mm, hlZ});
2535 auto vol2 = std::make_shared<Volume>(transform2, bounds2);
2536
2537 std::vector<Volume*> volumes = {vol1.get(), vol2.get()};
2538
2539 CylinderVolumeStack cylStack(
2540 gctx, volumes, AxisDirection::AxisZ, VolumeAttachmentStrategy::Gap,
2541 {VolumeResizeStrategy::Expand, VolumeResizeStrategy::Gap}, *logger);
2542
2543
2544 auto newBounds = std::make_shared<CylinderVolumeBounds>(rMin, rMax, 3 * hlZ);
2545 Transform3 newTransform =
2546 Transform3::Identity() * Translation3{0_mm, 0_mm, -hlZ};
2547 cylStack.update(gctx, newBounds, newTransform, *logger);
2548
2549 auto* firstVol = volumes.front();
2550 BOOST_CHECK_EQUAL(firstVol, vol1.get());
2551 auto firstBounds =
2552 dynamic_cast<const CylinderVolumeBounds*>(&firstVol->volumeBounds());
2553 BOOST_REQUIRE(firstBounds != nullptr);
2554 BOOST_CHECK_EQUAL(firstBounds->get(CylinderVolumeBounds::eHalfLengthZ),
2555 2 * hlZ);
2556 BOOST_CHECK_EQUAL(firstBounds->get(CylinderVolumeBounds::eMinR), rMin);
2557 BOOST_CHECK_EQUAL(firstBounds->get(CylinderVolumeBounds::eMaxR), rMax);
2558 BOOST_CHECK_CLOSE(firstVol->center(gctx)[eZ], -2 * hlZ, 1e-10);
2559
2560
2561 auto* lastVol = volumes.back();
2562 BOOST_CHECK_EQUAL(lastVol, vol2.get());
2563 auto lastBounds =
2564 dynamic_cast<const CylinderVolumeBounds*>(&lastVol->volumeBounds());
2565 BOOST_REQUIRE(lastBounds != nullptr);
2566 BOOST_CHECK_EQUAL(lastBounds->get(CylinderVolumeBounds::eHalfLengthZ), hlZ);
2567 BOOST_CHECK_EQUAL(lastBounds->get(CylinderVolumeBounds::eMinR), rMin);
2568 BOOST_CHECK_EQUAL(lastBounds->get(CylinderVolumeBounds::eMaxR), rMax);
2569 BOOST_CHECK_CLOSE(lastVol->center(gctx)[eZ], hlZ, 1e-10);
2570
2571
2572 BOOST_CHECK_EQUAL(volumes.size(), 2);
2573 }
2574
2575 BOOST_AUTO_TEST_CASE(AsymmetricSingleSideResizeZNegativeFlipped) {
2576 const auto gctx = Acts::GeometryContext::dangerouslyDefaultConstruct();
2577 double hlZ = 400_mm;
2578 double rMin = 100_mm;
2579 double rMax = 200_mm;
2580
2581
2582 auto bounds1 = std::make_shared<CylinderVolumeBounds>(rMin, rMax, hlZ);
2583 auto bounds2 = std::make_shared<CylinderVolumeBounds>(rMin, rMax, hlZ);
2584
2585 Transform3 transform1 = Transform3::Identity();
2586 transform1.translate(Vector3{0_mm, 0_mm, -hlZ});
2587 auto vol1 = std::make_shared<Volume>(transform1, bounds1);
2588
2589 Transform3 transform2 = Transform3::Identity();
2590 transform2.translate(Vector3{0_mm, 0_mm, hlZ});
2591 auto vol2 = std::make_shared<Volume>(transform2, bounds2);
2592
2593 std::vector<Volume*> volumes = {vol1.get(), vol2.get()};
2594
2595 CylinderVolumeStack cylStack(
2596 gctx, volumes, AxisDirection::AxisZ, VolumeAttachmentStrategy::Gap,
2597 {VolumeResizeStrategy::Gap, VolumeResizeStrategy::Expand}, *logger);
2598
2599
2600 auto newBounds = std::make_shared<CylinderVolumeBounds>(rMin, rMax, 3 * hlZ);
2601 Transform3 newTransform =
2602 Transform3::Identity() * Translation3{0_mm, 0_mm, -hlZ};
2603 cylStack.update(gctx, newBounds, newTransform, *logger);
2604
2605
2606 BOOST_CHECK_EQUAL(volumes.size(), 3);
2607
2608
2609 auto* gapVol = volumes[0];
2610 auto gapBounds =
2611 dynamic_cast<const CylinderVolumeBounds*>(&gapVol->volumeBounds());
2612 BOOST_REQUIRE(gapBounds != nullptr);
2613 BOOST_CHECK_EQUAL(gapBounds->get(CylinderVolumeBounds::eHalfLengthZ), hlZ);
2614 BOOST_CHECK_EQUAL(gapBounds->get(CylinderVolumeBounds::eMinR), rMin);
2615 BOOST_CHECK_EQUAL(gapBounds->get(CylinderVolumeBounds::eMaxR), rMax);
2616 BOOST_CHECK_CLOSE(gapVol->center(gctx)[eZ], -3 * hlZ, 1e-10);
2617
2618
2619 auto* originalFirstVol = volumes[1];
2620 BOOST_CHECK_EQUAL(originalFirstVol, vol1.get());
2621 auto originalFirstBounds = dynamic_cast<const CylinderVolumeBounds*>(
2622 &originalFirstVol->volumeBounds());
2623 BOOST_REQUIRE(originalFirstBounds != nullptr);
2624 BOOST_CHECK_EQUAL(
2625 originalFirstBounds->get(CylinderVolumeBounds::eHalfLengthZ), hlZ);
2626 BOOST_CHECK_EQUAL(originalFirstBounds->get(CylinderVolumeBounds::eMinR),
2627 rMin);
2628 BOOST_CHECK_EQUAL(originalFirstBounds->get(CylinderVolumeBounds::eMaxR),
2629 rMax);
2630 BOOST_CHECK_CLOSE(originalFirstVol->center(gctx)[eZ], -hlZ, 1e-10);
2631 }
2632
2633 BOOST_AUTO_TEST_CASE(AsymmetricSingleSideResizeRNegative) {
2634 const auto gctx = Acts::GeometryContext::dangerouslyDefaultConstruct();
2635 double hlZ = 400_mm;
2636 double rMin1 = 100_mm;
2637 double rMax1 = 200_mm;
2638 double rMin2 = 200_mm;
2639 double rMax2 = 300_mm;
2640
2641
2642 auto bounds1 = std::make_shared<CylinderVolumeBounds>(rMin1, rMax1, hlZ);
2643 auto bounds2 = std::make_shared<CylinderVolumeBounds>(rMin2, rMax2, hlZ);
2644
2645 Transform3 transform = Transform3::Identity();
2646 auto vol1 = std::make_shared<Volume>(transform, bounds1);
2647 auto vol2 = std::make_shared<Volume>(transform, bounds2);
2648
2649 std::vector<Volume*> volumes = {vol1.get(), vol2.get()};
2650
2651 CylinderVolumeStack cylStack(
2652 gctx, volumes, AxisDirection::AxisR, VolumeAttachmentStrategy::Gap,
2653 {VolumeResizeStrategy::Expand, VolumeResizeStrategy::Gap}, *logger);
2654
2655
2656 auto newBounds = std::make_shared<CylinderVolumeBounds>(50_mm, rMax2, hlZ);
2657 cylStack.update(gctx, newBounds, std::nullopt, *logger);
2658
2659 auto* firstVol = volumes.front();
2660 BOOST_CHECK_EQUAL(firstVol, vol1.get());
2661 auto firstBounds =
2662 dynamic_cast<const CylinderVolumeBounds*>(&firstVol->volumeBounds());
2663 BOOST_REQUIRE(firstBounds != nullptr);
2664 BOOST_CHECK_EQUAL(firstBounds->get(CylinderVolumeBounds::eMinR), 50_mm);
2665 BOOST_CHECK_EQUAL(firstBounds->get(CylinderVolumeBounds::eMaxR), rMax1);
2666 BOOST_CHECK_EQUAL(firstBounds->get(CylinderVolumeBounds::eHalfLengthZ), hlZ);
2667 BOOST_CHECK_CLOSE(firstVol->center(gctx)[eZ], 0, 1e-10);
2668
2669
2670 auto* lastVol = volumes.back();
2671 BOOST_CHECK_EQUAL(lastVol, vol2.get());
2672 auto lastBounds =
2673 dynamic_cast<const CylinderVolumeBounds*>(&lastVol->volumeBounds());
2674 BOOST_REQUIRE(lastBounds != nullptr);
2675 BOOST_CHECK_EQUAL(lastBounds->get(CylinderVolumeBounds::eMinR), rMin2);
2676 BOOST_CHECK_EQUAL(lastBounds->get(CylinderVolumeBounds::eMaxR), rMax2);
2677 BOOST_CHECK_EQUAL(lastBounds->get(CylinderVolumeBounds::eHalfLengthZ), hlZ);
2678 BOOST_CHECK_CLOSE(lastVol->center(gctx)[eZ], 0, 1e-10);
2679
2680
2681 BOOST_CHECK_EQUAL(volumes.size(), 2);
2682 }
2683
2684 BOOST_AUTO_TEST_CASE(AsymmetricSingleSideResizeRNegativeFlipped) {
2685 const auto gctx = Acts::GeometryContext::dangerouslyDefaultConstruct();
2686 double hlZ = 400_mm;
2687 double rMin1 = 100_mm;
2688 double rMax1 = 200_mm;
2689 double rMin2 = 200_mm;
2690 double rMax2 = 300_mm;
2691
2692
2693 auto bounds1 = std::make_shared<CylinderVolumeBounds>(rMin1, rMax1, hlZ);
2694 auto bounds2 = std::make_shared<CylinderVolumeBounds>(rMin2, rMax2, hlZ);
2695
2696 Transform3 transform = Transform3::Identity();
2697 auto vol1 = std::make_shared<Volume>(transform, bounds1);
2698 auto vol2 = std::make_shared<Volume>(transform, bounds2);
2699
2700 std::vector<Volume*> volumes = {vol1.get(), vol2.get()};
2701
2702 CylinderVolumeStack cylStack(
2703 gctx, volumes, AxisDirection::AxisR, VolumeAttachmentStrategy::Gap,
2704 {VolumeResizeStrategy::Gap, VolumeResizeStrategy::Expand}, *logger);
2705
2706
2707 auto newBounds = std::make_shared<CylinderVolumeBounds>(50_mm, rMax2, hlZ);
2708 cylStack.update(gctx, newBounds, std::nullopt, *logger);
2709
2710 BOOST_CHECK_EQUAL(volumes.size(), 3);
2711
2712
2713 auto* gapVol = volumes[0];
2714 auto gapBounds =
2715 dynamic_cast<const CylinderVolumeBounds*>(&gapVol->volumeBounds());
2716 BOOST_REQUIRE(gapBounds != nullptr);
2717 BOOST_CHECK_EQUAL(gapBounds->get(CylinderVolumeBounds::eHalfLengthZ), hlZ);
2718 BOOST_CHECK_EQUAL(gapBounds->get(CylinderVolumeBounds::eMinR), 50_mm);
2719 BOOST_CHECK_EQUAL(gapBounds->get(CylinderVolumeBounds::eMaxR), rMin1);
2720 BOOST_CHECK_CLOSE(gapVol->center(gctx)[eZ], 0, 1e-10);
2721
2722
2723 auto* originalFirstVol = volumes[1];
2724 BOOST_CHECK_EQUAL(originalFirstVol, vol1.get());
2725 auto originalFirstBounds = dynamic_cast<const CylinderVolumeBounds*>(
2726 &originalFirstVol->volumeBounds());
2727 BOOST_REQUIRE(originalFirstBounds != nullptr);
2728 BOOST_CHECK_EQUAL(
2729 originalFirstBounds->get(CylinderVolumeBounds::eHalfLengthZ), hlZ);
2730 BOOST_CHECK_EQUAL(originalFirstBounds->get(CylinderVolumeBounds::eMinR),
2731 rMin1);
2732 BOOST_CHECK_EQUAL(originalFirstBounds->get(CylinderVolumeBounds::eMaxR),
2733 rMax1);
2734 BOOST_CHECK_CLOSE(originalFirstVol->center(gctx)[eZ], 0, 1e-10);
2735
2736
2737 auto* originalSecondVol = volumes[2];
2738 BOOST_CHECK_EQUAL(originalSecondVol, vol2.get());
2739 auto originalSecondBounds = dynamic_cast<const CylinderVolumeBounds*>(
2740 &originalSecondVol->volumeBounds());
2741 BOOST_REQUIRE(originalSecondBounds != nullptr);
2742 BOOST_CHECK_EQUAL(originalSecondBounds->get(CylinderVolumeBounds::eMinR),
2743 rMin2);
2744 BOOST_CHECK_EQUAL(originalSecondBounds->get(CylinderVolumeBounds::eMaxR),
2745 rMax2);
2746 BOOST_CHECK_EQUAL(
2747 originalSecondBounds->get(CylinderVolumeBounds::eHalfLengthZ), hlZ);
2748 BOOST_CHECK_CLOSE(originalSecondVol->center(gctx)[eZ], 0, 1e-10);
2749 }
2750
2751 BOOST_AUTO_TEST_CASE(RStackGapCreationWithUpdatedTransform) {
2752 const auto gctx = Acts::GeometryContext::dangerouslyDefaultConstruct();
2753 double hlZ = 400_mm;
2754 double rMin1 = 100_mm;
2755 double rMax1 = 200_mm;
2756 double rMin2 = 200_mm;
2757 double rMax2 = 300_mm;
2758
2759
2760 auto bounds1 = std::make_shared<CylinderVolumeBounds>(rMin1, rMax1, hlZ);
2761 auto bounds2 = std::make_shared<CylinderVolumeBounds>(rMin2, rMax2, hlZ);
2762
2763 Transform3 transform = Transform3::Identity();
2764 auto vol1 = std::make_shared<Volume>(transform, bounds1);
2765 auto vol2 = std::make_shared<Volume>(transform, bounds2);
2766
2767 std::vector<Volume*> volumes = {vol1.get(), vol2.get()};
2768
2769 CylinderVolumeStack cylStack(gctx, volumes, AxisDirection::AxisR,
2770 VolumeAttachmentStrategy::Midpoint,
2771 VolumeResizeStrategy::Gap, *logger);
2772
2773 cylStack.update(
2774 gctx, std::make_shared<CylinderVolumeBounds>(50_mm, rMax2, hlZ + 5_mm),
2775 Transform3(Translation3(Vector3::UnitZ() * 5_mm)), *logger);
2776
2777 auto& cylBounds =
2778 dynamic_cast<CylinderVolumeBounds&>(cylStack.volumeBounds());
2779
2780 BOOST_CHECK_EQUAL(cylBounds.get(CylinderVolumeBounds::eMinR), 50_mm);
2781 BOOST_CHECK_EQUAL(cylBounds.get(CylinderVolumeBounds::eMaxR), rMax2);
2782 BOOST_CHECK_EQUAL(cylBounds.get(CylinderVolumeBounds::eHalfLengthZ),
2783 hlZ + 5_mm);
2784
2785 auto gapIt = std::ranges::find_if(volumes, [&](const auto* vol) {
2786 return vol != vol1.get() && vol != vol2.get();
2787 });
2788
2789 BOOST_REQUIRE(gapIt != volumes.end());
2790
2791 const auto* gap1 = *gapIt;
2792
2793 BOOST_CHECK_EQUAL(vol1->center(gctx)[eZ], 5_mm);
2794 BOOST_CHECK_EQUAL(vol2->center(gctx)[eZ], 5_mm);
2795 BOOST_CHECK_EQUAL(gap1->center(gctx)[eZ], 5_mm);
2796
2797 cylStack.update(
2798 gctx, std::make_shared<CylinderVolumeBounds>(50_mm, 350_mm, hlZ + 10_mm),
2799 Transform3(Translation3(Vector3::UnitZ() * 10_mm)), *logger);
2800
2801 gapIt = std::ranges::find_if(volumes, [&](const auto* vol) {
2802 return vol != vol1.get() && vol != vol2.get() &&
2803 vol != gap1;
2804 });
2805
2806 BOOST_REQUIRE(gapIt != volumes.end());
2807
2808 const auto* gap2 = *gapIt;
2809
2810 BOOST_CHECK_EQUAL(vol1->center(gctx)[eZ], 10_mm);
2811 BOOST_CHECK_EQUAL(vol2->center(gctx)[eZ], 10_mm);
2812 BOOST_CHECK_EQUAL(gap1->center(gctx)[eZ], 10_mm);
2813 BOOST_CHECK_EQUAL(gap2->center(gctx)[eZ], 10_mm);
2814
2815 const auto& gap1Bounds =
2816 dynamic_cast<const CylinderVolumeBounds&>(gap1->volumeBounds());
2817
2818 const auto& gap2Bounds =
2819 dynamic_cast<const CylinderVolumeBounds&>(gap2->volumeBounds());
2820
2821 bounds1 =
2822 std::dynamic_pointer_cast<CylinderVolumeBounds>(vol1->volumeBoundsPtr());
2823 bounds2 =
2824 std::dynamic_pointer_cast<CylinderVolumeBounds>(vol2->volumeBoundsPtr());
2825
2826 BOOST_REQUIRE(bounds1 != nullptr);
2827 BOOST_REQUIRE(bounds2 != nullptr);
2828
2829 BOOST_CHECK_EQUAL(bounds1->get(CylinderVolumeBounds::eMinR), rMin1);
2830 BOOST_CHECK_EQUAL(bounds1->get(CylinderVolumeBounds::eMaxR), rMax1);
2831 BOOST_CHECK_EQUAL(bounds1->get(CylinderVolumeBounds::eHalfLengthZ),
2832 hlZ + 10_mm);
2833
2834 BOOST_CHECK_EQUAL(bounds2->get(CylinderVolumeBounds::eMinR), rMin2);
2835 BOOST_CHECK_EQUAL(bounds2->get(CylinderVolumeBounds::eMaxR), rMax2);
2836 BOOST_CHECK_EQUAL(bounds2->get(CylinderVolumeBounds::eHalfLengthZ),
2837 hlZ + 10_mm);
2838
2839 BOOST_CHECK_EQUAL(gap1Bounds.get(CylinderVolumeBounds::eMinR), 50_mm);
2840 BOOST_CHECK_EQUAL(gap1Bounds.get(CylinderVolumeBounds::eMaxR), rMin1);
2841 BOOST_CHECK_EQUAL(gap1Bounds.get(CylinderVolumeBounds::eHalfLengthZ),
2842 hlZ + 10_mm);
2843
2844 BOOST_CHECK_EQUAL(gap2Bounds.get(CylinderVolumeBounds::eMinR), rMax2);
2845 BOOST_CHECK_EQUAL(gap2Bounds.get(CylinderVolumeBounds::eMaxR), 350_mm);
2846 BOOST_CHECK_EQUAL(gap2Bounds.get(CylinderVolumeBounds::eHalfLengthZ),
2847 hlZ + 10_mm);
2848
2849 BOOST_CHECK_EQUAL(vol1->center(gctx)[eZ], 10_mm);
2850 BOOST_CHECK_EQUAL(vol2->center(gctx)[eZ], 10_mm);
2851 BOOST_CHECK_EQUAL(gap1->center(gctx)[eZ], 10_mm);
2852 }
2853
2854
2855
2856
2857
2858
2859
2860 BOOST_AUTO_TEST_CASE(RStackGapCreationTolerance) {
2861 const auto gctx = Acts::GeometryContext::dangerouslyDefaultConstruct();
2862 const double hlZ = 400_mm;
2863 const double rMin = 100_mm;
2864 const double rMax = 200_mm;
2865
2866
2867 const double eps = s_onSurfaceTolerance / 2.0;
2868
2869 BOOST_TEST_CONTEXT("Outer radius") {
2870 auto bounds = std::make_shared<CylinderVolumeBounds>(rMin, rMax, hlZ);
2871 auto vol = std::make_shared<Volume>(Transform3::Identity(), bounds);
2872
2873 std::vector<Volume*> volumes = {vol.get()};
2874 CylinderVolumeStack cylStack(gctx, volumes, AxisDirection::AxisR,
2875 VolumeAttachmentStrategy::Gap,
2876 VolumeResizeStrategy::Gap, *logger);
2877
2878 BOOST_CHECK(cylStack.gaps().empty());
2879
2880
2881 cylStack.update(
2882 gctx, std::make_shared<CylinderVolumeBounds>(rMin, rMax + eps, hlZ),
2883 std::nullopt, *logger);
2884
2885
2886 BOOST_CHECK(cylStack.gaps().empty());
2887 BOOST_CHECK_EQUAL(volumes.size(), 1);
2888 }
2889
2890 BOOST_TEST_CONTEXT("Inner radius") {
2891 auto bounds = std::make_shared<CylinderVolumeBounds>(rMin, rMax, hlZ);
2892 auto vol = std::make_shared<Volume>(Transform3::Identity(), bounds);
2893
2894 std::vector<Volume*> volumes = {vol.get()};
2895 CylinderVolumeStack cylStack(gctx, volumes, AxisDirection::AxisR,
2896 VolumeAttachmentStrategy::Gap,
2897 VolumeResizeStrategy::Gap, *logger);
2898
2899 BOOST_CHECK(cylStack.gaps().empty());
2900
2901
2902 cylStack.update(
2903 gctx, std::make_shared<CylinderVolumeBounds>(rMin - eps, rMax, hlZ),
2904 std::nullopt, *logger);
2905
2906
2907 BOOST_CHECK(cylStack.gaps().empty());
2908 BOOST_CHECK_EQUAL(volumes.size(), 1);
2909 }
2910 }
2911
2912 BOOST_AUTO_TEST_SUITE_END()
2913
2914 }