File indexing completed on 2026-07-28 08:22:09
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 or bevel 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 std::make_shared<CylinderVolumeBounds>(100_mm, 400_mm, 400_mm,
1953 std::numbers::pi, 0.,
1954 0.3 * std::numbers::pi),
1955 std::make_shared<CylinderVolumeBounds>(100_mm, 400_mm, 400_mm,
1956 std::numbers::pi, 0., 0.,
1957 0.3 * std::numbers::pi),
1958 };
1959
1960 for (const auto& invalid : invalidVolumeBounds) {
1961 std::stringstream ss;
1962 ss << "Invalid bounds: " << *invalid;
1963 BOOST_TEST_CONTEXT(ss.str()) {
1964 std::vector<Volume*> volumes;
1965 auto vol1 = std::make_shared<Volume>(
1966 Transform3{Translation3{Vector3{0_mm, 0_mm, -500_mm}}},
1967 std::make_shared<CylinderVolumeBounds>(100_mm, 400_mm, 400_mm));
1968 volumes.push_back(vol1.get());
1969
1970 {
1971
1972 CylinderVolumeStack cylStack(gctx, volumes, direction, strategy,
1973 VolumeResizeStrategy::Gap, *logger);
1974 BOOST_CHECK_THROW(
1975 cylStack.update(gctx, invalid, std::nullopt, *logger),
1976 std::invalid_argument);
1977 }
1978
1979 {
1980 std::shared_ptr<Volume> vol;
1981 if (direction == AxisDirection::AxisZ) {
1982 vol = std::make_shared<Volume>(
1983 Transform3{Translation3{Vector3{0_mm, 0_mm, 500_mm}}}, invalid);
1984 } else {
1985 invalid->set({
1986 {CylinderVolumeBounds::eMinR, 400_mm},
1987 {CylinderVolumeBounds::eMaxR, 600_mm},
1988 });
1989 vol = std::make_shared<Volume>(
1990 Transform3{Translation3{Vector3{0_mm, 0_mm, 0_mm}}}, invalid);
1991 }
1992 volumes.push_back(vol.get());
1993 BOOST_CHECK_THROW(
1994 CylinderVolumeStack(gctx, volumes, direction, strategy,
1995 VolumeResizeStrategy::Gap, *logger),
1996 std::invalid_argument);
1997 }
1998 }
1999 }
2000 }
2001 }
2002
2003 BOOST_DATA_TEST_CASE(JoinCylinderVolumeSingle,
2004 (boost::unit_test::data::make(AxisDirection::AxisZ,
2005 AxisDirection::AxisR) *
2006 boost::unit_test::data::make(strategies)),
2007 direction, strategy) {
2008 const auto gctx = Acts::GeometryContext::dangerouslyDefaultConstruct();
2009 auto vol = std::make_shared<Volume>(
2010 Transform3::Identity() * Translation3{14_mm, 24_mm, 0_mm} *
2011 AngleAxis3(73_degree, Vector3::UnitX()),
2012 std::make_shared<CylinderVolumeBounds>(100_mm, 400_mm, 400_mm));
2013
2014 std::vector<Volume*> volumes{vol.get()};
2015
2016 CylinderVolumeStack cylStack(gctx, volumes, direction, strategy,
2017 VolumeResizeStrategy::Gap, *logger);
2018
2019
2020
2021 BOOST_CHECK_EQUAL(volumes.size(), 1);
2022 BOOST_CHECK_EQUAL(volumes.at(0), vol.get());
2023 BOOST_CHECK_EQUAL(vol->localToGlobalTransform(gctx).matrix(),
2024 cylStack.localToGlobalTransform(gctx).matrix());
2025 BOOST_CHECK_EQUAL(vol->volumeBounds(), cylStack.volumeBounds());
2026 }
2027
2028 BOOST_AUTO_TEST_SUITE_END()
2029 BOOST_AUTO_TEST_SUITE_END()
2030 BOOST_AUTO_TEST_CASE(AsymmetricResizeZ) {
2031 const auto gctx = Acts::GeometryContext::dangerouslyDefaultConstruct();
2032 double hlZ = 400_mm;
2033 double rMin = 100_mm;
2034 double rMax = 200_mm;
2035
2036
2037 auto bounds1 = std::make_shared<CylinderVolumeBounds>(rMin, rMax, hlZ);
2038 auto bounds2 = std::make_shared<CylinderVolumeBounds>(rMin, rMax, hlZ);
2039 auto bounds3 = std::make_shared<CylinderVolumeBounds>(rMin, rMax, hlZ);
2040
2041 Transform3 transform1 = Transform3::Identity();
2042 transform1.translate(Vector3{0_mm, 0_mm, -2 * hlZ});
2043 auto vol1 = std::make_shared<Volume>(transform1, bounds1);
2044
2045 Transform3 transform2 = Transform3::Identity();
2046 transform2.translate(Vector3{0_mm, 0_mm, 0_mm});
2047 auto vol2 = std::make_shared<Volume>(transform2, bounds2);
2048
2049 Transform3 transform3 = Transform3::Identity();
2050 transform3.translate(Vector3{0_mm, 0_mm, 2 * hlZ});
2051 auto vol3 = std::make_shared<Volume>(transform3, bounds3);
2052
2053 std::vector<Volume*> volumes = {vol1.get(), vol2.get(), vol3.get()};
2054
2055 CylinderVolumeStack cylStack(
2056 gctx, volumes, AxisDirection::AxisZ, VolumeAttachmentStrategy::Gap,
2057 {VolumeResizeStrategy::Gap, VolumeResizeStrategy::Expand}, *logger);
2058
2059
2060
2061 auto newBounds = std::make_shared<CylinderVolumeBounds>(rMin, rMax, 4 * hlZ);
2062 Transform3 newTransform =
2063 Transform3::Identity() * Translation3{0_mm, 0_mm, 0_mm};
2064
2065 cylStack.update(gctx, newBounds, newTransform, *logger);
2066
2067
2068 BOOST_CHECK_EQUAL(volumes.size(), 4);
2069
2070
2071 auto gapVol = volumes.front();
2072 auto gapBounds =
2073 dynamic_cast<const CylinderVolumeBounds*>(&gapVol->volumeBounds());
2074 BOOST_REQUIRE(gapBounds != nullptr);
2075 BOOST_CHECK_EQUAL(
2076 gapBounds->get(CylinderVolumeBounds::eHalfLengthZ),
2077 hlZ / 2);
2078 BOOST_CHECK_EQUAL(gapBounds->get(CylinderVolumeBounds::eMinR), rMin);
2079 BOOST_CHECK_EQUAL(gapBounds->get(CylinderVolumeBounds::eMaxR), rMax);
2080 BOOST_CHECK_CLOSE(gapVol->center(gctx)[eZ], -3.5 * hlZ,
2081 1e-10);
2082
2083
2084 auto* lastVol = volumes.back();
2085 BOOST_CHECK_EQUAL(lastVol, vol3.get());
2086 auto lastBounds =
2087 dynamic_cast<const CylinderVolumeBounds*>(&lastVol->volumeBounds());
2088 BOOST_REQUIRE(lastBounds != nullptr);
2089 BOOST_CHECK_EQUAL(lastBounds->get(CylinderVolumeBounds::eHalfLengthZ),
2090 1.5 * hlZ);
2091 BOOST_CHECK_EQUAL(lastBounds->get(CylinderVolumeBounds::eMinR), rMin);
2092 BOOST_CHECK_EQUAL(lastBounds->get(CylinderVolumeBounds::eMaxR), rMax);
2093 BOOST_CHECK_CLOSE(lastVol->center(gctx)[eZ], 2.5 * hlZ,
2094 1e-10);
2095
2096
2097 for (std::size_t i = 1; i < volumes.size() - 1; i++) {
2098 auto volBounds =
2099 dynamic_cast<const CylinderVolumeBounds*>(&volumes[i]->volumeBounds());
2100 BOOST_REQUIRE(volBounds != nullptr);
2101 BOOST_CHECK_EQUAL(volBounds->get(CylinderVolumeBounds::eHalfLengthZ), hlZ);
2102 BOOST_CHECK_EQUAL(volBounds->get(CylinderVolumeBounds::eMinR), rMin);
2103 BOOST_CHECK_EQUAL(volBounds->get(CylinderVolumeBounds::eMaxR), rMax);
2104 }
2105 BOOST_CHECK_CLOSE(volumes[1]->center(gctx)[eZ], -2 * hlZ, 1e-10);
2106 BOOST_CHECK_CLOSE(volumes[2]->center(gctx)[eZ], 0, 1e-10);
2107 }
2108
2109 BOOST_AUTO_TEST_CASE(AsymmetricResizeZFlipped) {
2110 const auto gctx = Acts::GeometryContext::dangerouslyDefaultConstruct();
2111
2112 double hlZ = 400_mm;
2113 double rMin = 100_mm;
2114 double rMax = 200_mm;
2115
2116
2117 auto bounds1 = std::make_shared<CylinderVolumeBounds>(rMin, rMax, hlZ);
2118 auto bounds2 = std::make_shared<CylinderVolumeBounds>(rMin, rMax, hlZ);
2119 auto bounds3 = std::make_shared<CylinderVolumeBounds>(rMin, rMax, hlZ);
2120
2121 Transform3 transform1 = Transform3::Identity() * Translation3(0, 0, -2 * hlZ);
2122 Transform3 transform2 = Transform3::Identity();
2123 Transform3 transform3 = Transform3::Identity() * Translation3(0, 0, 2 * hlZ);
2124
2125 auto vol1 = std::make_shared<Volume>(transform1, bounds1);
2126 auto vol2 = std::make_shared<Volume>(transform2, bounds2);
2127 auto vol3 = std::make_shared<Volume>(transform3, bounds3);
2128
2129 std::vector<Volume*> volumes = {vol1.get(), vol2.get(), vol3.get()};
2130
2131 CylinderVolumeStack cylStack(
2132 gctx, volumes, AxisDirection::AxisZ, VolumeAttachmentStrategy::Gap,
2133 {VolumeResizeStrategy::Expand, VolumeResizeStrategy::Gap}, *logger);
2134
2135
2136 auto newBounds = std::make_shared<CylinderVolumeBounds>(rMin, rMax, 4 * hlZ);
2137 cylStack.update(gctx, newBounds, std::nullopt, *logger);
2138
2139 BOOST_CHECK_EQUAL(volumes.size(), 4);
2140
2141
2142 auto gapVol = volumes.back();
2143 auto gapBounds =
2144 dynamic_cast<const CylinderVolumeBounds*>(&gapVol->volumeBounds());
2145 BOOST_REQUIRE(gapBounds != nullptr);
2146 BOOST_CHECK_EQUAL(
2147 gapBounds->get(CylinderVolumeBounds::eHalfLengthZ),
2148 hlZ / 2);
2149 BOOST_CHECK_EQUAL(gapBounds->get(CylinderVolumeBounds::eMinR), rMin);
2150 BOOST_CHECK_EQUAL(gapBounds->get(CylinderVolumeBounds::eMaxR), rMax);
2151 BOOST_CHECK_CLOSE(gapVol->center(gctx)[eZ], 3.5 * hlZ,
2152 1e-10);
2153
2154
2155 auto* firstVol = volumes.front();
2156 BOOST_CHECK_EQUAL(firstVol, vol1.get());
2157 auto firstBounds =
2158 dynamic_cast<const CylinderVolumeBounds*>(&firstVol->volumeBounds());
2159 BOOST_REQUIRE(firstBounds != nullptr);
2160 BOOST_CHECK_EQUAL(firstBounds->get(CylinderVolumeBounds::eHalfLengthZ),
2161 1.5 * hlZ);
2162 BOOST_CHECK_EQUAL(firstBounds->get(CylinderVolumeBounds::eMinR), rMin);
2163 BOOST_CHECK_EQUAL(firstBounds->get(CylinderVolumeBounds::eMaxR), rMax);
2164 BOOST_CHECK_CLOSE(firstVol->center(gctx)[eZ], -2.5 * hlZ,
2165 1e-10);
2166
2167
2168 for (std::size_t i = 1; i < volumes.size() - 1; i++) {
2169 auto volBounds =
2170 dynamic_cast<const CylinderVolumeBounds*>(&volumes[i]->volumeBounds());
2171 BOOST_REQUIRE(volBounds != nullptr);
2172 BOOST_CHECK_EQUAL(volBounds->get(CylinderVolumeBounds::eHalfLengthZ), hlZ);
2173 BOOST_CHECK_EQUAL(volBounds->get(CylinderVolumeBounds::eMinR), rMin);
2174 BOOST_CHECK_EQUAL(volBounds->get(CylinderVolumeBounds::eMaxR), rMax);
2175 }
2176 BOOST_CHECK_CLOSE(volumes[1]->center(gctx)[eZ], 0, 1e-10);
2177 BOOST_CHECK_CLOSE(volumes[2]->center(gctx)[eZ], 2 * hlZ, 1e-10);
2178 }
2179
2180 BOOST_AUTO_TEST_CASE(AsymmetricResizeR) {
2181 const auto gctx = Acts::GeometryContext::dangerouslyDefaultConstruct();
2182 double hlZ = 400_mm;
2183
2184
2185 auto bounds1 = std::make_shared<CylinderVolumeBounds>(100_mm, 200_mm, hlZ);
2186 auto bounds2 = std::make_shared<CylinderVolumeBounds>(200_mm, 300_mm, hlZ);
2187 auto bounds3 = std::make_shared<CylinderVolumeBounds>(300_mm, 400_mm, hlZ);
2188
2189 Transform3 transform = Transform3::Identity();
2190 auto vol1 = std::make_shared<Volume>(transform, bounds1);
2191 auto vol2 = std::make_shared<Volume>(transform, bounds2);
2192 auto vol3 = std::make_shared<Volume>(transform, bounds3);
2193
2194 std::vector<Volume*> volumes = {vol1.get(), vol2.get(), vol3.get()};
2195
2196 CylinderVolumeStack cylStack(
2197 gctx, volumes, AxisDirection::AxisR, VolumeAttachmentStrategy::Midpoint,
2198 {VolumeResizeStrategy::Gap, VolumeResizeStrategy::Expand}, *logger);
2199
2200
2201 auto newBounds = std::make_shared<CylinderVolumeBounds>(50_mm, 500_mm, hlZ);
2202 cylStack.update(gctx, newBounds, std::nullopt, *logger);
2203
2204 BOOST_CHECK_EQUAL(volumes.size(), 4);
2205
2206
2207 auto innerGap = volumes.front();
2208 auto innerGapBounds =
2209 dynamic_cast<const CylinderVolumeBounds*>(&innerGap->volumeBounds());
2210 BOOST_REQUIRE(innerGapBounds != nullptr);
2211 BOOST_CHECK_EQUAL(innerGapBounds->get(CylinderVolumeBounds::eMinR), 50_mm);
2212 BOOST_CHECK_EQUAL(innerGapBounds->get(CylinderVolumeBounds::eMaxR), 100_mm);
2213 BOOST_CHECK_EQUAL(innerGapBounds->get(CylinderVolumeBounds::eHalfLengthZ),
2214 hlZ);
2215
2216
2217 auto* outerVol = volumes.back();
2218 BOOST_CHECK_EQUAL(outerVol, vol3.get());
2219
2220 auto outerBounds =
2221 dynamic_cast<const CylinderVolumeBounds*>(&outerVol->volumeBounds());
2222 BOOST_REQUIRE(outerBounds != nullptr);
2223 BOOST_CHECK_EQUAL(outerBounds->get(CylinderVolumeBounds::eMinR), 300_mm);
2224 BOOST_CHECK_EQUAL(outerBounds->get(CylinderVolumeBounds::eMaxR), 500_mm);
2225 BOOST_CHECK_EQUAL(outerBounds->get(CylinderVolumeBounds::eHalfLengthZ), hlZ);
2226
2227
2228 for (std::size_t i = 1; i < volumes.size() - 1; i++) {
2229 auto volBounds =
2230 dynamic_cast<const CylinderVolumeBounds*>(&volumes[i]->volumeBounds());
2231 BOOST_REQUIRE(volBounds != nullptr);
2232 BOOST_CHECK_EQUAL(volBounds->get(CylinderVolumeBounds::eHalfLengthZ), hlZ);
2233 }
2234 }
2235
2236 BOOST_AUTO_TEST_CASE(AsymmetricResizeRFlipped) {
2237 const auto gctx = Acts::GeometryContext::dangerouslyDefaultConstruct();
2238 double hlZ = 400_mm;
2239
2240
2241 auto bounds1 = std::make_shared<CylinderVolumeBounds>(100_mm, 200_mm, hlZ);
2242 auto bounds2 = std::make_shared<CylinderVolumeBounds>(200_mm, 300_mm, hlZ);
2243 auto bounds3 = std::make_shared<CylinderVolumeBounds>(300_mm, 400_mm, hlZ);
2244
2245 Transform3 transform = Transform3::Identity();
2246 auto vol1 = std::make_shared<Volume>(transform, bounds1);
2247 auto vol2 = std::make_shared<Volume>(transform, bounds2);
2248 auto vol3 = std::make_shared<Volume>(transform, bounds3);
2249
2250 std::vector<Volume*> volumes = {vol1.get(), vol2.get(), vol3.get()};
2251
2252 CylinderVolumeStack cylStack(
2253 gctx, volumes, AxisDirection::AxisR, VolumeAttachmentStrategy::Gap,
2254 {VolumeResizeStrategy::Expand, VolumeResizeStrategy::Gap}, *logger);
2255
2256
2257 auto newBounds = std::make_shared<CylinderVolumeBounds>(50_mm, 500_mm, hlZ);
2258 cylStack.update(gctx, newBounds, std::nullopt, *logger);
2259
2260 BOOST_CHECK_EQUAL(volumes.size(), 4);
2261
2262
2263 auto outerGap = volumes.back();
2264 auto outerGapBounds =
2265 dynamic_cast<const CylinderVolumeBounds*>(&outerGap->volumeBounds());
2266 BOOST_REQUIRE(outerGapBounds != nullptr);
2267 BOOST_CHECK_EQUAL(outerGapBounds->get(CylinderVolumeBounds::eMinR), 400_mm);
2268 BOOST_CHECK_EQUAL(outerGapBounds->get(CylinderVolumeBounds::eMaxR), 500_mm);
2269 BOOST_CHECK_EQUAL(outerGapBounds->get(CylinderVolumeBounds::eHalfLengthZ),
2270 hlZ);
2271
2272
2273 auto* innerVol = volumes.front();
2274 BOOST_CHECK_EQUAL(innerVol, vol1.get());
2275
2276 auto innerBounds =
2277 dynamic_cast<const CylinderVolumeBounds*>(&innerVol->volumeBounds());
2278 BOOST_REQUIRE(innerBounds != nullptr);
2279 BOOST_CHECK_EQUAL(innerBounds->get(CylinderVolumeBounds::eMinR), 50_mm);
2280 BOOST_CHECK_EQUAL(innerBounds->get(CylinderVolumeBounds::eMaxR), 200_mm);
2281 BOOST_CHECK_EQUAL(innerBounds->get(CylinderVolumeBounds::eHalfLengthZ), hlZ);
2282
2283
2284 for (std::size_t i = 1; i < volumes.size() - 1; i++) {
2285 auto volBounds =
2286 dynamic_cast<const CylinderVolumeBounds*>(&volumes[i]->volumeBounds());
2287 BOOST_REQUIRE(volBounds != nullptr);
2288 BOOST_CHECK_EQUAL(volBounds->get(CylinderVolumeBounds::eHalfLengthZ), hlZ);
2289 }
2290 }
2291
2292 BOOST_AUTO_TEST_CASE(AsymmetricSingleSideResizeZ) {
2293 const auto gctx = Acts::GeometryContext::dangerouslyDefaultConstruct();
2294 double hlZ = 400_mm;
2295 double rMin = 100_mm;
2296 double rMax = 200_mm;
2297
2298
2299 auto bounds1 = std::make_shared<CylinderVolumeBounds>(rMin, rMax, hlZ);
2300 auto bounds2 = std::make_shared<CylinderVolumeBounds>(rMin, rMax, hlZ);
2301
2302 Transform3 transform1 = Transform3::Identity();
2303 transform1.translate(Vector3{0_mm, 0_mm, -hlZ});
2304 auto vol1 = std::make_shared<Volume>(transform1, bounds1);
2305
2306 Transform3 transform2 = Transform3::Identity();
2307 transform2.translate(Vector3{0_mm, 0_mm, hlZ});
2308 auto vol2 = std::make_shared<Volume>(transform2, bounds2);
2309
2310 std::vector<Volume*> volumes = {vol1.get(), vol2.get()};
2311
2312
2313 CylinderVolumeStack cylStack(
2314 gctx, volumes, AxisDirection::AxisZ, VolumeAttachmentStrategy::Gap,
2315 {VolumeResizeStrategy::Gap, VolumeResizeStrategy::Expand}, *logger);
2316
2317
2318 auto newBounds = std::make_shared<CylinderVolumeBounds>(rMin, rMax, 3 * hlZ);
2319 Transform3 newTransform =
2320 Transform3::Identity() * Translation3{0_mm, 0_mm, hlZ};
2321 cylStack.update(gctx, newBounds, newTransform, *logger);
2322
2323 auto* firstVol = volumes.front();
2324 BOOST_CHECK_EQUAL(firstVol, vol1.get());
2325 auto firstBounds =
2326 dynamic_cast<const CylinderVolumeBounds*>(&firstVol->volumeBounds());
2327 BOOST_REQUIRE(firstBounds != nullptr);
2328 BOOST_CHECK_EQUAL(firstBounds->get(CylinderVolumeBounds::eHalfLengthZ), hlZ);
2329 BOOST_CHECK_EQUAL(firstBounds->get(CylinderVolumeBounds::eMinR), rMin);
2330 BOOST_CHECK_EQUAL(firstBounds->get(CylinderVolumeBounds::eMaxR), rMax);
2331 BOOST_CHECK_CLOSE(firstVol->center(gctx)[eZ], -hlZ, 1e-10);
2332
2333
2334 auto* lastVol = volumes.back();
2335 BOOST_CHECK_EQUAL(lastVol, vol2.get());
2336 auto lastBounds =
2337 dynamic_cast<const CylinderVolumeBounds*>(&lastVol->volumeBounds());
2338 BOOST_REQUIRE(lastBounds != nullptr);
2339 BOOST_CHECK_EQUAL(lastBounds->get(CylinderVolumeBounds::eHalfLengthZ),
2340 2 * hlZ);
2341 BOOST_CHECK_EQUAL(lastBounds->get(CylinderVolumeBounds::eMinR), rMin);
2342 BOOST_CHECK_EQUAL(lastBounds->get(CylinderVolumeBounds::eMaxR), rMax);
2343 BOOST_CHECK_CLOSE(lastVol->center(gctx)[eZ], 2 * hlZ, 1e-10);
2344
2345
2346 BOOST_CHECK_EQUAL(volumes.size(), 2);
2347 }
2348
2349 BOOST_AUTO_TEST_CASE(AsymmetricSingleSideResizeZFlipped) {
2350 const auto gctx = Acts::GeometryContext::dangerouslyDefaultConstruct();
2351 double hlZ = 400_mm;
2352 double rMin = 100_mm;
2353 double rMax = 200_mm;
2354
2355
2356 auto bounds1 = std::make_shared<CylinderVolumeBounds>(rMin, rMax, hlZ);
2357 auto bounds2 = std::make_shared<CylinderVolumeBounds>(rMin, rMax, hlZ);
2358
2359 Transform3 transform1 = Transform3::Identity();
2360 transform1.translate(Vector3{0_mm, 0_mm, -hlZ});
2361 auto vol1 = std::make_shared<Volume>(transform1, bounds1);
2362
2363 Transform3 transform2 = Transform3::Identity();
2364 transform2.translate(Vector3{0_mm, 0_mm, hlZ});
2365 auto vol2 = std::make_shared<Volume>(transform2, bounds2);
2366
2367 std::vector<Volume*> volumes = {vol1.get(), vol2.get()};
2368
2369 CylinderVolumeStack cylStack(
2370 gctx, volumes, AxisDirection::AxisZ, VolumeAttachmentStrategy::Gap,
2371 {VolumeResizeStrategy::Expand, VolumeResizeStrategy::Gap}, *logger);
2372
2373
2374 auto newBounds = std::make_shared<CylinderVolumeBounds>(rMin, rMax, 3 * hlZ);
2375 Transform3 newTransform =
2376 Transform3::Identity() * Translation3{0_mm, 0_mm, hlZ};
2377 cylStack.update(gctx, newBounds, newTransform, *logger);
2378
2379 auto* firstVol = volumes.front();
2380 BOOST_CHECK_EQUAL(firstVol, vol1.get());
2381 auto firstBounds =
2382 dynamic_cast<const CylinderVolumeBounds*>(&firstVol->volumeBounds());
2383 BOOST_REQUIRE(firstBounds != nullptr);
2384 BOOST_CHECK_EQUAL(firstBounds->get(CylinderVolumeBounds::eHalfLengthZ), hlZ);
2385 BOOST_CHECK_EQUAL(firstBounds->get(CylinderVolumeBounds::eMinR), rMin);
2386 BOOST_CHECK_EQUAL(firstBounds->get(CylinderVolumeBounds::eMaxR), rMax);
2387 BOOST_CHECK_CLOSE(firstVol->center(gctx)[eZ], -hlZ, 1e-10);
2388
2389
2390 auto* midVol = volumes[1];
2391 BOOST_CHECK_EQUAL(midVol, vol2.get());
2392 auto midBounds =
2393 dynamic_cast<const CylinderVolumeBounds*>(&midVol->volumeBounds());
2394 BOOST_REQUIRE(midBounds != nullptr);
2395 BOOST_CHECK_EQUAL(midBounds->get(CylinderVolumeBounds::eHalfLengthZ), hlZ);
2396 BOOST_CHECK_EQUAL(midBounds->get(CylinderVolumeBounds::eMinR), rMin);
2397 BOOST_CHECK_EQUAL(midBounds->get(CylinderVolumeBounds::eMaxR), rMax);
2398 BOOST_CHECK_CLOSE(midVol->center(gctx)[eZ], hlZ, 1e-10);
2399
2400
2401 BOOST_CHECK_EQUAL(volumes.size(), 3);
2402
2403
2404 auto* gapVol = volumes.back();
2405 auto gapBounds =
2406 dynamic_cast<const CylinderVolumeBounds*>(&gapVol->volumeBounds());
2407 BOOST_REQUIRE(gapBounds != nullptr);
2408 BOOST_CHECK_EQUAL(gapBounds->get(CylinderVolumeBounds::eHalfLengthZ), hlZ);
2409 BOOST_CHECK_EQUAL(gapBounds->get(CylinderVolumeBounds::eMinR), rMin);
2410 BOOST_CHECK_EQUAL(gapBounds->get(CylinderVolumeBounds::eMaxR), rMax);
2411 BOOST_CHECK_CLOSE(gapVol->center(gctx)[eZ], 3 * hlZ, 1e-10);
2412 }
2413
2414 BOOST_AUTO_TEST_CASE(AsymmetricSingleSideResizeR) {
2415 const auto gctx = Acts::GeometryContext::dangerouslyDefaultConstruct();
2416 double hlZ = 400_mm;
2417 double rMin1 = 100_mm;
2418 double rMax1 = 200_mm;
2419 double rMin2 = 200_mm;
2420 double rMax2 = 300_mm;
2421
2422
2423 auto bounds1 = std::make_shared<CylinderVolumeBounds>(rMin1, rMax1, hlZ);
2424 auto bounds2 = std::make_shared<CylinderVolumeBounds>(rMin2, rMax2, hlZ);
2425
2426 Transform3 transform = Transform3::Identity();
2427 auto vol1 = std::make_shared<Volume>(transform, bounds1);
2428 auto vol2 = std::make_shared<Volume>(transform, bounds2);
2429
2430 std::vector<Volume*> volumes = {vol1.get(), vol2.get()};
2431
2432
2433 CylinderVolumeStack cylStack(
2434 gctx, volumes, AxisDirection::AxisR, VolumeAttachmentStrategy::Gap,
2435 {VolumeResizeStrategy::Gap, VolumeResizeStrategy::Expand}, *logger);
2436
2437
2438 auto newBounds = std::make_shared<CylinderVolumeBounds>(rMin1, 500_mm, hlZ);
2439 cylStack.update(gctx, newBounds, std::nullopt, *logger);
2440
2441
2442 auto* innerVol = volumes.front();
2443 BOOST_CHECK_EQUAL(innerVol, vol1.get());
2444 auto innerBounds =
2445 dynamic_cast<const CylinderVolumeBounds*>(&innerVol->volumeBounds());
2446 BOOST_REQUIRE(innerBounds != nullptr);
2447 BOOST_CHECK_EQUAL(innerBounds->get(CylinderVolumeBounds::eMinR), rMin1);
2448 BOOST_CHECK_EQUAL(innerBounds->get(CylinderVolumeBounds::eMaxR), rMax1);
2449 BOOST_CHECK_EQUAL(innerBounds->get(CylinderVolumeBounds::eHalfLengthZ), hlZ);
2450
2451
2452 auto* outerVol = volumes.back();
2453 BOOST_CHECK_EQUAL(outerVol, vol2.get());
2454 auto outerBounds =
2455 dynamic_cast<const CylinderVolumeBounds*>(&outerVol->volumeBounds());
2456 BOOST_REQUIRE(outerBounds != nullptr);
2457 BOOST_CHECK_EQUAL(outerBounds->get(CylinderVolumeBounds::eMinR), rMin2);
2458 BOOST_CHECK_EQUAL(outerBounds->get(CylinderVolumeBounds::eMaxR), 500_mm);
2459 BOOST_CHECK_EQUAL(outerBounds->get(CylinderVolumeBounds::eHalfLengthZ), hlZ);
2460
2461
2462 BOOST_CHECK_EQUAL(volumes.size(), 2);
2463 }
2464
2465 BOOST_AUTO_TEST_CASE(AsymmetricSingleSideResizeRFlipped) {
2466 const auto gctx = Acts::GeometryContext::dangerouslyDefaultConstruct();
2467 double hlZ = 400_mm;
2468 double rMin1 = 100_mm;
2469 double rMax1 = 200_mm;
2470 double rMin2 = 200_mm;
2471 double rMax2 = 300_mm;
2472
2473
2474 auto bounds1 = std::make_shared<CylinderVolumeBounds>(rMin1, rMax1, hlZ);
2475 auto bounds2 = std::make_shared<CylinderVolumeBounds>(rMin2, rMax2, hlZ);
2476
2477 Transform3 transform = Transform3::Identity();
2478 auto vol1 = std::make_shared<Volume>(transform, bounds1);
2479 auto vol2 = std::make_shared<Volume>(transform, bounds2);
2480
2481 std::vector<Volume*> volumes = {vol1.get(), vol2.get()};
2482
2483 CylinderVolumeStack cylStack(
2484 gctx, volumes, AxisDirection::AxisR, VolumeAttachmentStrategy::Gap,
2485 {VolumeResizeStrategy::Expand, VolumeResizeStrategy::Gap}, *logger);
2486
2487
2488 auto newBounds = std::make_shared<CylinderVolumeBounds>(rMin1, 500_mm, hlZ);
2489 cylStack.update(gctx, newBounds, std::nullopt, *logger);
2490
2491 auto* innerVol = volumes.front();
2492 BOOST_CHECK_EQUAL(innerVol, vol1.get());
2493 auto innerBounds =
2494 dynamic_cast<const CylinderVolumeBounds*>(&innerVol->volumeBounds());
2495 BOOST_REQUIRE(innerBounds != nullptr);
2496 BOOST_CHECK_EQUAL(innerBounds->get(CylinderVolumeBounds::eMinR), rMin1);
2497 BOOST_CHECK_EQUAL(innerBounds->get(CylinderVolumeBounds::eMaxR), rMax1);
2498 BOOST_CHECK_EQUAL(innerBounds->get(CylinderVolumeBounds::eHalfLengthZ), hlZ);
2499 BOOST_CHECK_CLOSE(innerVol->center(gctx)[eZ], 0, 1e-10);
2500
2501
2502 auto* midVol = volumes[1];
2503 BOOST_CHECK_EQUAL(midVol, vol2.get());
2504 auto midBounds =
2505 dynamic_cast<const CylinderVolumeBounds*>(&midVol->volumeBounds());
2506 BOOST_REQUIRE(midBounds != nullptr);
2507 BOOST_CHECK_EQUAL(midBounds->get(CylinderVolumeBounds::eMinR), rMin2);
2508 BOOST_CHECK_EQUAL(midBounds->get(CylinderVolumeBounds::eMaxR), rMax2);
2509 BOOST_CHECK_EQUAL(midBounds->get(CylinderVolumeBounds::eHalfLengthZ), hlZ);
2510 BOOST_CHECK_CLOSE(midVol->center(gctx)[eZ], 0, 1e-10);
2511
2512
2513 BOOST_CHECK_EQUAL(volumes.size(), 3);
2514
2515
2516 auto* gapVol = volumes.back();
2517 auto gapBounds =
2518 dynamic_cast<const CylinderVolumeBounds*>(&gapVol->volumeBounds());
2519 BOOST_REQUIRE(gapBounds != nullptr);
2520 BOOST_CHECK_EQUAL(gapBounds->get(CylinderVolumeBounds::eHalfLengthZ), hlZ);
2521 BOOST_CHECK_EQUAL(gapBounds->get(CylinderVolumeBounds::eMinR), rMax2);
2522 BOOST_CHECK_EQUAL(gapBounds->get(CylinderVolumeBounds::eMaxR), 500_mm);
2523 BOOST_CHECK_CLOSE(gapVol->center(gctx)[eZ], 0, 1e-10);
2524 }
2525
2526 BOOST_AUTO_TEST_CASE(AsymmetricSingleSideResizeZNegative) {
2527 const auto gctx = Acts::GeometryContext::dangerouslyDefaultConstruct();
2528 double hlZ = 400_mm;
2529 double rMin = 100_mm;
2530 double rMax = 200_mm;
2531
2532
2533 auto bounds1 = std::make_shared<CylinderVolumeBounds>(rMin, rMax, hlZ);
2534 auto bounds2 = std::make_shared<CylinderVolumeBounds>(rMin, rMax, hlZ);
2535
2536 Transform3 transform1 = Transform3::Identity();
2537 transform1.translate(Vector3{0_mm, 0_mm, -hlZ});
2538 auto vol1 = std::make_shared<Volume>(transform1, bounds1);
2539
2540 Transform3 transform2 = Transform3::Identity();
2541 transform2.translate(Vector3{0_mm, 0_mm, hlZ});
2542 auto vol2 = std::make_shared<Volume>(transform2, bounds2);
2543
2544 std::vector<Volume*> volumes = {vol1.get(), vol2.get()};
2545
2546 CylinderVolumeStack cylStack(
2547 gctx, volumes, AxisDirection::AxisZ, VolumeAttachmentStrategy::Gap,
2548 {VolumeResizeStrategy::Expand, VolumeResizeStrategy::Gap}, *logger);
2549
2550
2551 auto newBounds = std::make_shared<CylinderVolumeBounds>(rMin, rMax, 3 * hlZ);
2552 Transform3 newTransform =
2553 Transform3::Identity() * Translation3{0_mm, 0_mm, -hlZ};
2554 cylStack.update(gctx, newBounds, newTransform, *logger);
2555
2556 auto* firstVol = volumes.front();
2557 BOOST_CHECK_EQUAL(firstVol, vol1.get());
2558 auto firstBounds =
2559 dynamic_cast<const CylinderVolumeBounds*>(&firstVol->volumeBounds());
2560 BOOST_REQUIRE(firstBounds != nullptr);
2561 BOOST_CHECK_EQUAL(firstBounds->get(CylinderVolumeBounds::eHalfLengthZ),
2562 2 * hlZ);
2563 BOOST_CHECK_EQUAL(firstBounds->get(CylinderVolumeBounds::eMinR), rMin);
2564 BOOST_CHECK_EQUAL(firstBounds->get(CylinderVolumeBounds::eMaxR), rMax);
2565 BOOST_CHECK_CLOSE(firstVol->center(gctx)[eZ], -2 * hlZ, 1e-10);
2566
2567
2568 auto* lastVol = volumes.back();
2569 BOOST_CHECK_EQUAL(lastVol, vol2.get());
2570 auto lastBounds =
2571 dynamic_cast<const CylinderVolumeBounds*>(&lastVol->volumeBounds());
2572 BOOST_REQUIRE(lastBounds != nullptr);
2573 BOOST_CHECK_EQUAL(lastBounds->get(CylinderVolumeBounds::eHalfLengthZ), hlZ);
2574 BOOST_CHECK_EQUAL(lastBounds->get(CylinderVolumeBounds::eMinR), rMin);
2575 BOOST_CHECK_EQUAL(lastBounds->get(CylinderVolumeBounds::eMaxR), rMax);
2576 BOOST_CHECK_CLOSE(lastVol->center(gctx)[eZ], hlZ, 1e-10);
2577
2578
2579 BOOST_CHECK_EQUAL(volumes.size(), 2);
2580 }
2581
2582 BOOST_AUTO_TEST_CASE(AsymmetricSingleSideResizeZNegativeFlipped) {
2583 const auto gctx = Acts::GeometryContext::dangerouslyDefaultConstruct();
2584 double hlZ = 400_mm;
2585 double rMin = 100_mm;
2586 double rMax = 200_mm;
2587
2588
2589 auto bounds1 = std::make_shared<CylinderVolumeBounds>(rMin, rMax, hlZ);
2590 auto bounds2 = std::make_shared<CylinderVolumeBounds>(rMin, rMax, hlZ);
2591
2592 Transform3 transform1 = Transform3::Identity();
2593 transform1.translate(Vector3{0_mm, 0_mm, -hlZ});
2594 auto vol1 = std::make_shared<Volume>(transform1, bounds1);
2595
2596 Transform3 transform2 = Transform3::Identity();
2597 transform2.translate(Vector3{0_mm, 0_mm, hlZ});
2598 auto vol2 = std::make_shared<Volume>(transform2, bounds2);
2599
2600 std::vector<Volume*> volumes = {vol1.get(), vol2.get()};
2601
2602 CylinderVolumeStack cylStack(
2603 gctx, volumes, AxisDirection::AxisZ, VolumeAttachmentStrategy::Gap,
2604 {VolumeResizeStrategy::Gap, VolumeResizeStrategy::Expand}, *logger);
2605
2606
2607 auto newBounds = std::make_shared<CylinderVolumeBounds>(rMin, rMax, 3 * hlZ);
2608 Transform3 newTransform =
2609 Transform3::Identity() * Translation3{0_mm, 0_mm, -hlZ};
2610 cylStack.update(gctx, newBounds, newTransform, *logger);
2611
2612
2613 BOOST_CHECK_EQUAL(volumes.size(), 3);
2614
2615
2616 auto* gapVol = volumes[0];
2617 auto gapBounds =
2618 dynamic_cast<const CylinderVolumeBounds*>(&gapVol->volumeBounds());
2619 BOOST_REQUIRE(gapBounds != nullptr);
2620 BOOST_CHECK_EQUAL(gapBounds->get(CylinderVolumeBounds::eHalfLengthZ), hlZ);
2621 BOOST_CHECK_EQUAL(gapBounds->get(CylinderVolumeBounds::eMinR), rMin);
2622 BOOST_CHECK_EQUAL(gapBounds->get(CylinderVolumeBounds::eMaxR), rMax);
2623 BOOST_CHECK_CLOSE(gapVol->center(gctx)[eZ], -3 * hlZ, 1e-10);
2624
2625
2626 auto* originalFirstVol = volumes[1];
2627 BOOST_CHECK_EQUAL(originalFirstVol, vol1.get());
2628 auto originalFirstBounds = dynamic_cast<const CylinderVolumeBounds*>(
2629 &originalFirstVol->volumeBounds());
2630 BOOST_REQUIRE(originalFirstBounds != nullptr);
2631 BOOST_CHECK_EQUAL(
2632 originalFirstBounds->get(CylinderVolumeBounds::eHalfLengthZ), hlZ);
2633 BOOST_CHECK_EQUAL(originalFirstBounds->get(CylinderVolumeBounds::eMinR),
2634 rMin);
2635 BOOST_CHECK_EQUAL(originalFirstBounds->get(CylinderVolumeBounds::eMaxR),
2636 rMax);
2637 BOOST_CHECK_CLOSE(originalFirstVol->center(gctx)[eZ], -hlZ, 1e-10);
2638 }
2639
2640 BOOST_AUTO_TEST_CASE(AsymmetricSingleSideResizeRNegative) {
2641 const auto gctx = Acts::GeometryContext::dangerouslyDefaultConstruct();
2642 double hlZ = 400_mm;
2643 double rMin1 = 100_mm;
2644 double rMax1 = 200_mm;
2645 double rMin2 = 200_mm;
2646 double rMax2 = 300_mm;
2647
2648
2649 auto bounds1 = std::make_shared<CylinderVolumeBounds>(rMin1, rMax1, hlZ);
2650 auto bounds2 = std::make_shared<CylinderVolumeBounds>(rMin2, rMax2, hlZ);
2651
2652 Transform3 transform = Transform3::Identity();
2653 auto vol1 = std::make_shared<Volume>(transform, bounds1);
2654 auto vol2 = std::make_shared<Volume>(transform, bounds2);
2655
2656 std::vector<Volume*> volumes = {vol1.get(), vol2.get()};
2657
2658 CylinderVolumeStack cylStack(
2659 gctx, volumes, AxisDirection::AxisR, VolumeAttachmentStrategy::Gap,
2660 {VolumeResizeStrategy::Expand, VolumeResizeStrategy::Gap}, *logger);
2661
2662
2663 auto newBounds = std::make_shared<CylinderVolumeBounds>(50_mm, rMax2, hlZ);
2664 cylStack.update(gctx, newBounds, std::nullopt, *logger);
2665
2666 auto* firstVol = volumes.front();
2667 BOOST_CHECK_EQUAL(firstVol, vol1.get());
2668 auto firstBounds =
2669 dynamic_cast<const CylinderVolumeBounds*>(&firstVol->volumeBounds());
2670 BOOST_REQUIRE(firstBounds != nullptr);
2671 BOOST_CHECK_EQUAL(firstBounds->get(CylinderVolumeBounds::eMinR), 50_mm);
2672 BOOST_CHECK_EQUAL(firstBounds->get(CylinderVolumeBounds::eMaxR), rMax1);
2673 BOOST_CHECK_EQUAL(firstBounds->get(CylinderVolumeBounds::eHalfLengthZ), hlZ);
2674 BOOST_CHECK_CLOSE(firstVol->center(gctx)[eZ], 0, 1e-10);
2675
2676
2677 auto* lastVol = volumes.back();
2678 BOOST_CHECK_EQUAL(lastVol, vol2.get());
2679 auto lastBounds =
2680 dynamic_cast<const CylinderVolumeBounds*>(&lastVol->volumeBounds());
2681 BOOST_REQUIRE(lastBounds != nullptr);
2682 BOOST_CHECK_EQUAL(lastBounds->get(CylinderVolumeBounds::eMinR), rMin2);
2683 BOOST_CHECK_EQUAL(lastBounds->get(CylinderVolumeBounds::eMaxR), rMax2);
2684 BOOST_CHECK_EQUAL(lastBounds->get(CylinderVolumeBounds::eHalfLengthZ), hlZ);
2685 BOOST_CHECK_CLOSE(lastVol->center(gctx)[eZ], 0, 1e-10);
2686
2687
2688 BOOST_CHECK_EQUAL(volumes.size(), 2);
2689 }
2690
2691 BOOST_AUTO_TEST_CASE(AsymmetricSingleSideResizeRNegativeFlipped) {
2692 const auto gctx = Acts::GeometryContext::dangerouslyDefaultConstruct();
2693 double hlZ = 400_mm;
2694 double rMin1 = 100_mm;
2695 double rMax1 = 200_mm;
2696 double rMin2 = 200_mm;
2697 double rMax2 = 300_mm;
2698
2699
2700 auto bounds1 = std::make_shared<CylinderVolumeBounds>(rMin1, rMax1, hlZ);
2701 auto bounds2 = std::make_shared<CylinderVolumeBounds>(rMin2, rMax2, hlZ);
2702
2703 Transform3 transform = Transform3::Identity();
2704 auto vol1 = std::make_shared<Volume>(transform, bounds1);
2705 auto vol2 = std::make_shared<Volume>(transform, bounds2);
2706
2707 std::vector<Volume*> volumes = {vol1.get(), vol2.get()};
2708
2709 CylinderVolumeStack cylStack(
2710 gctx, volumes, AxisDirection::AxisR, VolumeAttachmentStrategy::Gap,
2711 {VolumeResizeStrategy::Gap, VolumeResizeStrategy::Expand}, *logger);
2712
2713
2714 auto newBounds = std::make_shared<CylinderVolumeBounds>(50_mm, rMax2, hlZ);
2715 cylStack.update(gctx, newBounds, std::nullopt, *logger);
2716
2717 BOOST_CHECK_EQUAL(volumes.size(), 3);
2718
2719
2720 auto* gapVol = volumes[0];
2721 auto gapBounds =
2722 dynamic_cast<const CylinderVolumeBounds*>(&gapVol->volumeBounds());
2723 BOOST_REQUIRE(gapBounds != nullptr);
2724 BOOST_CHECK_EQUAL(gapBounds->get(CylinderVolumeBounds::eHalfLengthZ), hlZ);
2725 BOOST_CHECK_EQUAL(gapBounds->get(CylinderVolumeBounds::eMinR), 50_mm);
2726 BOOST_CHECK_EQUAL(gapBounds->get(CylinderVolumeBounds::eMaxR), rMin1);
2727 BOOST_CHECK_CLOSE(gapVol->center(gctx)[eZ], 0, 1e-10);
2728
2729
2730 auto* originalFirstVol = volumes[1];
2731 BOOST_CHECK_EQUAL(originalFirstVol, vol1.get());
2732 auto originalFirstBounds = dynamic_cast<const CylinderVolumeBounds*>(
2733 &originalFirstVol->volumeBounds());
2734 BOOST_REQUIRE(originalFirstBounds != nullptr);
2735 BOOST_CHECK_EQUAL(
2736 originalFirstBounds->get(CylinderVolumeBounds::eHalfLengthZ), hlZ);
2737 BOOST_CHECK_EQUAL(originalFirstBounds->get(CylinderVolumeBounds::eMinR),
2738 rMin1);
2739 BOOST_CHECK_EQUAL(originalFirstBounds->get(CylinderVolumeBounds::eMaxR),
2740 rMax1);
2741 BOOST_CHECK_CLOSE(originalFirstVol->center(gctx)[eZ], 0, 1e-10);
2742
2743
2744 auto* originalSecondVol = volumes[2];
2745 BOOST_CHECK_EQUAL(originalSecondVol, vol2.get());
2746 auto originalSecondBounds = dynamic_cast<const CylinderVolumeBounds*>(
2747 &originalSecondVol->volumeBounds());
2748 BOOST_REQUIRE(originalSecondBounds != nullptr);
2749 BOOST_CHECK_EQUAL(originalSecondBounds->get(CylinderVolumeBounds::eMinR),
2750 rMin2);
2751 BOOST_CHECK_EQUAL(originalSecondBounds->get(CylinderVolumeBounds::eMaxR),
2752 rMax2);
2753 BOOST_CHECK_EQUAL(
2754 originalSecondBounds->get(CylinderVolumeBounds::eHalfLengthZ), hlZ);
2755 BOOST_CHECK_CLOSE(originalSecondVol->center(gctx)[eZ], 0, 1e-10);
2756 }
2757
2758 BOOST_AUTO_TEST_CASE(RStackGapCreationWithUpdatedTransform) {
2759 const auto gctx = Acts::GeometryContext::dangerouslyDefaultConstruct();
2760 double hlZ = 400_mm;
2761 double rMin1 = 100_mm;
2762 double rMax1 = 200_mm;
2763 double rMin2 = 200_mm;
2764 double rMax2 = 300_mm;
2765
2766
2767 auto bounds1 = std::make_shared<CylinderVolumeBounds>(rMin1, rMax1, hlZ);
2768 auto bounds2 = std::make_shared<CylinderVolumeBounds>(rMin2, rMax2, hlZ);
2769
2770 Transform3 transform = Transform3::Identity();
2771 auto vol1 = std::make_shared<Volume>(transform, bounds1);
2772 auto vol2 = std::make_shared<Volume>(transform, bounds2);
2773
2774 std::vector<Volume*> volumes = {vol1.get(), vol2.get()};
2775
2776 CylinderVolumeStack cylStack(gctx, volumes, AxisDirection::AxisR,
2777 VolumeAttachmentStrategy::Midpoint,
2778 VolumeResizeStrategy::Gap, *logger);
2779
2780 cylStack.update(
2781 gctx, std::make_shared<CylinderVolumeBounds>(50_mm, rMax2, hlZ + 5_mm),
2782 Transform3(Translation3(Vector3::UnitZ() * 5_mm)), *logger);
2783
2784 auto& cylBounds =
2785 dynamic_cast<CylinderVolumeBounds&>(cylStack.volumeBounds());
2786
2787 BOOST_CHECK_EQUAL(cylBounds.get(CylinderVolumeBounds::eMinR), 50_mm);
2788 BOOST_CHECK_EQUAL(cylBounds.get(CylinderVolumeBounds::eMaxR), rMax2);
2789 BOOST_CHECK_EQUAL(cylBounds.get(CylinderVolumeBounds::eHalfLengthZ),
2790 hlZ + 5_mm);
2791
2792 auto gapIt = std::ranges::find_if(volumes, [&](const auto* vol) {
2793 return vol != vol1.get() && vol != vol2.get();
2794 });
2795
2796 BOOST_REQUIRE(gapIt != volumes.end());
2797
2798 const auto* gap1 = *gapIt;
2799
2800 BOOST_CHECK_EQUAL(vol1->center(gctx)[eZ], 5_mm);
2801 BOOST_CHECK_EQUAL(vol2->center(gctx)[eZ], 5_mm);
2802 BOOST_CHECK_EQUAL(gap1->center(gctx)[eZ], 5_mm);
2803
2804 cylStack.update(
2805 gctx, std::make_shared<CylinderVolumeBounds>(50_mm, 350_mm, hlZ + 10_mm),
2806 Transform3(Translation3(Vector3::UnitZ() * 10_mm)), *logger);
2807
2808 gapIt = std::ranges::find_if(volumes, [&](const auto* vol) {
2809 return vol != vol1.get() && vol != vol2.get() &&
2810 vol != gap1;
2811 });
2812
2813 BOOST_REQUIRE(gapIt != volumes.end());
2814
2815 const auto* gap2 = *gapIt;
2816
2817 BOOST_CHECK_EQUAL(vol1->center(gctx)[eZ], 10_mm);
2818 BOOST_CHECK_EQUAL(vol2->center(gctx)[eZ], 10_mm);
2819 BOOST_CHECK_EQUAL(gap1->center(gctx)[eZ], 10_mm);
2820 BOOST_CHECK_EQUAL(gap2->center(gctx)[eZ], 10_mm);
2821
2822 const auto& gap1Bounds =
2823 dynamic_cast<const CylinderVolumeBounds&>(gap1->volumeBounds());
2824
2825 const auto& gap2Bounds =
2826 dynamic_cast<const CylinderVolumeBounds&>(gap2->volumeBounds());
2827
2828 bounds1 =
2829 std::dynamic_pointer_cast<CylinderVolumeBounds>(vol1->volumeBoundsPtr());
2830 bounds2 =
2831 std::dynamic_pointer_cast<CylinderVolumeBounds>(vol2->volumeBoundsPtr());
2832
2833 BOOST_REQUIRE(bounds1 != nullptr);
2834 BOOST_REQUIRE(bounds2 != nullptr);
2835
2836 BOOST_CHECK_EQUAL(bounds1->get(CylinderVolumeBounds::eMinR), rMin1);
2837 BOOST_CHECK_EQUAL(bounds1->get(CylinderVolumeBounds::eMaxR), rMax1);
2838 BOOST_CHECK_EQUAL(bounds1->get(CylinderVolumeBounds::eHalfLengthZ),
2839 hlZ + 10_mm);
2840
2841 BOOST_CHECK_EQUAL(bounds2->get(CylinderVolumeBounds::eMinR), rMin2);
2842 BOOST_CHECK_EQUAL(bounds2->get(CylinderVolumeBounds::eMaxR), rMax2);
2843 BOOST_CHECK_EQUAL(bounds2->get(CylinderVolumeBounds::eHalfLengthZ),
2844 hlZ + 10_mm);
2845
2846 BOOST_CHECK_EQUAL(gap1Bounds.get(CylinderVolumeBounds::eMinR), 50_mm);
2847 BOOST_CHECK_EQUAL(gap1Bounds.get(CylinderVolumeBounds::eMaxR), rMin1);
2848 BOOST_CHECK_EQUAL(gap1Bounds.get(CylinderVolumeBounds::eHalfLengthZ),
2849 hlZ + 10_mm);
2850
2851 BOOST_CHECK_EQUAL(gap2Bounds.get(CylinderVolumeBounds::eMinR), rMax2);
2852 BOOST_CHECK_EQUAL(gap2Bounds.get(CylinderVolumeBounds::eMaxR), 350_mm);
2853 BOOST_CHECK_EQUAL(gap2Bounds.get(CylinderVolumeBounds::eHalfLengthZ),
2854 hlZ + 10_mm);
2855
2856 BOOST_CHECK_EQUAL(vol1->center(gctx)[eZ], 10_mm);
2857 BOOST_CHECK_EQUAL(vol2->center(gctx)[eZ], 10_mm);
2858 BOOST_CHECK_EQUAL(gap1->center(gctx)[eZ], 10_mm);
2859 }
2860
2861
2862
2863
2864
2865
2866
2867 BOOST_AUTO_TEST_CASE(RStackGapCreationTolerance) {
2868 const auto gctx = Acts::GeometryContext::dangerouslyDefaultConstruct();
2869 const double hlZ = 400_mm;
2870 const double rMin = 100_mm;
2871 const double rMax = 200_mm;
2872
2873
2874 const double eps = s_onSurfaceTolerance / 2.0;
2875
2876 BOOST_TEST_CONTEXT("Outer radius") {
2877 auto bounds = std::make_shared<CylinderVolumeBounds>(rMin, rMax, hlZ);
2878 auto vol = std::make_shared<Volume>(Transform3::Identity(), bounds);
2879
2880 std::vector<Volume*> volumes = {vol.get()};
2881 CylinderVolumeStack cylStack(gctx, volumes, AxisDirection::AxisR,
2882 VolumeAttachmentStrategy::Gap,
2883 VolumeResizeStrategy::Gap, *logger);
2884
2885 BOOST_CHECK(cylStack.gaps().empty());
2886
2887
2888 cylStack.update(
2889 gctx, std::make_shared<CylinderVolumeBounds>(rMin, rMax + eps, hlZ),
2890 std::nullopt, *logger);
2891
2892
2893 BOOST_CHECK(cylStack.gaps().empty());
2894 BOOST_CHECK_EQUAL(volumes.size(), 1);
2895 }
2896
2897 BOOST_TEST_CONTEXT("Inner radius") {
2898 auto bounds = std::make_shared<CylinderVolumeBounds>(rMin, rMax, hlZ);
2899 auto vol = std::make_shared<Volume>(Transform3::Identity(), bounds);
2900
2901 std::vector<Volume*> volumes = {vol.get()};
2902 CylinderVolumeStack cylStack(gctx, volumes, AxisDirection::AxisR,
2903 VolumeAttachmentStrategy::Gap,
2904 VolumeResizeStrategy::Gap, *logger);
2905
2906 BOOST_CHECK(cylStack.gaps().empty());
2907
2908
2909 cylStack.update(
2910 gctx, std::make_shared<CylinderVolumeBounds>(rMin - eps, rMax, hlZ),
2911 std::nullopt, *logger);
2912
2913
2914 BOOST_CHECK(cylStack.gaps().empty());
2915 BOOST_CHECK_EQUAL(volumes.size(), 1);
2916 }
2917 }
2918
2919 BOOST_AUTO_TEST_SUITE_END()
2920
2921 }