Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-10-18 08:22:30

0001 // This file is part of the ACTS project.
0002 //
0003 // Copyright (C) 2016 CERN for the benefit of the ACTS project
0004 //
0005 // This Source Code Form is subject to the terms of the Mozilla Public
0006 // License, v. 2.0. If a copy of the MPL was not distributed with this
0007 // file, You can obtain one at https://mozilla.org/MPL/2.0/.
0008 
0009 #include <boost/test/data/test_case.hpp>
0010 #include <boost/test/unit_test.hpp>
0011 
0012 #include "Acts/Definitions/Algebra.hpp"
0013 #include "Acts/Definitions/Direction.hpp"
0014 #include "Acts/Geometry/CylinderVolumeBounds.hpp"
0015 #include "Acts/Geometry/GeometryContext.hpp"
0016 #include "Acts/Geometry/VolumeBounds.hpp"
0017 #include "Acts/Surfaces/CylinderBounds.hpp"
0018 #include "Acts/Surfaces/RadialBounds.hpp"
0019 #include "Acts/Utilities/BinningType.hpp"
0020 #include "Acts/Utilities/BoundingBox.hpp"
0021 #include "ActsTests/CommonHelpers/FloatComparisons.hpp"
0022 
0023 #include <algorithm>
0024 #include <array>
0025 #include <cmath>
0026 #include <memory>
0027 #include <numbers>
0028 #include <stdexcept>
0029 #include <vector>
0030 
0031 namespace bdata = boost::unit_test::data;
0032 
0033 using namespace Acts;
0034 
0035 namespace ActsTests {
0036 
0037 BOOST_AUTO_TEST_SUITE(GeometrySuite)
0038 
0039 BOOST_AUTO_TEST_CASE(CylinderVolumeBoundsConstruction) {
0040   double rmin{10.}, rmax{20.}, halfz{30.}, halfphi{std::numbers::pi / 4.},
0041       avgphi{0.};
0042 
0043   // Test different construction modes: solid
0044   CylinderVolumeBounds solidCylinder(0., rmax, halfz);
0045   BOOST_CHECK_EQUAL(solidCylinder.orientedSurfaces().size(), 3);
0046 
0047   // Test different construction modes: sectoral solid
0048   CylinderVolumeBounds solidCylinderSector(0., rmax, halfz, halfphi);
0049   BOOST_CHECK_EQUAL(solidCylinderSector.orientedSurfaces().size(), 5);
0050 
0051   // Test different construction modes: tube
0052   CylinderVolumeBounds tubeCylinder(rmin, rmax, halfz);
0053   BOOST_CHECK_EQUAL(tubeCylinder.orientedSurfaces().size(), 4);
0054 
0055   // Test different construction modes: sectoral tube
0056   CylinderVolumeBounds tubeCylinderSector(rmin, rmax, halfz, halfphi);
0057   BOOST_CHECK_EQUAL(tubeCylinderSector.orientedSurfaces().size(), 6);
0058 
0059   CylinderVolumeBounds original(rmin, rmax, halfz, halfphi, avgphi);
0060 
0061   // Test construction from CylinderBounds and thickness
0062   double rmed = 0.5 * (rmin + rmax);
0063   double rthickness = (rmax - rmin);
0064   CylinderBounds cBounds(rmed, halfz, halfphi, avgphi);
0065   CylinderVolumeBounds fromCylinder(cBounds, rthickness);
0066   BOOST_CHECK_EQUAL(original, fromCylinder);
0067 
0068   // Test construction from RadialBounds and thickness
0069   RadialBounds rBounds(rmin, rmax, halfphi, avgphi);
0070   CylinderVolumeBounds fromDisc(rBounds, 2 * halfz);
0071   BOOST_CHECK_EQUAL(original, fromDisc);
0072 
0073   // Test the copy construction
0074   CylinderVolumeBounds copied(original);
0075   BOOST_CHECK_EQUAL(original, copied);
0076 
0077   // Test the assignment
0078   CylinderVolumeBounds assigned = original;
0079   BOOST_CHECK_EQUAL(original, assigned);
0080 }
0081 
0082 BOOST_AUTO_TEST_CASE(CylinderVolumeBoundsRecreation) {
0083   double rmin{10.}, rmax{20.}, halfz{30.}, halfphi{std::numbers::pi / 4.},
0084       avgphi{0.};
0085 
0086   CylinderVolumeBounds original(rmin, rmax, halfz, halfphi, avgphi);
0087   std::array<double, CylinderVolumeBounds::eSize> values{};
0088   std::vector<double> valvector = original.values();
0089   std::copy_n(valvector.begin(), CylinderVolumeBounds::eSize, values.begin());
0090   CylinderVolumeBounds recreated(values);
0091   BOOST_CHECK_EQUAL(original, recreated);
0092 }
0093 
0094 BOOST_AUTO_TEST_CASE(CylinderVolumeBoundsExceptions) {
0095   double rmin{10.}, rmax{20.}, halfz{30.}, halfphi{std::numbers::pi / 4.},
0096       avgphi{0.};
0097 
0098   // Negative inner radius
0099   BOOST_CHECK_THROW(CylinderVolumeBounds(-rmin, rmax, halfz, halfphi, avgphi),
0100                     std::logic_error);
0101 
0102   // Negative outer radius
0103   BOOST_CHECK_THROW(CylinderVolumeBounds(rmin, -rmax, halfz, halfphi, avgphi),
0104                     std::logic_error);
0105 
0106   // Swapped radii
0107   BOOST_CHECK_THROW(CylinderVolumeBounds(rmax, rmin, halfz, halfphi, avgphi),
0108                     std::logic_error);
0109 
0110   // Zero half length
0111   BOOST_CHECK_THROW(CylinderVolumeBounds(rmax, rmin, 0., halfphi, avgphi),
0112                     std::logic_error);
0113 
0114   // Negative half length
0115   BOOST_CHECK_THROW(CylinderVolumeBounds(rmax, rmin, -halfz, halfphi, avgphi),
0116                     std::logic_error);
0117 
0118   // Out of bounds half phi
0119   BOOST_CHECK_THROW(CylinderVolumeBounds(rmax, rmin, halfz, -4., avgphi),
0120                     std::logic_error);
0121 
0122   // Wrong positioning phi
0123   BOOST_CHECK_THROW(CylinderVolumeBounds(rmax, rmin, halfz, halfphi, 4.),
0124                     std::logic_error);
0125 
0126   // Test construction from CylinderBounds and thickness
0127   double rmed = 0.5 * (rmin + rmax);
0128   CylinderBounds cBounds(rmed, halfz, halfphi, avgphi);
0129   RadialBounds rBounds(rmin, rmax, halfphi, avgphi);
0130 
0131   // Negative thickness
0132   BOOST_CHECK_THROW(CylinderVolumeBounds(cBounds, -1.), std::logic_error);
0133 
0134   // Wrong thickness
0135   BOOST_CHECK_THROW(CylinderVolumeBounds(cBounds, 1000.), std::logic_error);
0136 
0137   // Test construction from RadialBounds and thickness
0138   BOOST_CHECK_THROW(CylinderVolumeBounds(rBounds, -1), std::logic_error);
0139 }
0140 
0141 BOOST_AUTO_TEST_CASE(CylinderVolumeBoundsAccess) {
0142   double rmin{10.}, rmax{20.}, halfz{30.}, halfphi{std::numbers::pi / 4.},
0143       avgphi{0.};
0144   CylinderVolumeBounds cvBounds(rmin, rmax, halfz, halfphi, avgphi);
0145 
0146   // Test the accessors
0147   BOOST_CHECK_EQUAL(cvBounds.get(CylinderVolumeBounds::eMinR), rmin);
0148   BOOST_CHECK_EQUAL(cvBounds.get(CylinderVolumeBounds::eMaxR), rmax);
0149   BOOST_CHECK_EQUAL(cvBounds.get(CylinderVolumeBounds::eHalfLengthZ), halfz);
0150   BOOST_CHECK_EQUAL(cvBounds.get(CylinderVolumeBounds::eHalfPhiSector),
0151                     halfphi);
0152   BOOST_CHECK_EQUAL(cvBounds.get(CylinderVolumeBounds::eAveragePhi), avgphi);
0153 }
0154 
0155 /// Unit test for testing the orientedSurfaces() function
0156 BOOST_DATA_TEST_CASE(
0157     CylinderVolumeBoundsOrientedSurfaces,
0158     bdata::random((bdata::engine = std::mt19937(), bdata::seed = 1,
0159                    bdata::distribution = std::uniform_real_distribution<double>(
0160                        -std::numbers::pi, std::numbers::pi))) ^
0161         bdata::random(
0162             (bdata::engine = std::mt19937(), bdata::seed = 2,
0163              bdata::distribution = std::uniform_real_distribution<double>(
0164                  -std::numbers::pi, std::numbers::pi))) ^
0165         bdata::random(
0166             (bdata::engine = std::mt19937(), bdata::seed = 3,
0167              bdata::distribution = std::uniform_real_distribution<double>(
0168                  -std::numbers::pi, std::numbers::pi))) ^
0169         bdata::random((bdata::engine = std::mt19937(), bdata::seed = 4,
0170                        bdata::distribution =
0171                            std::uniform_real_distribution<double>(-10., 10.))) ^
0172         bdata::random((bdata::engine = std::mt19937(), bdata::seed = 5,
0173                        bdata::distribution =
0174                            std::uniform_real_distribution<double>(-10., 10.))) ^
0175         bdata::random((bdata::engine = std::mt19937(), bdata::seed = 6,
0176                        bdata::distribution =
0177                            std::uniform_real_distribution<double>(-10., 10.))) ^
0178         bdata::xrange(100),
0179     alpha, beta, gamma, posX, posY, posZ, index) {
0180   (void)index;
0181 
0182   // Create a test context
0183   GeometryContext tgContext = GeometryContext();
0184 
0185   // position of volume
0186   const Vector3 pos(posX, posY, posZ);
0187   // rotation around x axis
0188   AngleAxis3 rotX(alpha, Vector3(1., 0., 0.));
0189   // rotation around y axis
0190   AngleAxis3 rotY(beta, Vector3(0., 1., 0.));
0191   // rotation around z axis
0192   AngleAxis3 rotZ(gamma, Vector3(0., 0., 1.));
0193 
0194   // create the cylinder bounds
0195   double rmin = 1.;
0196   double rmax = 2.;
0197   double halfz = 3.;
0198   CylinderVolumeBounds cylBounds(rmin, rmax, halfz);
0199   // create the transformation matrix
0200   auto transform = Transform3(Translation3(pos));
0201   transform *= rotZ;
0202   transform *= rotY;
0203   transform *= rotX;
0204   // get the boundary surfaces
0205   auto boundarySurfaces = cylBounds.orientedSurfaces(transform);
0206   // Test
0207 
0208   // check if difference is halfZ - sign and direction independent
0209   CHECK_CLOSE_REL(
0210       (pos - boundarySurfaces.at(0).surface->center(tgContext)).norm(),
0211       cylBounds.get(CylinderVolumeBounds::eHalfLengthZ), 1e-12);
0212   CHECK_CLOSE_REL(
0213       (pos - boundarySurfaces.at(1).surface->center(tgContext)).norm(),
0214       cylBounds.get(CylinderVolumeBounds::eHalfLengthZ), 1e-12);
0215   // transform to local
0216   double posDiscPosZ =
0217       (transform.inverse() * boundarySurfaces.at(1).surface->center(tgContext))
0218           .z();
0219   double centerPosZ = (transform.inverse() * pos).z();
0220   double negDiscPosZ =
0221       (transform.inverse() * boundarySurfaces.at(0).surface->center(tgContext))
0222           .z();
0223   // check if center of disc boundaries lies in the middle in z
0224   BOOST_CHECK_LT(centerPosZ, posDiscPosZ);
0225   BOOST_CHECK_GT(centerPosZ, negDiscPosZ);
0226   // check positions of disc boundarysurfaces
0227   // checks for zero value. double precision value is not exact.
0228   CHECK_CLOSE_ABS(
0229       negDiscPosZ + cylBounds.get(CylinderVolumeBounds::eHalfLengthZ),
0230       centerPosZ, 1e-12);
0231   CHECK_CLOSE_ABS(
0232       posDiscPosZ - cylBounds.get(CylinderVolumeBounds::eHalfLengthZ),
0233       centerPosZ, 1e-12);
0234   // orientation of disc surfaces
0235   // positive disc durface should point in positive direction in the frame of
0236   // the volume
0237   CHECK_CLOSE_REL(
0238       transform.rotation().col(2).dot(
0239           boundarySurfaces.at(1).surface->normal(tgContext, Vector2(0., 0.))),
0240       1., 1e-12);
0241   // negative disc durface should point in positive direction in the frame of
0242   // the volume
0243   CHECK_CLOSE_REL(
0244       transform.rotation().col(2).dot(
0245           boundarySurfaces.at(0).surface->normal(tgContext, Vector2(0., 0.))),
0246       1., 1e-12);
0247   // test in r
0248   CHECK_CLOSE_REL(boundarySurfaces.at(3).surface->center(tgContext), pos,
0249                   1e-12);
0250   CHECK_CLOSE_REL(boundarySurfaces.at(2).surface->center(tgContext), pos,
0251                   1e-12);
0252 }
0253 
0254 BOOST_AUTO_TEST_CASE(CylinderVolumeBoundsBoundingBox) {
0255   GeometryContext tgContext = GeometryContext();
0256 
0257   float tol = 1e-4;
0258 
0259   CylinderVolumeBounds cvb(0., 5, 10);
0260   auto bb = cvb.boundingBox();
0261 
0262   Transform3 rot;
0263   rot = AngleAxis3(std::numbers::pi / 2., Vector3::UnitX());
0264 
0265   BOOST_CHECK_EQUAL(bb.entity(), nullptr);
0266   BOOST_CHECK_EQUAL(bb.max(), Vector3(5, 5, 10));
0267   BOOST_CHECK_EQUAL(bb.min(), Vector3(-5, -5, -10));
0268 
0269   bb = cvb.boundingBox(&rot);
0270   BOOST_CHECK_EQUAL(bb.entity(), nullptr);
0271   CHECK_CLOSE_ABS(bb.max(), Vector3(5, 10, 5), tol);
0272   CHECK_CLOSE_ABS(bb.min(), Vector3(-5, -10, -5), tol);
0273 
0274   cvb = CylinderVolumeBounds(5, 8, 12);
0275   bb = cvb.boundingBox();
0276   BOOST_CHECK_EQUAL(bb.entity(), nullptr);
0277   BOOST_CHECK_EQUAL(bb.max(), Vector3(8, 8, 12));
0278   BOOST_CHECK_EQUAL(bb.min(), Vector3(-8, -8, -12));
0279 
0280   double angle = std::numbers::pi / 8.;
0281   cvb = CylinderVolumeBounds(5, 8, 13, angle);
0282   bb = cvb.boundingBox();
0283   BOOST_CHECK_EQUAL(bb.entity(), nullptr);
0284   CHECK_CLOSE_ABS(bb.max(), Vector3(8, 8 * std::sin(angle), 13), tol);
0285   CHECK_CLOSE_ABS(bb.min(),
0286                   Vector3(5 * std::cos(angle), -8 * std::sin(angle), -13), tol);
0287 
0288   rot = AngleAxis3(std::numbers::pi / 2., Vector3::UnitZ());
0289   bb = cvb.boundingBox(&rot);
0290   BOOST_CHECK_EQUAL(bb.entity(), nullptr);
0291   CHECK_CLOSE_ABS(bb.max(), Vector3(8 * std::sin(angle), 8, 13), tol);
0292   CHECK_CLOSE_ABS(bb.min(),
0293                   Vector3(-8 * std::sin(angle), 5 * std::cos(angle), -13), tol);
0294 
0295   rot = AngleAxis3(std::numbers::pi / 2., Vector3(-2, 4, 5).normalized());
0296   bb = cvb.boundingBox(&rot);
0297   BOOST_CHECK_EQUAL(bb.entity(), nullptr);
0298   CHECK_CLOSE_ABS(bb.max(), Vector3(8.40007, 15.2828, 3.88911), tol);
0299   CHECK_CLOSE_ABS(bb.min(), Vector3(-7.27834, -8.12028, -14.2182), tol);
0300 }
0301 
0302 BOOST_AUTO_TEST_CASE(CylinderVolumeOrientedBoundaries) {
0303   GeometryContext tgContext = GeometryContext();
0304 
0305   CylinderVolumeBounds cvb(5, 10, 20);
0306 
0307   auto cvbOrientedSurfaces = cvb.orientedSurfaces(Transform3::Identity());
0308   BOOST_CHECK_EQUAL(cvbOrientedSurfaces.size(), 4);
0309 
0310   auto geoCtx = GeometryContext();
0311   Vector3 xaxis(1., 0., 0.);
0312   Vector3 yaxis(0., 1., 0.);
0313   Vector3 zaxis(0., 0., 1.);
0314 
0315   for (auto& os : cvbOrientedSurfaces) {
0316     auto onSurface =
0317         os.surface->referencePosition(geoCtx, AxisDirection::AxisR);
0318     auto locPos =
0319         os.surface->globalToLocal(geoCtx, onSurface, Vector3::Zero()).value();
0320     auto osNormal = os.surface->normal(geoCtx, locPos);
0321     // Check if you step inside the volume with the oriented normal
0322     Vector3 insideCvb = onSurface + os.direction * osNormal;
0323     Vector3 outsideCvb = onSurface - os.direction * osNormal;
0324 
0325     BOOST_CHECK(cvb.inside(insideCvb));
0326     BOOST_CHECK(!cvb.inside(outsideCvb));
0327 
0328     // Test the orientation of the boundary surfaces
0329     auto rot = os.surface->transform(geoCtx).rotation();
0330     BOOST_CHECK(rot.col(0).isApprox(xaxis));
0331     BOOST_CHECK(rot.col(1).isApprox(yaxis));
0332     BOOST_CHECK(rot.col(2).isApprox(zaxis));
0333   }
0334 }
0335 
0336 BOOST_AUTO_TEST_CASE(CylinderVolumeBoundsSetValues) {
0337   CylinderVolumeBounds cyl(100, 300, 200);
0338 
0339   BOOST_CHECK_THROW(cyl.set(CylinderVolumeBounds::eMinR, 400),
0340                     std::invalid_argument);
0341   BOOST_CHECK_EQUAL(cyl.get(CylinderVolumeBounds::eMinR), 100);
0342 
0343   cyl.set(CylinderVolumeBounds::eMinR, 200);
0344   BOOST_CHECK_EQUAL(cyl.get(CylinderVolumeBounds::eMinR), 200);
0345 
0346   BOOST_CHECK_THROW(cyl.set(CylinderVolumeBounds::eMaxR, 50),
0347                     std::invalid_argument);
0348   BOOST_CHECK_EQUAL(cyl.get(CylinderVolumeBounds::eMaxR), 300);
0349 
0350   cyl.set(CylinderVolumeBounds::eMaxR, 250);
0351   BOOST_CHECK_EQUAL(cyl.get(CylinderVolumeBounds::eMaxR), 250);
0352 
0353   BOOST_CHECK_THROW(cyl.set(CylinderVolumeBounds::eHalfLengthZ, -200),
0354                     std::invalid_argument);
0355   BOOST_CHECK_EQUAL(cyl.get(CylinderVolumeBounds::eHalfLengthZ), 200);
0356 
0357   cyl.set(CylinderVolumeBounds::eHalfLengthZ, 250);
0358   BOOST_CHECK_EQUAL(cyl.get(CylinderVolumeBounds::eHalfLengthZ), 250);
0359 
0360   cyl.set(CylinderVolumeBounds::eHalfLengthZ, 150);
0361   BOOST_CHECK_EQUAL(cyl.get(CylinderVolumeBounds::eHalfLengthZ), 150);
0362 
0363   BOOST_CHECK_THROW(
0364       cyl.set(CylinderVolumeBounds::eHalfPhiSector, -std::numbers::pi),
0365       std::invalid_argument);
0366   BOOST_CHECK_EQUAL(cyl.get(CylinderVolumeBounds::eHalfPhiSector),
0367                     std::numbers::pi);
0368 
0369   BOOST_CHECK_THROW(
0370       cyl.set(CylinderVolumeBounds::eHalfPhiSector, 1.5 * std::numbers::pi),
0371       std::invalid_argument);
0372   BOOST_CHECK_EQUAL(cyl.get(CylinderVolumeBounds::eHalfPhiSector),
0373                     std::numbers::pi);
0374 
0375   cyl.set(CylinderVolumeBounds::eHalfPhiSector, std::numbers::pi / 2.);
0376   BOOST_CHECK_EQUAL(cyl.get(CylinderVolumeBounds::eHalfPhiSector),
0377                     std::numbers::pi / 2.);
0378 
0379   for (auto bValue :
0380        {CylinderVolumeBounds::eAveragePhi, CylinderVolumeBounds::eBevelMaxZ,
0381         CylinderVolumeBounds::eBevelMinZ}) {
0382     BOOST_CHECK_THROW(cyl.set(bValue, -1.5 * std::numbers::pi),
0383                       std::invalid_argument);
0384     BOOST_CHECK_EQUAL(cyl.get(bValue), 0);
0385 
0386     BOOST_CHECK_THROW(cyl.set(bValue, 1.5 * std::numbers::pi),
0387                       std::invalid_argument);
0388     BOOST_CHECK_EQUAL(cyl.get(bValue), 0);
0389 
0390     cyl.set(bValue, std::numbers::pi / 2.);
0391     BOOST_CHECK_EQUAL(cyl.get(bValue), std::numbers::pi / 2.);
0392     cyl.set(bValue, -std::numbers::pi / 2.);
0393     BOOST_CHECK_EQUAL(cyl.get(bValue), -std::numbers::pi / 2.);
0394   }
0395 
0396   cyl = CylinderVolumeBounds(100, 300, 200);
0397   auto previous = cyl.values();
0398 
0399   BOOST_CHECK_THROW(cyl.set({
0400                         {CylinderVolumeBounds::eMinR, 50},
0401                         {CylinderVolumeBounds::eMaxR, 200},
0402                         {CylinderVolumeBounds::eHalfLengthZ, -1},
0403                     }),
0404                     std::logic_error);
0405   auto act = cyl.values();
0406   BOOST_CHECK_EQUAL_COLLECTIONS(previous.begin(), previous.end(), act.begin(),
0407                                 act.end());
0408 
0409   cyl.set({
0410       {CylinderVolumeBounds::eMinR, 50},
0411       {CylinderVolumeBounds::eMaxR, 200},
0412       {CylinderVolumeBounds::eHalfLengthZ, 150},
0413   });
0414 
0415   BOOST_CHECK_EQUAL(cyl.get(CylinderVolumeBounds::eMinR), 50);
0416   BOOST_CHECK_EQUAL(cyl.get(CylinderVolumeBounds::eMaxR), 200);
0417   BOOST_CHECK_EQUAL(cyl.get(CylinderVolumeBounds::eHalfLengthZ), 150);
0418   BOOST_CHECK_EQUAL(cyl.get(CylinderVolumeBounds::eHalfPhiSector),
0419                     std::numbers::pi);
0420   BOOST_CHECK_EQUAL(cyl.get(CylinderVolumeBounds::eAveragePhi), 0);
0421   BOOST_CHECK_EQUAL(cyl.get(CylinderVolumeBounds::eBevelMinZ), 0);
0422   BOOST_CHECK_EQUAL(cyl.get(CylinderVolumeBounds::eBevelMaxZ), 0);
0423 }
0424 
0425 BOOST_AUTO_TEST_SUITE_END()
0426 
0427 }  // namespace ActsTests