Skip to content

Commit 13d4c99

Browse files
authored
ref: Reuse ray/helix scan results and switch to random track generator (#713)
This PR adds a simple whiteboard to the validation so that the ray and helix scan traces can be reused by the navigation tests, which reduces execution time of the tests a lot. Also allows to add new scans in the future by moving the surface loop into a distinct functor. Switches the navigation validation to use the random_track_generator to avoid effects from detector symmetry and cleans that generator ups a bit (mostly setting a default seed that is not zero). With the random tracks, the number of mismatches between the Newton intersector and the navigator increased, so I adapted the mask tolerances in both of them dynamically. In the Newton solver, the an estimation of the approximation error gets projected onto the tangential surface of the mask at the track position and in the navigator, the mask tolerance gets scaled by the distance of the track to the surface (per mille of the path) and then clamped to a configurable min-max range. That has the advantage, that at long distances to the surface, when the ray is a poor approximation to the curved track, the mask tolerance is very large, while it goes to zero as we approach the candidate, ensuring a precise decision. The minimal mask tolerance is the machine epsilon by default, but be set in a propagation executable e.g. to the mean position uncertainty of the stepper or to what the CKF needs. Fixes a few smaller bugs and tightens the portals around the sensitive surfaces in the toy detector for better navigation. Clamps the phi values in the track generators to -pi, pi. The detector integration tests now run 10000 helices at p_T=1GeV, except for the wire chamber which starts failing for p_T<3Gev. That needs some more investigation. I also discovered, that the CUDA propagation test was essentially not testing anything, since the host propagation silently failed. I set that test back up, but found discrepancies in the number of track position in single precision (double precision ran fine), so I configured fewer tracks and added step size constraints until it worked. That test should probably get an overhaul at some point.
1 parent f9b8817 commit 13d4c99

File tree

60 files changed

+1488
-737
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+1488
-737
lines changed

core/include/detray/navigation/detail/helix.hpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ class helix {
6767
_h0 = vector::normalize(*_mag_field);
6868

6969
// Normalized tangent vector
70-
_t0 = dir;
70+
_t0 = vector::normalize(dir);
7171

7272
// Momentum
7373
const vector3_type mom =

core/include/detray/navigation/intersection/helix_cylinder_intersector.hpp

+39-12
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,14 @@ struct helix_intersector_impl<cylindrical2D<algebra_t>, algebra_t>
6262
DETRAY_HOST_DEVICE inline std::array<intersection_type<surface_descr_t>, 2>
6363
operator()(const helix_type &h, const surface_descr_t &sf_desc,
6464
const mask_t &mask, const transform3_type &trf,
65-
const scalar_type mask_tolerance = 0.f,
65+
const std::array<scalar_type, 2u> mask_tolerance =
66+
{detail::invalid_value<scalar_type>(),
67+
detail::invalid_value<scalar_type>()},
6668
const scalar_type = 0.f) const {
6769

70+
assert((mask_tolerance[0] == mask_tolerance[1]) &&
71+
"Helix intersectors use only one mask tolerance value");
72+
6873
std::array<intersection_type<surface_descr_t>, 2> ret;
6974

7075
// Guard against inifinite loops
@@ -117,7 +122,7 @@ struct helix_intersector_impl<cylindrical2D<algebra_t>, algebra_t>
117122
for (unsigned int i = 0u; i < n_runs; ++i) {
118123

119124
scalar_type &s = paths[i];
120-
intersection_type<surface_descr_t> &is = ret[i];
125+
intersection_type<surface_descr_t> &sfi = ret[i];
121126

122127
// Path length in the previous iteration step
123128
scalar_type s_prev{0.f};
@@ -150,26 +155,48 @@ struct helix_intersector_impl<cylindrical2D<algebra_t>, algebra_t>
150155
}
151156

152157
// Build intersection struct from helix parameters
153-
is.path = s;
158+
sfi.path = s;
154159
const auto p3 = h.pos(s);
155-
is.local = mask.to_local_frame(trf, p3);
156-
is.status = mask.is_inside(is.local, mask_tolerance);
160+
sfi.local = mask.to_local_frame(trf, p3);
161+
sfi.cos_incidence_angle = vector::dot(
162+
mask.local_frame().normal(trf, sfi.local), h.dir(s));
163+
164+
scalar_type tol{mask_tolerance[1]};
165+
if (detail::is_invalid_value(tol)) {
166+
// Due to floating point errors this can be negative if cos ~ 1
167+
const scalar_type sin_inc2{math::abs(
168+
1.f - sfi.cos_incidence_angle * sfi.cos_incidence_angle)};
169+
170+
tol = math::abs((s - s_prev) * math::sqrt(sin_inc2));
171+
}
172+
sfi.status = mask.is_inside(sfi.local, tol);
157173

158174
// Compute some additional information if the intersection is valid
159-
if (is.status == intersection::status::e_inside) {
160-
is.sf_desc = sf_desc;
161-
is.direction = math::signbit(s)
162-
? intersection::direction::e_opposite
163-
: intersection::direction::e_along;
164-
is.volume_link = mask.volume_link();
175+
if (sfi.status == intersection::status::e_inside) {
176+
sfi.sf_desc = sf_desc;
177+
sfi.direction = math::signbit(s)
178+
? intersection::direction::e_opposite
179+
: intersection::direction::e_along;
180+
sfi.volume_link = mask.volume_link();
165181
}
166182
}
167183

168184
return ret;
169185
}
170186

187+
/// Interface to use fixed mask tolerance
188+
template <typename surface_descr_t, typename mask_t>
189+
DETRAY_HOST_DEVICE inline std::array<intersection_type<surface_descr_t>, 2>
190+
operator()(const helix_type &h, const surface_descr_t &sf_desc,
191+
const mask_t &mask, const transform3_type &trf,
192+
const scalar_type mask_tolerance,
193+
const scalar_type = 0.f) const {
194+
return this->operator()(h, sf_desc, mask, trf, {mask_tolerance, 0.f},
195+
0.f);
196+
}
197+
171198
/// Tolerance for convergence
172-
scalar_type convergence_tolerance{1e-3f};
199+
scalar_type convergence_tolerance{1.f * unit<scalar_type>::um};
173200
};
174201

175202
template <typename algebra_t>

core/include/detray/navigation/intersection/helix_line_intersector.hpp

+29-5
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,15 @@ struct helix_intersector_impl<line2D<algebra_t>, algebra_t> {
5454
template <typename surface_descr_t, typename mask_t>
5555
DETRAY_HOST_DEVICE inline intersection_type<surface_descr_t> operator()(
5656
const helix_type &h, const surface_descr_t &sf_desc, const mask_t &mask,
57-
const transform3_type &trf, const scalar_type mask_tolerance = 0.f,
57+
const transform3_type &trf,
58+
const std::array<scalar_type, 2u> mask_tolerance =
59+
{detail::invalid_value<scalar_type>(),
60+
detail::invalid_value<scalar_type>()},
5861
const scalar_type = 0.f) const {
5962

63+
assert((mask_tolerance[0] == mask_tolerance[1]) &&
64+
"Helix intersectors use only one mask tolerance value");
65+
6066
intersection_type<surface_descr_t> sfi;
6167

6268
// Guard against inifinite loops
@@ -147,9 +153,17 @@ struct helix_intersector_impl<line2D<algebra_t>, algebra_t> {
147153
// Build intersection struct from helix parameters
148154
sfi.path = s;
149155
sfi.local = mask.to_local_frame(trf, h.pos(s), h.dir(s));
150-
151-
const point3_type local = mask.to_local_frame(trf, h.pos(s), h.dir(s));
152-
sfi.status = mask.is_inside(local, mask_tolerance);
156+
sfi.cos_incidence_angle =
157+
vector::dot(mask.local_frame().normal(trf, sfi.local), h.dir(s));
158+
scalar_type tol{mask_tolerance[1]};
159+
if (detail::is_invalid_value(tol)) {
160+
// Due to floating point errors this can be negative if cos ~ 1
161+
const scalar_type sin_inc2{math::abs(
162+
1.f - sfi.cos_incidence_angle * sfi.cos_incidence_angle)};
163+
164+
tol = math::abs((s - s_prev) * math::sqrt(sin_inc2));
165+
}
166+
sfi.status = mask.is_inside(sfi.local, tol);
153167

154168
// Compute some additional information if the intersection is valid
155169
if (sfi.status == intersection::status::e_inside) {
@@ -163,8 +177,18 @@ struct helix_intersector_impl<line2D<algebra_t>, algebra_t> {
163177
return sfi;
164178
}
165179

180+
/// Interface to use fixed mask tolerance
181+
template <typename surface_descr_t, typename mask_t>
182+
DETRAY_HOST_DEVICE inline intersection_type<surface_descr_t> operator()(
183+
const helix_type &h, const surface_descr_t &sf_desc, const mask_t &mask,
184+
const transform3_type &trf, const scalar_type mask_tolerance,
185+
const scalar_type = 0.f) const {
186+
return this->operator()(h, sf_desc, mask, trf, {mask_tolerance, 0.f},
187+
0.f);
188+
}
189+
166190
/// Tolerance for convergence
167-
scalar_type convergence_tolerance{1e-3f};
191+
scalar_type convergence_tolerance{1.f * unit<scalar_type>::um};
168192
};
169193

170194
} // namespace detray

core/include/detray/navigation/intersection/helix_plane_intersector.hpp

+30-3
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,15 @@ struct helix_intersector_impl<cartesian2D<algebra_t>, algebra_t> {
5656
template <typename surface_descr_t, typename mask_t>
5757
DETRAY_HOST_DEVICE inline intersection_type<surface_descr_t> operator()(
5858
const helix_type &h, const surface_descr_t &sf_desc, const mask_t &mask,
59-
const transform3_type &trf, const scalar_type mask_tolerance = 0.f,
59+
const transform3_type &trf,
60+
const std::array<scalar_type, 2u> mask_tolerance =
61+
{detail::invalid_value<scalar_type>(),
62+
detail::invalid_value<scalar_type>()},
6063
const scalar_type = 0.f) const {
6164

65+
assert((mask_tolerance[0] == mask_tolerance[1]) &&
66+
"Helix intersectors use only one mask tolerance value");
67+
6268
intersection_type<surface_descr_t> sfi;
6369

6470
// Guard against inifinite loops
@@ -100,7 +106,18 @@ struct helix_intersector_impl<cartesian2D<algebra_t>, algebra_t> {
100106
// Build intersection struct from helix parameters
101107
sfi.path = s;
102108
sfi.local = mask.to_local_frame(trf, h.pos(s), h.dir(s));
103-
sfi.status = mask.is_inside(sfi.local, mask_tolerance);
109+
sfi.cos_incidence_angle =
110+
vector::dot(mask.local_frame().normal(trf, sfi.local), h.dir(s));
111+
112+
scalar_type tol{mask_tolerance[1]};
113+
if (detail::is_invalid_value(tol)) {
114+
// Due to floating point errors this can be negative if cos ~ 1
115+
const scalar_type sin_inc2{math::abs(
116+
1.f - sfi.cos_incidence_angle * sfi.cos_incidence_angle)};
117+
118+
tol = math::abs((s - s_prev) * math::sqrt(sin_inc2));
119+
}
120+
sfi.status = mask.is_inside(sfi.local, tol);
104121

105122
// Compute some additional information if the intersection is valid
106123
if (sfi.status == intersection::status::e_inside) {
@@ -114,8 +131,18 @@ struct helix_intersector_impl<cartesian2D<algebra_t>, algebra_t> {
114131
return sfi;
115132
}
116133

134+
/// Interface to use fixed mask tolerance
135+
template <typename surface_descr_t, typename mask_t>
136+
DETRAY_HOST_DEVICE inline intersection_type<surface_descr_t> operator()(
137+
const helix_type &h, const surface_descr_t &sf_desc, const mask_t &mask,
138+
const transform3_type &trf, const scalar_type mask_tolerance,
139+
const scalar_type = 0.f) const {
140+
return this->operator()(h, sf_desc, mask, trf, {mask_tolerance, 0.f},
141+
0.f);
142+
}
143+
117144
/// Tolerance for convergence
118-
scalar_type convergence_tolerance{1e-3f};
145+
scalar_type convergence_tolerance{1.f * unit<scalar_type>::um};
119146
};
120147

121148
template <typename algebra_t>

core/include/detray/navigation/intersection/ray_concentric_cylinder_intersector.hpp

+20-3
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,9 @@ struct ray_concentric_cylinder_intersector {
5555
template <typename surface_descr_t, typename mask_t>
5656
DETRAY_HOST_DEVICE inline intersection_type<surface_descr_t> operator()(
5757
const ray_type &ray, const surface_descr_t &sf, const mask_t &mask,
58-
const transform3_type & /*trf*/, const scalar_type mask_tolerance = 0.f,
58+
const transform3_type & /*trf*/,
59+
const std::array<scalar_type, 2u> mask_tolerance =
60+
{0.f, 1.f * unit<scalar_type>::mm},
5961
const scalar_type overstep_tol = 0.f) const {
6062

6163
intersection_type<surface_descr_t> is;
@@ -110,7 +112,11 @@ struct ray_concentric_cylinder_intersector {
110112
is.path = t01[cindex];
111113
// In this case, the point has to be in cylinder3 coordinates
112114
// for the r-check
113-
is.status = mask.is_inside(is.local, mask_tolerance);
115+
// Tolerance: per mille of the distance
116+
is.status = mask.is_inside(
117+
is.local, math::max(mask_tolerance[0],
118+
math::min(mask_tolerance[1],
119+
1e-3f * math::abs(is.path))));
114120

115121
// prepare some additional information in case the intersection
116122
// is valid
@@ -131,6 +137,16 @@ struct ray_concentric_cylinder_intersector {
131137
return is;
132138
}
133139

140+
/// Interface to use fixed mask tolerance
141+
template <typename surface_descr_t, typename mask_t>
142+
DETRAY_HOST_DEVICE inline intersection_type<surface_descr_t> operator()(
143+
const ray_type &ray, const surface_descr_t &sf, const mask_t &mask,
144+
const transform3_type &trf, const scalar_type mask_tolerance,
145+
const scalar_type overstep_tol = 0.f) const {
146+
return this->operator()(ray, sf, mask, trf, {mask_tolerance, 0.f},
147+
overstep_tol);
148+
}
149+
134150
/// Operator function to find intersections between a ray and a 2D cylinder
135151
///
136152
/// @tparam mask_t is the input mask type
@@ -146,7 +162,8 @@ struct ray_concentric_cylinder_intersector {
146162
DETRAY_HOST_DEVICE inline void update(
147163
const ray_type &ray, intersection_type<surface_descr_t> &sfi,
148164
const mask_t &mask, const transform3_type &trf,
149-
const scalar_type mask_tolerance = 0.f,
165+
const std::array<scalar_type, 2u> &mask_tolerance =
166+
{0.f, 1.f * unit<scalar_type>::mm},
150167
const scalar_type overstep_tol = 0.f) const {
151168
sfi = this->operator()(ray, sfi.sf_desc, mask, trf, mask_tolerance,
152169
overstep_tol)[0];

core/include/detray/navigation/intersection/ray_cylinder_intersector.hpp

+26-5
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@ struct ray_intersector_impl<cylindrical2D<algebra_t>, algebra_t> {
5757
DETRAY_HOST_DEVICE inline std::array<intersection_type<surface_descr_t>, 2>
5858
operator()(const ray_type &ray, const surface_descr_t &sf,
5959
const mask_t &mask, const transform3_type &trf,
60-
const scalar_type mask_tolerance = 0.f,
60+
const std::array<scalar_type, 2u> mask_tolerance =
61+
{0.f, 100.f * unit<scalar_type>::um},
6162
const scalar_type overstep_tol = 0.f) const {
6263

6364
// One or both of these solutions might be invalid
@@ -89,6 +90,17 @@ struct ray_intersector_impl<cylindrical2D<algebra_t>, algebra_t> {
8990
return ret;
9091
}
9192

93+
/// Interface to use fixed mask tolerance
94+
template <typename surface_descr_t, typename mask_t>
95+
DETRAY_HOST_DEVICE inline std::array<intersection_type<surface_descr_t>, 2>
96+
operator()(const ray_type &ray, const surface_descr_t &sf,
97+
const mask_t &mask, const transform3_type &trf,
98+
const scalar_type mask_tolerance,
99+
const scalar_type overstep_tol = 0.f) const {
100+
return this->operator()(ray, sf, mask, trf, {mask_tolerance, 0.f},
101+
overstep_tol);
102+
}
103+
92104
/// Operator function to find intersections between a ray and a 2D cylinder
93105
///
94106
/// @tparam mask_t is the input mask type
@@ -103,7 +115,8 @@ struct ray_intersector_impl<cylindrical2D<algebra_t>, algebra_t> {
103115
DETRAY_HOST_DEVICE inline void update(
104116
const ray_type &ray, intersection_type<surface_descr_t> &sfi,
105117
const mask_t &mask, const transform3_type &trf,
106-
const scalar_type mask_tolerance = 0.f,
118+
const std::array<scalar_type, 2u> mask_tolerance =
119+
{0.f, 1.f * unit<scalar_type>::mm},
107120
const scalar_type overstep_tol = 0.f) const {
108121

109122
// One or both of these solutions might be invalid
@@ -155,8 +168,12 @@ struct ray_intersector_impl<cylindrical2D<algebra_t>, algebra_t> {
155168
DETRAY_HOST_DEVICE inline intersection_type<surface_descr_t>
156169
build_candidate(const ray_type &ray, mask_t &mask,
157170
const transform3_type &trf, const scalar_type path,
158-
const scalar_type mask_tolerance = 0.f,
159-
const scalar_type overstep_tol = 0.f) const {
171+
const std::array<scalar_type, 2u> mask_tolerance,
172+
const scalar_type overstep_tol) const {
173+
174+
assert((mask_tolerance[0] <= mask_tolerance[1]) &&
175+
"Minimal mask tolerance needs to be smaller or equal maximal "
176+
"mask tolerance");
160177

161178
intersection_type<surface_descr_t> is;
162179

@@ -170,7 +187,11 @@ struct ray_intersector_impl<cylindrical2D<algebra_t>, algebra_t> {
170187
const point3_type p3 = ro + is.path * rd;
171188

172189
is.local = mask.to_local_frame(trf, p3);
173-
is.status = mask.is_inside(is.local, mask_tolerance);
190+
// Tolerance: per mille of the distance
191+
is.status = mask.is_inside(
192+
is.local, math::max(mask_tolerance[0],
193+
math::min(mask_tolerance[1],
194+
1e-3f * math::abs(is.path))));
174195

175196
// prepare some additional information in case the intersection
176197
// is valid

core/include/detray/navigation/intersection/ray_cylinder_portal_intersector.hpp

+15-2
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,9 @@ struct ray_intersector_impl<concentric_cylindrical2D<algebra_t>, algebra_t>
6262
template <typename surface_descr_t, typename mask_t>
6363
DETRAY_HOST_DEVICE inline intersection_type<surface_descr_t> operator()(
6464
const ray_type &ray, const surface_descr_t &sf, const mask_t &mask,
65-
const transform3_type &trf, const scalar_type mask_tolerance = 0.f,
65+
const transform3_type &trf,
66+
const std::array<scalar_type, 2u> mask_tolerance =
67+
{0.f, 1.f * unit<scalar_type>::mm},
6668
const scalar_type overstep_tol = 0.f) const {
6769

6870
intersection_type<surface_descr_t> is;
@@ -87,6 +89,16 @@ struct ray_intersector_impl<concentric_cylindrical2D<algebra_t>, algebra_t>
8789
return is;
8890
}
8991

92+
/// Interface to use fixed mask tolerance
93+
template <typename surface_descr_t, typename mask_t>
94+
DETRAY_HOST_DEVICE inline intersection_type<surface_descr_t> operator()(
95+
const ray_type &ray, const surface_descr_t &sf, const mask_t &mask,
96+
const transform3_type &trf, const scalar_type mask_tolerance,
97+
const scalar_type overstep_tol = 0.f) const {
98+
return this->operator()(ray, sf, mask, trf, {mask_tolerance, 0.f},
99+
overstep_tol);
100+
}
101+
90102
/// Operator function to find intersections between a ray and a 2D cylinder
91103
///
92104
/// @tparam mask_t is the input mask type
@@ -101,7 +113,8 @@ struct ray_intersector_impl<concentric_cylindrical2D<algebra_t>, algebra_t>
101113
DETRAY_HOST_DEVICE inline void update(
102114
const ray_type &ray, intersection_type<surface_descr_t> &sfi,
103115
const mask_t &mask, const transform3_type &trf,
104-
const scalar_type mask_tolerance = 0.f,
116+
const std::array<scalar_type, 2u> &mask_tolerance =
117+
{0.f, 1.f * unit<scalar_type>::mm},
105118
const scalar_type overstep_tol = 0.f) const {
106119
sfi = this->operator()(ray, sfi.sf_desc, mask, trf, mask_tolerance,
107120
overstep_tol);

0 commit comments

Comments
 (0)