Skip to content

Commit 98a55cf

Browse files
authored
Merge pull request #948 from beomki-yeo/direct-navigator
Implementing direct navigator
2 parents db21a1f + b56be12 commit 98a55cf

File tree

11 files changed

+884
-28
lines changed

11 files changed

+884
-28
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,372 @@
1+
/** Detray library, part of the ACTS project (R&D line)
2+
*
3+
* (c) 2025 CERN for the benefit of the ACTS project
4+
*
5+
* Mozilla Public License Version 2.0
6+
*/
7+
8+
#pragma once
9+
10+
// Project include(s)
11+
#include "detray/core/detector.hpp"
12+
#include "detray/definitions/algorithms.hpp"
13+
#include "detray/definitions/containers.hpp"
14+
#include "detray/definitions/detail/qualifiers.hpp"
15+
#include "detray/definitions/indexing.hpp"
16+
#include "detray/definitions/units.hpp"
17+
#include "detray/geometry/barcode.hpp"
18+
#include "detray/navigation/intersection/intersection.hpp"
19+
#include "detray/navigation/intersection/ray_intersector.hpp"
20+
#include "detray/navigation/intersection_kernel.hpp"
21+
#include "detray/navigation/navigation_config.hpp"
22+
#include "detray/navigation/navigator.hpp"
23+
#include "detray/tracks/ray.hpp"
24+
#include "detray/utils/ranges.hpp"
25+
26+
namespace detray {
27+
28+
template <typename detector_t>
29+
class direct_navigator {
30+
31+
public:
32+
using detector_type = detector_t;
33+
using context_type = detector_type::geometry_context;
34+
using algebra_type = typename detector_type::algebra_type;
35+
using scalar_type = dscalar<algebra_type>;
36+
using intersection_type =
37+
intersection2D<typename detector_t::surface_type,
38+
typename detector_t::algebra_type, false>;
39+
40+
class state {
41+
42+
friend struct intersection_update<ray_intersector>;
43+
44+
using candidate_t = intersection_type;
45+
46+
public:
47+
using sequence_t = vecmem::device_vector<detray::geometry::barcode>;
48+
using detector_type = direct_navigator::detector_type;
49+
using nav_link_type =
50+
typename detector_type::surface_type::navigation_link;
51+
using view_type = dvector_view<detray::geometry::barcode>;
52+
53+
state() = delete;
54+
55+
DETRAY_HOST_DEVICE explicit state(const detector_t &det,
56+
const view_type &sequence)
57+
: m_detector(&det), m_sequence(sequence) {
58+
59+
m_it = m_sequence.begin();
60+
m_it_rev = m_sequence.rbegin();
61+
}
62+
63+
/// Scalar representation of the navigation state,
64+
/// @returns distance to next
65+
DETRAY_HOST_DEVICE
66+
scalar_type operator()() const {
67+
return static_cast<scalar_type>(direction()) * target().path;
68+
}
69+
70+
/// @returns a pointer of detector
71+
DETRAY_HOST_DEVICE
72+
const detector_type &detector() const { return (*m_detector); }
73+
74+
/// @returns the navigation heartbeat
75+
DETRAY_HOST_DEVICE
76+
bool is_alive() const { return m_heartbeat; }
77+
78+
/// @returns current/previous object that was reached
79+
DETRAY_HOST_DEVICE
80+
inline auto current() const -> const candidate_t & {
81+
return m_candidate_prev;
82+
}
83+
84+
/// @returns next object that we want to reach (current target) - const
85+
DETRAY_HOST_DEVICE
86+
inline auto target() const -> const candidate_t & {
87+
return m_candidate;
88+
}
89+
90+
DETRAY_HOST_DEVICE
91+
inline auto target() -> candidate_t & { return m_candidate; }
92+
93+
DETRAY_HOST_DEVICE
94+
void update_candidate(bool update_candidate_prev = true) {
95+
96+
if (update_candidate_prev) {
97+
m_candidate_prev = m_candidate;
98+
}
99+
100+
if (!is_complete()) {
101+
m_candidate.sf_desc = m_detector->surface(get_target_barcode());
102+
m_candidate.volume_link =
103+
tracking_surface{*m_detector, m_candidate.sf_desc}
104+
.volume_link();
105+
m_candidate.path = std::numeric_limits<scalar_type>::max();
106+
set_volume(m_candidate.volume_link);
107+
}
108+
}
109+
110+
/// @returns current detector surface the navigator is on
111+
/// (cannot be used when not on surface) - const
112+
DETRAY_HOST_DEVICE
113+
inline auto get_surface() const -> tracking_surface<detector_type> {
114+
assert(is_on_surface());
115+
return tracking_surface<detector_type>{*m_detector,
116+
current().sf_desc};
117+
}
118+
119+
/// @returns current navigation status - const
120+
DETRAY_HOST_DEVICE
121+
inline auto status() const -> navigation::status { return m_status; }
122+
123+
DETRAY_HOST_DEVICE
124+
inline bool is_init() {
125+
if (m_direction == navigation::direction::e_forward) {
126+
return m_it == m_sequence.begin();
127+
} else {
128+
return m_it_rev == m_sequence.rbegin();
129+
}
130+
}
131+
132+
DETRAY_HOST_DEVICE
133+
detray::geometry::barcode get_target_barcode() const {
134+
if (m_direction == navigation::direction::e_forward) {
135+
return *m_it;
136+
} else {
137+
return *m_it_rev;
138+
}
139+
}
140+
141+
DETRAY_HOST_DEVICE
142+
detray::geometry::barcode get_current_barcode() const {
143+
if (m_direction == navigation::direction::e_forward) {
144+
return *(m_it - 1);
145+
} else {
146+
return *(m_it_rev - 1);
147+
}
148+
}
149+
150+
/// Advance the iterator
151+
DETRAY_HOST_DEVICE
152+
void next() {
153+
if (m_direction == navigation::direction::e_forward) {
154+
m_it++;
155+
} else {
156+
m_it_rev++;
157+
}
158+
}
159+
160+
/// @return true if the iterator reaches the end of vector
161+
DETRAY_HOST_DEVICE
162+
bool is_complete() {
163+
if ((m_direction == navigation::direction::e_forward) &&
164+
m_it == m_sequence.end()) {
165+
return true;
166+
} else if ((m_direction == navigation::direction::e_backward) &&
167+
m_it_rev == m_sequence.rend()) {
168+
return true;
169+
}
170+
return false;
171+
}
172+
173+
/// @returns current navigation direction - const
174+
DETRAY_HOST_DEVICE
175+
inline auto direction() const -> navigation::direction {
176+
return m_direction;
177+
}
178+
179+
/// Helper method to check the track has reached a module surface
180+
DETRAY_HOST_DEVICE
181+
inline auto is_on_surface() const -> bool {
182+
return (m_status == navigation::status::e_on_module ||
183+
m_status == navigation::status::e_on_portal);
184+
}
185+
186+
/// Helper method to check if a candidate lies on a surface - const
187+
DETRAY_HOST_DEVICE inline auto is_on_surface(
188+
const intersection_type &candidate,
189+
const navigation::config &cfg) const -> bool {
190+
return (math::fabs(candidate.path) < cfg.path_tolerance);
191+
}
192+
193+
/// Helper method to check the track has encountered material
194+
DETRAY_HOST_DEVICE
195+
inline auto encountered_sf_material() const -> bool {
196+
return (is_on_surface()) && (current().sf_desc.material().id() !=
197+
detector_t::materials::id::e_none);
198+
}
199+
200+
/// Helper method to check the track has reached a sensitive surface
201+
DETRAY_HOST_DEVICE
202+
inline auto is_on_sensitive() const -> bool {
203+
return (m_status == navigation::status::e_on_module) &&
204+
(get_current_barcode().id() == surface_id::e_sensitive);
205+
}
206+
207+
DETRAY_HOST_DEVICE
208+
inline auto barcode() const -> geometry::barcode {
209+
return m_candidate_prev.sf_desc.barcode();
210+
}
211+
212+
/// @returns current volume (index) - const
213+
DETRAY_HOST_DEVICE
214+
inline auto volume() const -> nav_link_type { return m_volume_index; }
215+
216+
/// Set start/new volume
217+
DETRAY_HOST_DEVICE
218+
inline void set_volume(dindex v) {
219+
assert(detail::is_invalid_value(static_cast<nav_link_type>(v)) ||
220+
v < detector().volumes().size());
221+
m_volume_index = static_cast<nav_link_type>(v);
222+
}
223+
224+
DETRAY_HOST_DEVICE
225+
inline auto abort() -> bool {
226+
m_status = navigation::status::e_abort;
227+
m_heartbeat = false;
228+
return m_heartbeat;
229+
}
230+
231+
/// @returns current detector volume of the navigation stream
232+
DETRAY_HOST_DEVICE
233+
inline auto get_volume() const {
234+
return tracking_volume<detector_type>{*m_detector, m_volume_index};
235+
}
236+
237+
/// Set direction
238+
DETRAY_HOST_DEVICE
239+
inline void set_direction(const navigation::direction dir) {
240+
m_direction = dir;
241+
}
242+
243+
DETRAY_HOST_DEVICE
244+
inline void set_no_trust() { return; }
245+
246+
DETRAY_HOST_DEVICE
247+
inline void set_full_trust() { return; }
248+
249+
DETRAY_HOST_DEVICE
250+
inline void set_high_trust() { return; }
251+
252+
DETRAY_HOST_DEVICE
253+
inline void set_fair_trust() { return; }
254+
255+
/// Intersection candidate
256+
candidate_t m_candidate;
257+
candidate_t m_candidate_prev;
258+
259+
/// Detector pointer
260+
const detector_type *m_detector{nullptr};
261+
262+
/// Index in the detector volume container of current navigation volume
263+
nav_link_type m_volume_index{0u};
264+
265+
/// Target surfaces
266+
sequence_t m_sequence;
267+
268+
// iterator for forward direction
269+
typename sequence_t::iterator m_it;
270+
271+
// iterator for backward direction
272+
typename sequence_t::reverse_iterator m_it_rev;
273+
274+
/// The navigation direction
275+
navigation::direction m_direction{navigation::direction::e_forward};
276+
277+
/// The navigation status
278+
navigation::status m_status{navigation::status::e_unknown};
279+
280+
/// Step size when the valid intersection is not found for the target
281+
scalar_type safe_step_size = 10.f * unit<scalar_type>::mm;
282+
283+
/// Heartbeat of this navigation flow signals navigation is alive
284+
bool m_heartbeat{false};
285+
};
286+
287+
template <typename track_t>
288+
DETRAY_HOST_DEVICE inline void init(const track_t &track, state &navigation,
289+
const navigation::config &cfg,
290+
const context_type &ctx) const {
291+
292+
if (navigation.is_complete()) {
293+
navigation.m_heartbeat = false;
294+
return;
295+
}
296+
297+
navigation.m_heartbeat = true;
298+
navigation.update_candidate(!navigation.is_init());
299+
update(track, navigation, cfg, ctx);
300+
}
301+
302+
template <typename track_t>
303+
DETRAY_HOST_DEVICE inline bool update(
304+
const track_t &track, state &navigation, const navigation::config &cfg,
305+
const context_type &ctx = {},
306+
const bool is_before_actor_run = true) const {
307+
308+
if (navigation.is_complete()) {
309+
navigation.m_heartbeat = false;
310+
return true;
311+
}
312+
313+
assert(!navigation.get_target_barcode().is_invalid());
314+
update_intersection(track, navigation, cfg, ctx);
315+
316+
if (is_before_actor_run) {
317+
if (navigation.is_on_surface(navigation.target(), cfg)) {
318+
navigation.m_status = (navigation.target().sf_desc.is_portal())
319+
? navigation::status::e_on_portal
320+
: navigation::status::e_on_module;
321+
navigation.next();
322+
navigation.update_candidate(true);
323+
assert(navigation.is_on_surface(navigation.current(), cfg));
324+
325+
if (!navigation.is_complete()) {
326+
update_intersection(track, navigation, cfg, ctx);
327+
}
328+
329+
// Return true to reset the step size
330+
return true;
331+
}
332+
}
333+
334+
// Otherwise the track is moving towards a surface
335+
navigation.m_status = navigation::status::e_towards_object;
336+
337+
// Return false to scale the step size with RK4
338+
return false;
339+
}
340+
341+
template <typename track_t>
342+
DETRAY_HOST_DEVICE inline void update_intersection(
343+
const track_t &track, state &navigation, const navigation::config &cfg,
344+
const context_type &ctx = {}) const {
345+
346+
const auto &det = navigation.detector();
347+
const auto sf = tracking_surface{det, navigation.target().sf_desc};
348+
349+
const bool res =
350+
sf.template visit_mask<intersection_update<ray_intersector>>(
351+
detail::ray<algebra_type>(
352+
track.pos(),
353+
static_cast<scalar_type>(navigation.direction()) *
354+
track.dir()),
355+
navigation.target(), det.transform_store(), ctx,
356+
darray<scalar_type, 2>{cfg.min_mask_tolerance,
357+
cfg.max_mask_tolerance},
358+
static_cast<scalar_type>(cfg.mask_tolerance_scalor),
359+
static_cast<scalar_type>(cfg.overstep_tolerance));
360+
361+
// If an intersection is not found, proceed the track with safe step
362+
// size
363+
if (!res) {
364+
const auto path = navigation.target().path;
365+
navigation.update_candidate(false);
366+
navigation.target().path =
367+
math::copysign(navigation.safe_step_size, path);
368+
}
369+
}
370+
};
371+
372+
} // namespace detray

0 commit comments

Comments
 (0)