11/* * Detray library, part of the ACTS project (R&D line)
22 *
3- * (c) 2022-2024 CERN for the benefit of the ACTS project
3+ * (c) 2022-2025 CERN for the benefit of the ACTS project
44 *
55 * Mozilla Public License Version 2.0
66 */
1919#include " detray/navigation/intersection/ray_cylinder_intersector.hpp"
2020#include " detray/tracks/helix.hpp"
2121#include " detray/utils/invalid_values.hpp"
22-
23- // System include(s)
24- #include < limits>
25- #include < type_traits>
22+ #include " detray/utils/root_finding.hpp"
2623
2724namespace detray {
2825
@@ -39,12 +36,11 @@ template <algebra::concepts::aos algebra_t>
3936struct helix_intersector_impl <cylindrical2D<algebra_t >, algebra_t >
4037 : public ray_intersector_impl<cylindrical2D<algebra_t >, algebra_t ,
4138 intersection::contains_pos> {
39+ private:
40+ using scalar_t = dscalar<algebra_t >;
4241
42+ public:
4343 using algebra_type = algebra_t ;
44- using scalar_type = dscalar<algebra_t >;
45- using point3_type = dpoint3D<algebra_t >;
46- using vector3_type = dvector3D<algebra_t >;
47- using transform3_type = dtransform3D<algebra_t >;
4844
4945 template <typename surface_descr_t >
5046 using intersection_type =
@@ -58,204 +54,117 @@ struct helix_intersector_impl<cylindrical2D<algebra_t>, algebra_t>
5854
5955 using result_type = darray<intersection_point_err<algebra_t >, n_solutions>;
6056
61- // / Operator function to find intersections between ray and planar mask
57+ // / Operator function to find intersections between a helix and a cylinder
58+ // / surface
6259 // /
63- // / @param ray is the input ray trajectory
64- // / @param sf the surface handle the mask is associated with
65- // / @param mask is the input mask that defines the surface extent
60+ // / @param h is the input helix trajectory
6661 // / @param trf is the surface placement transform
67- // / @param mask_tolerance is the tolerance for mask edges
68- // / @param overstep_tol negative cutoff for the path
62+ // / @param mask is the input mask that defines the surface extent
6963 // /
7064 // / @return the intersection
7165 template <typename mask_t >
7266 DETRAY_HOST_DEVICE constexpr result_type point_of_intersection (
73- const trajectory_type<algebra_t > &h, const transform3_type &trf,
74- const mask_t &mask, const scalar_type = 0 .f) const {
67+ const trajectory_type<algebra_t > &h, const dtransform3D<algebra_t > &trf,
68+ const mask_t &mask, const scalar_t = 0 .f) const {
69+
70+ using point3_t = dpoint3D<algebra_t >;
71+ using vector3_t = dvector3D<algebra_t >;
7572
7673 result_type ret{};
7774
78- if (!run_rtsafe) {
79- // Get the surface placement
80- const auto &sm = trf.matrix ();
81- // Cylinder z axis
82- const vector3_type sz = getter::vector<3 >(sm, 0u , 2u );
83- // Cylinder centre
84- const point3_type sc = getter::vector<3 >(sm, 0u , 3u );
85-
86- // Starting point on the helix for the Newton iteration
87- // The mask is a cylinder -> it provides its radius as the first
88- // value
89- const scalar_type r{mask[cylinder2D::e_r]};
90-
91- // Try to guess the best starting positions for the iteration
92-
93- // Direction of the track at the helix origin
94- const auto h_dir = h.dir (0 .f );
95- // Default starting path length for the Newton iteration (assumes
96- // concentric cylinder)
97- const scalar_type default_s{r * vector::perp (h_dir)};
98-
99- // Initial helix path length parameter
100- darray<scalar_type, 2 > paths{default_s, default_s};
101-
102- // try to guess good starting path by calculating the intersection
103- // path of the helix tangential with the cylinder. This only has a
104- // chance of working for tracks with reasonably high p_T !
105- detail::ray<algebra_t > t{h.pos (), h.time (), h_dir, h.qop ()};
106- const auto qe = this ->solve_intersection (t, mask, trf);
107-
108- // Obtain both possible solutions by looping over the (different)
109- // starting positions
110- auto n_runs{static_cast <unsigned int >(qe.solutions ())};
111-
112- // Note: the default path length might be smaller than either
113- // solution
114- switch (qe.solutions ()) {
115- case 2 :
116- paths[1 ] = qe.larger ();
117- // If there are two solutions, reuse the case for a single
118- // solution to setup the intersection with the smaller path
119- // in ret[0]
120- [[fallthrough]];
121- case 1 : {
122- paths[0 ] = qe.smaller ();
123- break ;
124- }
125- // Even if the ray is parallel to the cylinder, the helix
126- // might still hit it
127- default : {
128- n_runs = 2u ;
129- paths[0 ] = r;
130- paths[1 ] = -r;
131- }
75+ // Cylinder z axis
76+ const vector3_t sz = trf.z ();
77+ // Cylinder centre
78+ const point3_t sc = trf.translation ();
79+
80+ // Starting point on the helix for the Newton iteration
81+ // The mask is a cylinder -> it provides its radius as the first
82+ // value
83+ const scalar_t r{mask[cylinder2D::e_r]};
84+
85+ // Try to guess the best starting positions for the iteration
86+
87+ // Direction of the track at the helix origin
88+ const auto h_dir = h.dir (0 .5f * r);
89+ // Default starting path length for the Newton iteration (assumes
90+ // concentric cylinder)
91+ const scalar_t default_s{r * vector::perp (h_dir)};
92+
93+ // Initial helix path length parameter
94+ darray<scalar_t , 2 > paths{default_s, default_s};
95+
96+ // try to guess good starting path by calculating the intersection
97+ // path of the helix tangential with the cylinder. This only has a
98+ // chance of working for tracks with reasonably high p_T !
99+ detail::ray<algebra_t > t{h.pos (), h.time (), h_dir, h.qop ()};
100+ const auto qe = this ->solve_intersection (t, mask, trf);
101+
102+ // Obtain both possible solutions by looping over the (different)
103+ // starting positions
104+ auto n_runs{static_cast <unsigned int >(qe.solutions ())};
105+
106+ // Note: the default path length might be smaller than either
107+ // solution
108+ switch (qe.solutions ()) {
109+ case 2 :
110+ paths[1 ] = qe.larger ();
111+ // If there are two solutions, reuse the case for a single
112+ // solution to setup the intersection with the smaller path
113+ // in ret[0]
114+ [[fallthrough]];
115+ case 1 : {
116+ paths[0 ] = qe.smaller ();
117+ break ;
132118 }
133-
134- for (unsigned int i = 0u ; i < n_runs; ++i) {
135-
136- scalar_type &s = paths[i];
137-
138- // Path length in the previous iteration step
139- scalar_type s_prev{0 .f };
140-
141- // f(s) = ((h.pos(s) - sc) x sz)^2 - r^2 == 0
142- // Run the iteration on s
143- std::size_t n_tries{0u };
144- while (math::fabs (s - s_prev) > convergence_tolerance &&
145- n_tries < max_n_tries) {
146-
147- // f'(s) = 2 * ( (h.pos(s) - sc) x sz) * (h.dir(s) x sz) )
148- const vector3_type crp = vector::cross (h.pos (s) - sc, sz);
149- const scalar_type denom{
150- 2 .f * vector::dot (crp, vector::cross (h.dir (s), sz))};
151-
152- // No intersection can be found if dividing by zero
153- if (denom == 0 .f ) {
154- return {};
155- }
156-
157- // x_n+1 = x_n - f(s) / f'(s)
158- s_prev = s;
159- s -= (vector::dot (crp, crp) - r * r) / denom;
160-
161- ++n_tries;
162- }
163- // No intersection found within max number of trials
164- if (n_tries == max_n_tries) {
165- return {};
166- }
167-
168- ret[i] = {s, h.pos (s), s - s_prev};
119+ default : {
120+ n_runs = 2u ;
121+ paths[0 ] = r;
122+ paths[1 ] = -r;
169123 }
124+ }
170125
171- return ret;
172- } else {
173- // Cylinder z axis
174- const vector3_type sz = trf.z ();
175- // Cylinder centre
176- const point3_type sc = trf.translation ();
177-
178- // Starting point on the helix for the Newton iteration
179- // The mask is a cylinder -> it provides its radius as the first
180- // value
181- const scalar_type r{mask[cylinder2D::e_r]};
182-
183- // Try to guess the best starting positions for the iteration
184-
185- // Direction of the track at the helix origin
186- const auto h_dir = h.dir (0 .5f * r);
187- // Default starting path length for the Newton iteration (assumes
188- // concentric cylinder)
189- const scalar_type default_s{r * vector::perp (h_dir)};
190-
191- // Initial helix path length parameter
192- darray<scalar_type, 2 > paths{default_s, default_s};
193-
194- // try to guess good starting path by calculating the intersection
195- // path of the helix tangential with the cylinder. This only has a
196- // chance of working for tracks with reasonably high p_T !
197- detail::ray<algebra_t > t{h.pos (), h.time (), h_dir, h.qop ()};
198- const auto qe = this ->solve_intersection (t, mask, trf);
199-
200- // Obtain both possible solutions by looping over the (different)
201- // starting positions
202- auto n_runs{static_cast <unsigned int >(qe.solutions ())};
203-
204- // Note: the default path length might be smaller than either
205- // solution
206- switch (qe.solutions ()) {
207- case 2 :
208- paths[1 ] = qe.larger ();
209- // If there are two solutions, reuse the case for a single
210- // solution to setup the intersection with the smaller path
211- // in ret[0]
212- [[fallthrough]];
213- case 1 : {
214- paths[0 ] = qe.smaller ();
215- break ;
216- }
217- default : {
218- n_runs = 2u ;
219- paths[0 ] = r;
220- paths[1 ] = -r;
221- }
222- }
223-
224- // / Evaluate the function and its derivative at the point @param x
225- auto cyl_inters_func = [&h, &r, &sz, &sc](const scalar_type x) {
226- const vector3_type crp = vector::cross (h.pos (x) - sc, sz);
227-
228- // f(s) = ((h.pos(s) - sc) x sz)^2 - r^2 == 0
229- const scalar_type f_s{(vector::dot (crp, crp) - r * r)};
230- // f'(s) = 2 * ( (h.pos(s) - sc) x sz) * (h.dir(s) x sz) )
231- const scalar_type df_s{
232- 2 .f * vector::dot (crp, vector::cross (h.dir (x), sz))};
233-
234- return std::make_tuple (f_s, df_s);
235- };
236-
237- for (unsigned int i = 0u ; i < n_runs; ++i) {
238-
239- const scalar_type &s_ini = paths[i];
240-
241- // Run the root finding algorithm
242- const auto [s, ds] = newton_raphson_safe (cyl_inters_func, s_ini,
243- convergence_tolerance,
244- max_n_tries, max_path);
245-
246- ret[i] = {s, h.pos (s), ds};
126+ // / Evaluate the function and its derivative at the point @param x
127+ auto cyl_inters_func = [&h, &r, &sz, &sc](const scalar_t x) {
128+ const vector3_t crp = vector::cross (h.pos (x) - sc, sz);
129+
130+ // f(s) = ((h.pos(s) - sc) x sz)^2 - r^2 == 0
131+ const scalar_t f_s{(vector::dot (crp, crp) - r * r)};
132+ // f'(s) = 2 * ( (h.pos(s) - sc) x sz) * (h.dir(s) x sz) )
133+ const scalar_t df_s{2 .f *
134+ vector::dot (crp, vector::cross (h.dir (x), sz))};
135+
136+ return std::make_tuple (f_s, df_s);
137+ };
138+
139+ for (unsigned int i = 0u ; i < n_runs; ++i) {
140+
141+ const scalar_t &s_ini = paths[i];
142+
143+ // Run the root finding algorithm
144+ scalar_t s;
145+ scalar_t ds;
146+ if (run_rtsafe) {
147+ std::tie (s, ds) = newton_raphson_safe (cyl_inters_func, s_ini,
148+ convergence_tolerance,
149+ max_n_tries, max_path);
150+ } else {
151+ std::tie (s, ds) = newton_raphson (cyl_inters_func, s_ini,
152+ convergence_tolerance,
153+ max_n_tries, max_path);
247154 }
248155
249- return ret;
156+ ret[i] = {s, h. pos (s), ds} ;
250157 }
158+
159+ return ret;
251160 }
252161
253162 // / Tolerance for convergence
254- scalar_type convergence_tolerance{1 .f * unit<scalar_type >::um};
163+ scalar_t convergence_tolerance{1 .f * unit<scalar_t >::um};
255164 // Guard against inifinite loops
256165 std::size_t max_n_tries{1000u };
257166 // Early exit, if the intersection is too far away
258- scalar_type max_path{5 .f * unit<scalar_type >::m};
167+ scalar_t max_path{5 .f * unit<scalar_t >::m};
259168 // Complement the Newton algorithm with Bisection steps
260169 bool run_rtsafe{true };
261170};
@@ -264,14 +173,7 @@ template <concepts::algebra algebra_t>
264173struct helix_intersector_impl <concentric_cylindrical2D<algebra_t >, algebra_t >
265174 : public helix_intersector_impl<cylindrical2D<algebra_t >, algebra_t > {
266175
267- using base_type =
268- helix_intersector_impl<cylindrical2D<algebra_t >, algebra_t >;
269-
270176 using algebra_type = algebra_t ;
271- using scalar_type = dscalar<algebra_t >;
272- using point3_type = dpoint3D<algebra_t >;
273- using vector3_type = dvector3D<algebra_t >;
274- using transform3_type = dtransform3D<algebra_t >;
275177
276178 template <typename surface_descr_t >
277179 using intersection_type =
@@ -285,24 +187,25 @@ struct helix_intersector_impl<concentric_cylindrical2D<algebra_t>, algebra_t>
285187
286188 using result_type = intersection_point_err<algebra_t >;
287189
288- // / Operator function to find intersections between ray and planar mask
190+ // / Operator function to find intersections between helix and a
191+ // / concentric cylinder surface
289192 // /
290- // / @param ray is the input ray trajectory
291- // / @param sf the surface handle the mask is associated with
292- // / @param mask is the input mask that defines the surface extent
193+ // / @param h is the input helix trajectory
293194 // / @param trf is the surface placement transform
294- // / @param mask_tolerance is the tolerance for mask edges
295- // / @param overstep_tol negative cutoff for the path
195+ // / @param mask is the input mask that defines the surface extent
296196 // /
297197 // / @return the intersection
298198 template <typename mask_t >
299199 DETRAY_HOST_DEVICE constexpr result_type point_of_intersection (
300- const trajectory_type<algebra_t > &h, const transform3_type &trf,
301- const mask_t &mask, const scalar_type = 0 .f) const {
200+ const trajectory_type<algebra_t > &h, const dtransform3D<algebra_t > &trf,
201+ const mask_t &mask, const dscalar<algebra_t > = 0 .f) const {
202+
203+ using base_t =
204+ helix_intersector_impl<cylindrical2D<algebra_t >, algebra_t >;
302205
303206 // Array of two solutions
304- typename base_type ::result_type results =
305- base_type ::point_of_intersection (h, trf, mask, 0 .f );
207+ typename base_t ::result_type results =
208+ base_t ::point_of_intersection (h, trf, mask, 0 .f );
306209
307210 // For portals, only take the solution along the helix direction,
308211 // not the one in the opposite direction
0 commit comments