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