Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
SurfaceIntersectionTests.cpp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file SurfaceIntersectionTests.cpp
1 // This file is part of the Acts project.
2 //
3 // Copyright (C) 2019 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/tools/output_test_stream.hpp>
11 #include <boost/test/unit_test.hpp>
12 
25 
26 #include <cmath>
27 #include <memory>
28 #include <utility>
29 
30 namespace Acts {
31 
32 using namespace UnitLiterals;
33 
34 // Create a test context
36 
37 // Some random transform
38 Transform3 aTransform = Transform3::Identity() *
39  Translation3(30_cm, 7_m, -87_mm) *
40  AngleAxis3(0.42, Vector3(-3., 1., 8).normalized());
41 
42 namespace Test {
43 
44 BOOST_AUTO_TEST_SUITE(Surfaces)
45 
46 
47 
48 BOOST_AUTO_TEST_CASE(CylinderIntersectionTests) {
49  double radius = 1_m;
50  double halfZ = 10_m;
51 
52  auto testCylinderIntersection = [&](const Transform3& transform) -> void {
53  // A cylinder created aligned with a provided transform
54  auto aCylinder =
55  Surface::makeShared<CylinderSurface>(transform, radius, halfZ);
56 
57  // Linear transform
58  auto lTransform = transform.linear();
59 
60  // An onCylinder solution
61  Vector3 onCylinder = transform * Vector3(radius, 0., 0.);
62  Vector3 outCylinder = transform * Vector3(-radius, 0.6 * radius, 90_cm);
63  Vector3 atCenter = transform * Vector3(0., 0., 0.);
64  Vector3 atEdge = transform * Vector3(0.5 * radius, 0., 0.99 * halfZ);
65  // Simply along the x axis
66  Vector3 alongX = lTransform * Vector3(1., 0., 0.);
67  Vector3 transXY = lTransform * Vector3(1., 1., 0).normalized();
68  Vector3 transTZ = lTransform * Vector3(1., 0., 1.).normalized();
69 
70  // Intersect without boundary check
71  auto aIntersection =
72  aCylinder->intersect(tgContext, onCylinder, alongX, true);
73 
74  // Check the validity of the intersection
75  BOOST_CHECK(aIntersection[0]);
76  // The status of this one should be on surface
77  BOOST_CHECK(aIntersection[0].status() == Intersection3D::Status::reachable);
78  // The intersection is at 2 meter distance
79  CHECK_CLOSE_ABS(aIntersection[0].pathLength(), -2_m, s_onSurfaceTolerance);
80  // There MUST be a second solution
81  BOOST_CHECK(aIntersection[1]);
82  // The other intersection MUST be reachable
83  BOOST_CHECK(aIntersection[1].status() == Intersection3D::Status::onSurface);
84 
85  // Intersect from the center
86  auto cIntersection =
87  aCylinder->intersect(tgContext, atCenter, alongX, true);
88 
89  // Check the validity of the intersection
90  BOOST_CHECK(cIntersection[0]);
91  // The status of this one MUST be reachable
92  BOOST_CHECK(cIntersection[0].status() == Intersection3D::Status::reachable);
93  // There MUST be a second solution
94  BOOST_CHECK(cIntersection[1]);
95  // The other intersection MUST be reachable
96  BOOST_CHECK(cIntersection[1].status() == Intersection3D::Status::reachable);
97  // There MUST be one forward one backwards solution
98  BOOST_CHECK(cIntersection[1].pathLength() * cIntersection[0].pathLength() <
99  0);
100 
101  // Intersect from outside where both intersections are reachable
102  auto oIntersection =
103  aCylinder->intersect(tgContext, outCylinder, alongX, true);
104 
105  // Check the validity of the intersection
106  BOOST_CHECK(oIntersection[0]);
107  // The status of this one MUST be reachable
108  BOOST_CHECK(oIntersection[0].status() == Intersection3D::Status::reachable);
109  // There MUST be a second solution
110  BOOST_CHECK(oIntersection[1]);
111  // The other intersection MUST be reachable
112  BOOST_CHECK(oIntersection[1].status() == Intersection3D::Status::reachable);
113  // There MUST be one forward one backwards solution
114  BOOST_CHECK(oIntersection[1].pathLength() * oIntersection[0].pathLength() >
115  0);
116 
117  // Intersection from outside without chance of hitting the cylinder
118  auto iIntersection =
119  aCylinder->intersect(tgContext, outCylinder, transXY, false);
120 
121  // Check the validity of the intersection
122  BOOST_CHECK(!iIntersection[0]);
123 
124  // From edge tests - wo boundary test
125  auto eIntersection =
126  aCylinder->intersect(tgContext, atEdge, transTZ, false);
127 
128  // Check the validity of the intersection
129  BOOST_CHECK(eIntersection[0]);
130  // This should be the positive one
131  BOOST_CHECK(eIntersection[0].pathLength() < 0.);
132  // The status of this one should be reachable
133  BOOST_CHECK(eIntersection[0].status() == Intersection3D::Status::reachable);
134  // There MUST be a second solution
135  BOOST_CHECK(eIntersection[1]);
136  // The other intersection MUST be reachable
137  BOOST_CHECK(eIntersection[1].status() == Intersection3D::Status::reachable);
138  // And be the negative one
139  BOOST_CHECK(eIntersection[1].pathLength() > 0.);
140 
141  // Now re-do with boundary check
142  eIntersection = aCylinder->intersect(tgContext, atEdge, transTZ, true);
143  // This should be the negative one
144  BOOST_CHECK(eIntersection[0].pathLength() < 0.);
145  // The status of this one should be reachable
146  BOOST_CHECK(eIntersection[0].status() == Intersection3D::Status::reachable);
147  // There MUST be a second solution
148  BOOST_CHECK(!eIntersection[1]);
149  // The other intersection MUST NOT be reachable
150  BOOST_CHECK(eIntersection[1].status() == Intersection3D::Status::missed);
151  // And be the positive one
152  BOOST_CHECK(eIntersection[1].pathLength() > 0.);
153  };
154 
155  // In a nominal world
156  testCylinderIntersection(Transform3::Identity());
157 
158  // In a system somewhere away
159  testCylinderIntersection(aTransform);
160 }
161 
164 BOOST_AUTO_TEST_CASE(ConeIntersectionTest) {
165  double alpha = 0.25 * M_PI;
166 
167  auto testConeIntersection = [&](const Transform3& transform) -> void {
168  // A cone surface ready to use
169  auto aCone = Surface::makeShared<ConeSurface>(transform, alpha, true);
170 
171  // Linear transform
172  auto lTransform = transform.linear();
173 
174  // An onCylinder solution
175  Vector3 onCone = transform * Vector3(std::sqrt(2.), std::sqrt(2.), 2.);
176  Vector3 outCone = transform * Vector3(std::sqrt(4.), std::sqrt(4.), 2.);
177  // Simply along the x axis
178  Vector3 perpXY = lTransform * Vector3(1., -1., 0.).normalized();
179  Vector3 transXY = lTransform * Vector3(1., 1., 0).normalized();
180 
181  // Intersect without boundary check with an on solution
182  BOOST_CHECK(aCone->isOnSurface(tgContext, onCone, transXY, false));
183  auto aIntersection = aCone->intersect(tgContext, onCone, transXY, true);
184 
185  // Check the validity of the intersection
186  BOOST_CHECK(aIntersection[0]);
187  // The status of this one should be on surface
188  BOOST_CHECK(aIntersection[0].status() == Intersection3D::Status::reachable);
189  // The intersection is at 4 mm distance
190  CHECK_CLOSE_ABS(aIntersection[0].pathLength(), -4., s_onSurfaceTolerance);
191  // There MUST be a second solution
192  BOOST_CHECK(aIntersection[1]);
193  // The other intersection MUST be reachable
194  BOOST_CHECK(aIntersection[1].status() == Intersection3D::Status::onSurface);
195 
196  // Intersection from outside without chance of hitting the cylinder
197  auto iIntersection = aCone->intersect(tgContext, outCone, perpXY, false);
198 
199  // Check the validity of the intersection
200  BOOST_CHECK(!iIntersection[0]);
201  };
202 
203  // In a nominal world
204  testConeIntersection(Transform3::Identity());
205 
206  // In a system somewhere away
207  testConeIntersection(aTransform);
208 }
209 
214 BOOST_AUTO_TEST_CASE(PlanarIntersectionTest) {
215  double halfX = 1_m;
216  double halfY = 10_m;
217 
218  auto testPlanarIntersection = [&](const Transform3& transform) -> void {
219  // A Plane created with a specific transform
220  auto aPlane = Surface::makeShared<PlaneSurface>(
221  transform, std::make_shared<RectangleBounds>(halfX, halfY));
222 
224  Vector3 before = transform * Vector3(-50_cm, -1_m, -1_m);
225  Vector3 onit = transform * Vector3(11_cm, -22_cm, 0_m);
226  Vector3 after = transform * Vector3(33_cm, 12_mm, 1_m);
227  Vector3 outside = transform * Vector3(2. * halfX, 2 * halfY, -1_mm);
228 
229  // Linear transform
230  auto lTransform = transform.linear();
231 
232  // A direction that is non trivial
233  Vector3 direction = lTransform * Vector3(4_mm, 8_mm, 50_cm).normalized();
234  Vector3 parallel = lTransform * Vector3(1., 1., 0.).normalized();
235 
236  // Intersect forward
237  auto fIntersection = aPlane->intersect(tgContext, before, direction, true);
238 
239  // The intersection MUST be valid
240  BOOST_CHECK(fIntersection[0]);
241  // The intersection MUST be reachable
242  BOOST_CHECK(fIntersection[0].status() == Intersection3D::Status::reachable);
243  // The path length MUST be positive
244  BOOST_CHECK(fIntersection[0].pathLength() > 0.);
245  // The intersection MUST be unique
246  BOOST_CHECK(!fIntersection[1]);
247 
248  // On surface intersection
249  auto oIntersection = aPlane->intersect(tgContext, onit, direction, true);
250  // The intersection MUST be valid
251  BOOST_CHECK(oIntersection[0]);
252  // The intersection MUST be reachable
253  BOOST_CHECK(oIntersection[0].status() == Intersection3D::Status::onSurface);
254  // The path length MUST be positive
255  BOOST_CHECK(std::abs(oIntersection[0].pathLength()) < s_onSurfaceTolerance);
256  // The intersection MUST be unique
257  BOOST_CHECK(!oIntersection[1]);
258 
259  // Intersect backwards
260  auto bIntersection = aPlane->intersect(tgContext, after, direction, true);
261  // The intersection MUST be valid
262  BOOST_CHECK(bIntersection[0]);
263  // The intersection MUST be reachable
264  BOOST_CHECK(bIntersection[0].status() == Intersection3D::Status::reachable);
265  // The path length MUST be negative
266  BOOST_CHECK(bIntersection[0].pathLength() < 0.);
267  // The intersection MUST be unique
268  BOOST_CHECK(!bIntersection[1]);
269 
270  // An out of bounds attempt: missed
271  auto mIntersection = aPlane->intersect(tgContext, outside, direction, true);
272  // The intersection MUST NOT be valid
273  BOOST_CHECK(!mIntersection[0]);
274  // The intersection MUST be reachable
275  BOOST_CHECK(mIntersection[0].status() == Intersection3D::Status::missed);
276  // The path length MUST be negative
277  BOOST_CHECK(mIntersection[0].pathLength() > 0.);
278  // The intersection MUST be unique
279  BOOST_CHECK(!mIntersection[1]);
280 
281  // An invalid attempt
282  auto iIntersection = aPlane->intersect(tgContext, before, parallel, true);
283  // The intersection MUST NOT be valid
284  BOOST_CHECK(!iIntersection[0]);
285  // The intersection MUST be reachable
286  BOOST_CHECK(iIntersection[0].status() ==
287  Intersection3D::Status::unreachable);
288  // The intersection MUST be unique
289  BOOST_CHECK(!iIntersection[1]);
290  };
291 
292  // In a nominal world
293  testPlanarIntersection(Transform3::Identity());
294 
295  // In a system somewhere away
296  testPlanarIntersection(aTransform);
297 }
298 
303 BOOST_AUTO_TEST_CASE(LineIntersectionTest) {
304  double radius = 1_m;
305  double halfZ = 10_m;
306 
307  auto testLineAppraoch = [&](const Transform3& transform) -> void {
308  // A Plane created with a specific transform
309  auto aLine = Surface::makeShared<StrawSurface>(transform, radius, halfZ);
310 
312  Vector3 before = transform * Vector3(-50_cm, -1_m, -1_m);
313  Vector3 onit1 = transform * Vector3(0_m, 0_m, 0_m);
314  Vector3 onitP = transform * Vector3(1_cm, 0_m, 23_um);
315  Vector3 after = transform * Vector3(33_cm, 12_mm, 1_m);
316  Vector3 outside = transform * Vector3(2., 0., 100_m);
317 
318  // Linear transform
319  auto lTransform = transform.linear();
320  Vector3 direction = lTransform * Vector3(2_cm, 3_cm, 5_cm).normalized();
321  Vector3 normalP = lTransform * Vector3(0, 1., 0.).normalized();
322  Vector3 parallel = lTransform * Vector3(0, 0., 1.).normalized();
323 
324  // A random intersection form backward
325  // Intersect forward
326  auto fIntersection = aLine->intersect(tgContext, before, direction, true);
327  // The intersection MUST be valid
328  BOOST_CHECK(fIntersection[0]);
329  // The intersection MUST be reachable
330  BOOST_CHECK(fIntersection[0].status() == Intersection3D::Status::reachable);
331  // The path length MUST be positive
332  BOOST_CHECK(fIntersection[0].pathLength() > 0.);
333  // The intersection MUST be unique
334  BOOST_CHECK(!fIntersection[1]);
335 
336  // On surface intersection - on the straw with random direction
337  auto oIntersection = aLine->intersect(tgContext, onit1, direction, true);
338  // The intersection MUST be valid
339  BOOST_CHECK(oIntersection[0]);
340  // The intersection MUST be reachable
341  BOOST_CHECK(oIntersection[0].status() == Intersection3D::Status::onSurface);
342  // The path length MUST be positive
343  BOOST_CHECK(std::abs(oIntersection[0].pathLength()) < s_onSurfaceTolerance);
344  // The intersection MUST be unique
345  BOOST_CHECK(!oIntersection[1]);
346 
347  // On surface intersecion - on the surface with normal vector
348  oIntersection = aLine->intersect(tgContext, onitP, normalP, true);
349  // The intersection MUST be valid
350  BOOST_CHECK(oIntersection[0]);
351  // The intersection MUST be reachable
352  BOOST_CHECK(oIntersection[0].status() == Intersection3D::Status::onSurface);
353  // The path length MUST be positive
354  BOOST_CHECK(std::abs(oIntersection[0].pathLength()) < s_onSurfaceTolerance);
355  // The intersection MUST be unique
356  BOOST_CHECK(!oIntersection[1]);
357 
358  // Intersect backwards
359  auto bIntersection = aLine->intersect(tgContext, after, direction, true);
360  // The intersection MUST be valid
361  BOOST_CHECK(bIntersection[0]);
362  // The intersection MUST be reachable
363  BOOST_CHECK(bIntersection[0].status() == Intersection3D::Status::reachable);
364  // The path length MUST be negative
365  BOOST_CHECK(bIntersection[0].pathLength() < 0.);
366  // The intersection MUST be unique
367  BOOST_CHECK(!bIntersection[1]);
368 
369  // An out of bounds attempt: missed
370  auto mIntersection = aLine->intersect(tgContext, outside, direction, true);
371  // The intersection MUST NOT be valid
372  BOOST_CHECK(!mIntersection[0]);
373  // The intersection MUST be reachable
374  BOOST_CHECK(mIntersection[0].status() == Intersection3D::Status::missed);
375  // The path length MUST be negative
376  BOOST_CHECK(mIntersection[0].pathLength() < 0.);
377  // The intersection MUST be unique
378  BOOST_CHECK(!mIntersection[1]);
379 
380  // An invalid attempt
381  auto iIntersection = aLine->intersect(tgContext, before, parallel, true);
382  // The intersection MUST NOT be valid
383  BOOST_CHECK(!iIntersection[0]);
384  // The intersection MUST be reachable
385  BOOST_CHECK(iIntersection[0].status() ==
386  Intersection3D::Status::unreachable);
387  // The intersection MUST be unique
388  BOOST_CHECK(!iIntersection[1]);
389  };
390 
391  // In a nominal world
392  testLineAppraoch(Transform3::Identity());
393 
394  // In a system somewhere away
395  testLineAppraoch(aTransform);
396 }
397 
398 BOOST_AUTO_TEST_SUITE_END()
399 
400 } // namespace Test
401 
402 } // namespace Acts