Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
CylinderVolumeBoundsTests.cpp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file CylinderVolumeBoundsTests.cpp
1 // This file is part of the Acts project.
2 //
3 // Copyright (C) 2017-2018 CERN for the benefit of the Acts project
4 //
5 // This Source Code Form is subject to the terms of the Mozilla Public
6 // License, v. 2.0. If a copy of the MPL was not distributed with this
7 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 
9 #include <boost/test/data/test_case.hpp>
10 #include <boost/test/unit_test.hpp>
11 
23 
24 #include <algorithm>
25 #include <array>
26 #include <cmath>
27 #include <memory>
28 #include <stdexcept>
29 #include <utility>
30 #include <vector>
31 
32 namespace bdata = boost::unit_test::data;
33 namespace tt = boost::test_tools;
34 
35 namespace Acts {
36 
37 namespace Test {
38 
39 BOOST_AUTO_TEST_SUITE(Geometry)
40 
41 BOOST_AUTO_TEST_CASE(CylinderVolumeBoundsConstruction) {
42  double rmin{10.}, rmax{20.}, halfz{30.}, halfphi{M_PI / 4}, avgphi{0.};
43 
44  // Test different construction modes: solid
45  CylinderVolumeBounds solidCylinder(0., rmax, halfz);
46  BOOST_CHECK_EQUAL(solidCylinder.orientedSurfaces().size(), 3);
47 
48  // Test different construction modes: sectoral solid
49  CylinderVolumeBounds solidCylinderSector(0., rmax, halfz, halfphi);
50  BOOST_CHECK_EQUAL(solidCylinderSector.orientedSurfaces().size(), 5);
51 
52  // Test different construction modes: tube
53  CylinderVolumeBounds tubeCylinder(rmin, rmax, halfz);
54  BOOST_CHECK_EQUAL(tubeCylinder.orientedSurfaces().size(), 4);
55 
56  // Test different construction modes: sectoral tube
57  CylinderVolumeBounds tubeCylinderSector(rmin, rmax, halfz, halfphi);
58  BOOST_CHECK_EQUAL(tubeCylinderSector.orientedSurfaces().size(), 6);
59 
60  CylinderVolumeBounds original(rmin, rmax, halfz, halfphi, avgphi);
61 
62  // Test construction from CylinderBounds and thickness
63  double rmed = 0.5 * (rmin + rmax);
64  double rthickness = (rmax - rmin);
65  CylinderBounds cBounds(rmed, halfz, halfphi, avgphi);
66  CylinderVolumeBounds fromCylinder(cBounds, rthickness);
67  BOOST_CHECK_EQUAL(original, fromCylinder);
68 
69  // Test construction from RadialBounds and thickness
70  RadialBounds rBounds(rmin, rmax, halfphi, avgphi);
71  CylinderVolumeBounds fromDisc(rBounds, 2 * halfz);
72  BOOST_CHECK_EQUAL(original, fromDisc);
73 
74  // Test the copy construction
75  CylinderVolumeBounds copied(original);
76  BOOST_CHECK_EQUAL(original, copied);
77 
78  // Test the assignment
79  CylinderVolumeBounds assigned = original;
80  BOOST_CHECK_EQUAL(original, assigned);
81 }
82 
83 BOOST_AUTO_TEST_CASE(CylinderVolumeBoundsRecreation) {
84  double rmin{10.}, rmax{20.}, halfz{30.}, halfphi{M_PI / 4}, avgphi{0.};
85 
86  CylinderVolumeBounds original(rmin, rmax, halfz, halfphi, avgphi);
87  std::array<double, CylinderVolumeBounds::eSize> values{};
88  std::vector<double> valvector = original.values();
89  std::copy_n(valvector.begin(), CylinderVolumeBounds::eSize, values.begin());
90  CylinderVolumeBounds recreated(values);
91  BOOST_CHECK_EQUAL(original, recreated);
92 }
93 
94 BOOST_AUTO_TEST_CASE(CylinderVolumeBoundsExceptions) {
95  double rmin{10.}, rmax{20.}, halfz{30.}, halfphi{M_PI / 4}, avgphi{0.};
96 
97  // Negative inner radius
98  BOOST_CHECK_THROW(CylinderVolumeBounds(-rmin, rmax, halfz, halfphi, avgphi),
99  std::logic_error);
100 
101  // Negative outer radius
102  BOOST_CHECK_THROW(CylinderVolumeBounds(rmin, -rmax, halfz, halfphi, avgphi),
103  std::logic_error);
104 
105  // Swapped radii
106  BOOST_CHECK_THROW(CylinderVolumeBounds(rmax, rmin, halfz, halfphi, avgphi),
107  std::logic_error);
108 
109  // Zero half length
110  BOOST_CHECK_THROW(CylinderVolumeBounds(rmax, rmin, 0., halfphi, avgphi),
111  std::logic_error);
112 
113  // Negative half length
114  BOOST_CHECK_THROW(CylinderVolumeBounds(rmax, rmin, -halfz, halfphi, avgphi),
115  std::logic_error);
116 
117  // Out of bounds half phi
118  BOOST_CHECK_THROW(CylinderVolumeBounds(rmax, rmin, halfz, -4., avgphi),
119  std::logic_error);
120 
121  // Wrong positioning phi
122  BOOST_CHECK_THROW(CylinderVolumeBounds(rmax, rmin, halfz, halfphi, 4.),
123  std::logic_error);
124 
125  // Test construction from CylinderBounds and thickness
126  double rmed = 0.5 * (rmin + rmax);
127  CylinderBounds cBounds(rmed, halfz, halfphi, avgphi);
128  RadialBounds rBounds(rmin, rmax, halfphi, avgphi);
129 
130  // Negative thickness
131  BOOST_CHECK_THROW(CylinderVolumeBounds(cBounds, -1.), std::logic_error);
132 
133  // Wrong thickness
134  BOOST_CHECK_THROW(CylinderVolumeBounds(cBounds, 1000.), std::logic_error);
135 
136  // Test construction from RadialBounds and thickness
137  BOOST_CHECK_THROW(CylinderVolumeBounds(rBounds, -1), std::logic_error);
138 }
139 
140 BOOST_AUTO_TEST_CASE(CylinderVolumeBoundsAccess) {
141  double rmin{10.}, rmax{20.}, halfz{30.}, halfphi{M_PI / 4}, avgphi{0.};
142  CylinderVolumeBounds cvBounds(rmin, rmax, halfz, halfphi, avgphi);
143 
144  // Test the accessors
145  BOOST_CHECK_EQUAL(cvBounds.get(CylinderVolumeBounds::eMinR), rmin);
146  BOOST_CHECK_EQUAL(cvBounds.get(CylinderVolumeBounds::eMaxR), rmax);
147  BOOST_CHECK_EQUAL(cvBounds.get(CylinderVolumeBounds::eHalfLengthZ), halfz);
148  BOOST_CHECK_EQUAL(cvBounds.get(CylinderVolumeBounds::eHalfPhiSector),
149  halfphi);
150  BOOST_CHECK_EQUAL(cvBounds.get(CylinderVolumeBounds::eAveragePhi), avgphi);
151 }
152 
154 BOOST_DATA_TEST_CASE(CylinderVolumeBoundsOrientedSurfaces,
155  bdata::random(-M_PI, M_PI) ^ bdata::random(-M_PI, M_PI) ^
156  bdata::random(-M_PI, M_PI) ^ bdata::random(-10., 10.) ^
157  bdata::random(-10., 10.) ^ bdata::random(-10., 10.) ^
158  bdata::xrange(100),
159  alpha, beta, gamma, posX, posY, posZ, index) {
160  (void)index;
161 
162  // Create a test context
164 
165  // position of volume
166  const Vector3 pos(posX, posY, posZ);
167  // rotation around x axis
168  AngleAxis3 rotX(alpha, Vector3(1., 0., 0.));
169  // rotation around y axis
170  AngleAxis3 rotY(beta, Vector3(0., 1., 0.));
171  // rotation around z axis
172  AngleAxis3 rotZ(gamma, Vector3(0., 0., 1.));
173 
174  // create the cylinder bounds
175  double rmin = 1.;
176  double rmax = 2.;
177  double halfz = 3.;
178  CylinderVolumeBounds cylBounds(rmin, rmax, halfz);
179  // create the transformation matrix
180  auto transform = Transform3(Translation3(pos));
181  transform *= rotZ;
182  transform *= rotY;
183  transform *= rotX;
184  // get the boundary surfaces
185  auto boundarySurfaces = cylBounds.orientedSurfaces(transform);
186  // Test
187 
188  // check if difference is halfZ - sign and direction independent
190  (pos - boundarySurfaces.at(0).first->center(tgContext)).norm(),
191  cylBounds.get(CylinderVolumeBounds::eHalfLengthZ), 1e-12);
193  (pos - boundarySurfaces.at(1).first->center(tgContext)).norm(),
194  cylBounds.get(CylinderVolumeBounds::eHalfLengthZ), 1e-12);
195  // transform to local
196  double posDiscPosZ =
197  (transform.inverse() * boundarySurfaces.at(1).first->center(tgContext))
198  .z();
199  double centerPosZ = (transform.inverse() * pos).z();
200  double negDiscPosZ =
201  (transform.inverse() * boundarySurfaces.at(0).first->center(tgContext))
202  .z();
203  // check if center of disc boundaries lies in the middle in z
204  BOOST_CHECK_LT(centerPosZ, posDiscPosZ);
205  BOOST_CHECK_GT(centerPosZ, negDiscPosZ);
206  // check positions of disc boundarysurfaces
207  // checks for zero value. double precision value is not exact.
209  negDiscPosZ + cylBounds.get(CylinderVolumeBounds::eHalfLengthZ),
210  centerPosZ, 1e-12);
212  posDiscPosZ - cylBounds.get(CylinderVolumeBounds::eHalfLengthZ),
213  centerPosZ, 1e-12);
214  // orientation of disc surfaces
215  // positive disc durface should point in positive direction in the frame of
216  // the volume
218  transform.rotation().col(2).dot(boundarySurfaces.at(1).first->normal(
219  tgContext, Acts::Vector2(0., 0.))),
220  1., 1e-12);
221  // negative disc durface should point in positive direction in the frame of
222  // the volume
224  transform.rotation().col(2).dot(boundarySurfaces.at(0).first->normal(
225  tgContext, Acts::Vector2(0., 0.))),
226  1., 1e-12);
227  // test in r
228  CHECK_CLOSE_REL(boundarySurfaces.at(3).first->center(tgContext), pos, 1e-12);
229  CHECK_CLOSE_REL(boundarySurfaces.at(2).first->center(tgContext), pos, 1e-12);
230 }
231 
232 BOOST_AUTO_TEST_CASE(CylinderVolumeBoundsBoundingBox) {
234 
235  float tol = 1e-4;
236 
237  CylinderVolumeBounds cvb(0., 5, 10);
238  auto bb = cvb.boundingBox();
239 
240  Transform3 rot;
241  rot = AngleAxis3(M_PI / 2., Vector3::UnitX());
242 
243  BOOST_CHECK_EQUAL(bb.entity(), nullptr);
244  BOOST_CHECK_EQUAL(bb.max(), Vector3(5, 5, 10));
245  BOOST_CHECK_EQUAL(bb.min(), Vector3(-5, -5, -10));
246 
247  bb = cvb.boundingBox(&rot);
248  BOOST_CHECK_EQUAL(bb.entity(), nullptr);
249  CHECK_CLOSE_ABS(bb.max(), Vector3(5, 10, 5), tol);
250  CHECK_CLOSE_ABS(bb.min(), Vector3(-5, -10, -5), tol);
251 
252  cvb = CylinderVolumeBounds(5, 8, 12);
253  bb = cvb.boundingBox();
254  BOOST_CHECK_EQUAL(bb.entity(), nullptr);
255  BOOST_CHECK_EQUAL(bb.max(), Vector3(8, 8, 12));
256  BOOST_CHECK_EQUAL(bb.min(), Vector3(-8, -8, -12));
257 
258  double angle = M_PI / 8.;
259  cvb = CylinderVolumeBounds(5, 8, 13, angle);
260  bb = cvb.boundingBox();
261  BOOST_CHECK_EQUAL(bb.entity(), nullptr);
262  CHECK_CLOSE_ABS(bb.max(), Vector3(8, 8 * std::sin(angle), 13), tol);
263  CHECK_CLOSE_ABS(bb.min(),
264  Vector3(5 * std::cos(angle), -8 * std::sin(angle), -13), tol);
265 
266  rot = AngleAxis3(M_PI / 2., Vector3::UnitZ());
267  bb = cvb.boundingBox(&rot);
268  BOOST_CHECK_EQUAL(bb.entity(), nullptr);
269  CHECK_CLOSE_ABS(bb.max(), Vector3(8 * std::sin(angle), 8, 13), tol);
270  CHECK_CLOSE_ABS(bb.min(),
271  Vector3(-8 * std::sin(angle), 5 * std::cos(angle), -13), tol);
272 
273  rot = AngleAxis3(M_PI / 2., Vector3(-2, 4, 5).normalized());
274  bb = cvb.boundingBox(&rot);
275  BOOST_CHECK_EQUAL(bb.entity(), nullptr);
276  CHECK_CLOSE_ABS(bb.max(), Vector3(8.40007, 15.2828, 3.88911), tol);
277  CHECK_CLOSE_ABS(bb.min(), Vector3(-7.27834, -8.12028, -14.2182), tol);
278 }
279 
280 BOOST_AUTO_TEST_CASE(CylinderVolumeOrientedBoundaries) {
282 
283  CylinderVolumeBounds cvb(5, 10, 20);
284 
285  auto cvbOrientedSurfaces = cvb.orientedSurfaces(Transform3::Identity());
286  BOOST_CHECK_EQUAL(cvbOrientedSurfaces.size(), 4);
287 
288  auto geoCtx = GeometryContext();
289  Vector3 xaxis(1., 0., 0.);
290  Vector3 yaxis(0., 1., 0.);
291  Vector3 zaxis(0., 0., 1.);
292 
293  for (auto& os : cvbOrientedSurfaces) {
294  auto onSurface = os.first->binningPosition(geoCtx, binR);
295  auto osNormal = os.first->normal(geoCtx, onSurface);
296  // Check if you step inside the volume with the oriented normal
297  Vector3 insideCvb = onSurface + os.second * osNormal;
298  Vector3 outsideCvb = onSurface - os.second * osNormal;
299 
300  BOOST_CHECK(cvb.inside(insideCvb));
301  BOOST_CHECK(!cvb.inside(outsideCvb));
302 
303  // Test the orientation of the boundary surfaces
304  auto rot = os.first->transform(geoCtx).rotation();
305  BOOST_CHECK(rot.col(0).isApprox(xaxis));
306  BOOST_CHECK(rot.col(1).isApprox(yaxis));
307  BOOST_CHECK(rot.col(2).isApprox(zaxis));
308  }
309 }
310 
311 BOOST_AUTO_TEST_SUITE_END()
312 
313 } // namespace Test
314 
315 } // namespace Acts