Project
Loading...
Searching...
No Matches
testHalfSpaceBox.cxx
Go to the documentation of this file.
1// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11
15
16#define BOOST_TEST_MODULE Test HalfSpaceBox
17#define BOOST_TEST_MAIN
18#define BOOST_TEST_DYN_LINK
19#include <boost/test/unit_test.hpp>
20
22#include "TGeoManager.h"
23#include "TGeoBBox.h"
24#include "TGeoTube.h"
25#include "TGeoMatrix.h"
26#include "TGeoHalfSpace.h"
27#include "TGeoCompositeShape.h"
28#include "TMath.h"
29#include "TRandom3.h"
30#include "TString.h"
31#include <cmath>
32#include <vector>
33
34namespace
35{
36struct Plane {
37 const char* label;
38 double p[3];
39 double n[3];
40};
41
42// The fifteen half-space cuts of the TPC support structures (Detectors/TPC/simulation/src/Detector.cxx).
43// Largest solid any of them is subtracted from is 1.65 x 1.85 x 8.9 cm, hence the 10 cm parent below.
44std::vector<Plane> tpcPlanes()
45{
46 const double slope = TMath::Tan(22. * TMath::DegToRad());
47 const double intp = 1.245;
48 const double b = slope * slope + 1.;
49 const double p1[3] = {intp * slope / b, -intp / b, 0.};
50 const double p2[3] = {-intp * slope / b, -intp / b, 0.};
51 return {
52 {"sp1", {p1[0], p1[1], 0.}, {-p1[0], -p1[1], 0.}},
53 {"sp2", {p2[0], p2[1], 0.}, {-p2[0], -p2[1], 0.}},
54 {"cutil1", {0., 0.105, 0.}, {0., 1., 0.}},
55 {"cutomh1", {0., -1.05, -3.4}, {0., -TMath::Tan(30. * TMath::DegToRad()), 1.}},
56 {"cutomh2", {0., -1.05, 3.4}, {0., -TMath::Tan(30. * TMath::DegToRad()), -1.}},
57 {"cutomh3", {-1.65, 0., -0.9}, {TMath::Tan(75. * TMath::DegToRad()), 0., 1.}},
58 {"cutomh4", {-1.65, 0., 0.9}, {TMath::Tan(75. * TMath::DegToRad()), 0., -1.}},
59 {"cutomh5", {1.65, -1.05, 0.}, {-1., -TMath::Tan(20. * TMath::DegToRad()), 0.}},
60 {"cutohs1", {0., -0.186, 0.}, {0., -1., 0.}},
61 {"cutmmh1", {-1.65, 0., -0.9}, {8., 0., 8. * TMath::Tan(13. * TMath::DegToRad())}},
62 {"cutmmh2", {-1.65, 0., 0.9}, {8., 0., -8. * TMath::Tan(13. * TMath::DegToRad())}},
63 {"cutmmh3", {0., 1.85, -2.8}, {0., -6.1, 6.1 * TMath::Tan(20. * TMath::DegToRad())}},
64 {"cutmmh4", {0., 1.85, 2.8}, {0., -6.1, -6.1 * TMath::Tan(20. * TMath::DegToRad())}},
65 {"cutmmh5", {0.75, 0., -8.9}, {2.4 * TMath::Tan(30. * TMath::DegToRad()), 0., 2.4}},
66 {"cutmmh6", {0.75, 0., 8.9}, {2.4 * TMath::Tan(30. * TMath::DegToRad()), 0., -2.4}}};
67}
68
69// Compares "parent - halfspace" against "parent - box:box_tr" on random points and random rays.
70// Points closer than kSurfaceBand to the plane are skipped: on the surface itself the two
71// implementations may legitimately round to different sides.
72void compare(const TString& tag, const double p[3], const double n[3], double parentHalfSize, double reach,
73 TRandom3& rnd, int nPoints, int nRays, double& maxDistDiff)
74{
75 const double nl = std::sqrt(n[0] * n[0] + n[1] * n[1] + n[2] * n[2]);
76 BOOST_REQUIRE(nl > 1e-6);
77 constexpr double kSurfaceBand = 1e-9;
78
79 new TGeoBBox(TString::Format("parent_%s", tag.Data()).Data(), parentHalfSize, parentHalfSize, parentHalfSize);
80 new TGeoHalfSpace(TString::Format("hs_%s", tag.Data()).Data(), const_cast<double*>(p), const_cast<double*>(n));
81 o2::base::TGeoGeometryUtils::makeHalfSpaceBox(TString::Format("bx_%s", tag.Data()).Data(), p, n, reach);
82
83 auto* ref = new TGeoCompositeShape(TString::Format("ref_%s", tag.Data()),
84 TString::Format("parent_%s-hs_%s", tag.Data(), tag.Data()));
85 auto* box = new TGeoCompositeShape(TString::Format("new_%s", tag.Data()),
86 TString::Format("parent_%s-(bx_%s:bx_%s_tr)", tag.Data(), tag.Data(), tag.Data()));
87
88 const double range = 1.2 * parentHalfSize;
89 for (int k = 0; k < nPoints; ++k) {
90 double x[3];
91 for (int i = 0; i < 3; ++i) {
92 x[i] = rnd.Uniform(-range, range);
93 }
94 const double d = ((x[0] - p[0]) * n[0] + (x[1] - p[1]) * n[1] + (x[2] - p[2]) * n[2]) / nl;
95 if (std::abs(d) < kSurfaceBand) {
96 continue;
97 }
98 if (ref->Contains(x) != box->Contains(x)) {
99 BOOST_REQUIRE_MESSAGE(false, "containment differs for " << tag.Data() << " at (" << x[0] << "," << x[1] << ","
100 << x[2] << "), distance to plane " << d);
101 }
102 }
103
104 for (int k = 0; k < nRays; ++k) {
105 double x[3], dir[3];
106 for (int i = 0; i < 3; ++i) {
107 x[i] = rnd.Uniform(-3. * range, 3. * range);
108 dir[i] = rnd.Uniform(-1., 1.);
109 }
110 const double dn = std::sqrt(dir[0] * dir[0] + dir[1] * dir[1] + dir[2] * dir[2]);
111 if (dn < 1e-6) {
112 continue;
113 }
114 for (int i = 0; i < 3; ++i) {
115 dir[i] /= dn;
116 }
117 const bool inside = ref->Contains(x);
118 if (inside != box->Contains(x)) {
119 continue; // a point sitting on the surface; covered by the containment loop above
120 }
121 const double d1 = inside ? ref->DistFromInside(x, dir, 3) : ref->DistFromOutside(x, dir, 3);
122 const double d2 = inside ? box->DistFromInside(x, dir, 3) : box->DistFromOutside(x, dir, 3);
123 if (d1 > 1e15 && d2 > 1e15) {
124 continue; // both miss
125 }
126 maxDistDiff = std::max(maxDistDiff, std::abs(d1 - d2));
127 }
128}
129} // namespace
130
131BOOST_AUTO_TEST_CASE(HalfSpaceBox_reproduces_TGeoHalfSpace)
132{
133 auto* geom = new TGeoManager("halfspacetest", "half-space replacement test");
134 TRandom3 rnd(20240101);
135 double maxDistDiff = 0.;
136
137 // the real TPC cuts
138 for (const auto& pl : tpcPlanes()) {
139 compare(pl.label, pl.p, pl.n, 10., 100., rnd, 200000, 20000, maxDistDiff);
140 }
141
142 // and a spread of arbitrary planes, to pin the rotation for normals in every octant
143 for (int i = 0; i < 200; ++i) {
144 double p[3], n[3];
145 for (int k = 0; k < 3; ++k) {
146 p[k] = rnd.Uniform(-5., 5.);
147 n[k] = rnd.Uniform(-1., 1.);
148 }
149 if (std::sqrt(n[0] * n[0] + n[1] * n[1] + n[2] * n[2]) < 1e-3) {
150 continue;
151 }
152 compare(TString::Format("rnd%d", i), p, n, 10., 100., rnd, 20000, 2000, maxDistDiff);
153 }
154
155 // the two shapes are not bit-identical, but they must agree to double round-off
156 BOOST_CHECK_SMALL(maxDistDiff, 1e-9);
157 BOOST_TEST_MESSAGE("maximum ray-distance difference: " << maxDistDiff);
158 delete geom;
159}
160
161// The composite expressions of the TPC support structures are not all of the simple
162// "parent - cut" shape: tpcihs6 subtracts a union and two placed tubes first. That shape is
163// what makes a *trailing* "cut:matrix" term unsafe to write unparenthesised, so keep a case
164// with the same structure.
165BOOST_AUTO_TEST_CASE(HalfSpaceBox_in_a_compound_expression)
166{
167 auto* geom = new TGeoManager("halfspacetest2", "half-space replacement, compound expression");
168 const double shift[3] = {0., -0.175, 0.};
169 const double p[3] = {0., 0.105, 0.};
170 const double n[3] = {0., 1., 0.};
171
172 new TGeoBBox("tpcihs1", 4.7, 0.66, 2.35);
173 new TGeoBBox("tpcihs2", 4.7, 0.485, 1.0, const_cast<double*>(shift));
174 new TGeoBBox("tpcihs3", 1.5, 0.485, 2.35, const_cast<double*>(shift));
175 new TGeoTube("tpcihs4", 0.0, 2.38, 0.1);
176 auto* trans2 = new TGeoTranslation("trans2", 0.0, 2.84, 2.25);
177 trans2->RegisterYourself();
178 auto* trans3 = new TGeoTranslation("trans3", 0.0, 2.84, -2.25);
179 trans3->RegisterYourself();
180 new TGeoHalfSpace("cutil1", const_cast<double*>(p), const_cast<double*>(n));
182
183 auto* ref = new TGeoCompositeShape(
184 "ref_tpcihs6", "tpcihs1-(tpcihs2+tpcihs3)-(tpcihs4:trans2)-(tpcihs4:trans3)-cutil1");
185 auto* box = new TGeoCompositeShape(
186 "new_tpcihs6", "tpcihs1-(tpcihs2+tpcihs3)-(tpcihs4:trans2)-(tpcihs4:trans3)-(bcutil1:bcutil1_tr)");
187
188 TRandom3 rnd(20240102);
189 long inRef = 0, inBox = 0;
190 for (int k = 0; k < 2000000; ++k) {
191 double x[3];
192 for (int i = 0; i < 3; ++i) {
193 x[i] = rnd.Uniform(-6., 6.);
194 }
195 if (std::abs(x[1] - p[1]) < 1e-9) {
196 continue;
197 }
198 const bool a = ref->Contains(x);
199 const bool b = box->Contains(x);
200 inRef += a;
201 inBox += b;
202 if (a != b) {
203 BOOST_REQUIRE_MESSAGE(false, "containment differs at (" << x[0] << "," << x[1] << "," << x[2] << ")");
204 }
205 }
206 // guards against both shapes being empty, which would make the comparison vacuous
207 BOOST_CHECK_GT(inRef, 0);
208 BOOST_CHECK_EQUAL(inRef, inBox);
209 delete geom;
210}
int32_t i
constexpr int p2()
constexpr int p1()
constexpr to accelerate the coordinates changing
uint16_t slope
Definition RawData.h:1
Collection of utility functions for TGeo.
static void makeHalfSpaceBox(const char *name, const double p[3], const double n[3], double reach)
GLdouble n
Definition glcorearb.h:1982
GLint GLenum GLint x
Definition glcorearb.h:403
GLboolean GLboolean GLboolean b
Definition glcorearb.h:1233
GLenum GLint * range
Definition glcorearb.h:1899
GLuint GLsizei const GLchar * label
Definition glcorearb.h:2519
GLboolean GLboolean GLboolean GLboolean a
Definition glcorearb.h:1233
GLint ref
Definition glcorearb.h:291
GLsizei const GLint * box
Definition glcorearb.h:4697
void compare(std::string_view s1, std::string_view s2)
BOOST_AUTO_TEST_CASE(HalfSpaceBox_reproduces_TGeoHalfSpace)
BOOST_CHECK_EQUAL(triggersD.size(), triggers.size())