45 const scalar_t b, function_t &f,
47 const scalar_t k = 1.f) {
49 throw std::invalid_argument(
50 "Root bracketing: Not a valid start interval [" + std::to_string(a) +
51 ", " + std::to_string(b) +
"]");
54 scalar_t lower{a > b ? b : a};
55 scalar_t upper{a > b ? a : b};
58 scalar_t f_l{f(lower)};
59 scalar_t f_u{f(upper)};
60 std::size_t n_tries{0u};
62 DETRAY_DEBUG_HOST(
"Initial bracket: [" << lower <<
", " << upper <<
"]");
65 const auto check_bracket = [a, b, &bracket](std::size_t n, scalar_t fl,
66 scalar_t fu, scalar_t l,
68 if ((n == 1000u) || !std::isfinite(fl) || !std::isfinite(fu) ||
69 !std::isfinite(l) || !std::isfinite(u)) {
70 DETRAY_VERBOSE_HOST(
"Could not bracket a root (a="
71 << l <<
", b=" << u <<
", f(a)=" << fl
73 <<
", root might not exist). Running Newton-Raphson "
74 "without bisection.");
83 while (!math::signbit(f_l * f_u)) {
86 if (!check_bracket(n_tries, f_l, f_u, lower, upper)) {
89 scalar_t d{k * (upper - lower)};
91 if (math::fabs(f_l) < math::fabs(f_u)) {
101 if (!check_bracket(n_tries, f_l, f_u, lower, upper)) {
104 bracket = {lower, upper};
122 function_t &evaluate_func, scalar_t s,
124 const std::size_t max_n_tries = 1000u,
126 constexpr scalar_t inv{detail::invalid_value<scalar_t>()};
127 constexpr scalar_t epsilon{std::numeric_limits<scalar_t>::epsilon()};
129 DETRAY_DEBUG_HOST(
"Initial path estimate: s=" << s <<
"mm");
131 if (math::fabs(s) >= max_path) {
132 DETRAY_VERBOSE_HOST(
"Initial path estimate outside search area: s=" << s);
134 if (math::fabs(s) >= inv) {
135 const std::string err_msg{
"Initial path estimate invalid"};
136 DETRAY_FATAL_HOST(err_msg);
137 throw std::invalid_argument(err_msg);
141 scalar_t s_prev{0.f};
142 std::size_t n_tries{0u};
143 auto [f_s, df_s] = evaluate_func(s);
145 while (math::fabs(s - s_prev) > convergence_tolerance) {
147 if (math::fabs(f_s) < convergence_tolerance) {
148 DETRAY_DEBUG_HOST(
"Found: s = " << s <<
"mm, err = " << epsilon <<
"mm");
149 return std::make_pair(s, epsilon);
153 if (math::fabs(df_s) == 0.f) {
155 "Newton step encountered invalid derivative - skipping");
156 return std::make_pair(inv, inv);
164 std::tie(f_s, df_s) = evaluate_func(s);
169 if (n_tries >= max_n_tries) {
170 DETRAY_VERBOSE_HOST(
"Helix intersector did not converge after "
171 << n_tries <<
" steps - skipping");
172 return std::make_pair(inv, inv);
176 DETRAY_DEBUG_HOST(
"Found: s = " << s <<
"mm, err = " << math::fabs(s - s_prev)
178 return std::make_pair(s, math::fabs(s - s_prev));
194 function_t &evaluate_func, scalar_t s,
196 const std::size_t max_n_tries = 1000u,
198 constexpr scalar_t inv{detail::invalid_value<scalar_t>()};
199 constexpr scalar_t epsilon{std::numeric_limits<scalar_t>::epsilon()};
201 DETRAY_DEBUG_HOST(
"Initial path estimate: s=" << s <<
"mm");
204 auto f = [&evaluate_func](
const scalar_t x) {
205 auto [f_x, df_x] = evaluate_func(x);
210 if (math::fabs(s) >= max_path) {
212 "Initial path estimate outside search area: s=" << s <<
"mm");
214 if (math::fabs(s) >= inv) {
215 const std::string err_msg{
"Initial path estimate invalid"};
216 DETRAY_FATAL_HOST(err_msg);
217 throw std::invalid_argument(err_msg);
221 scalar_t a{math::fabs(s) == 0.f ? -0.2f : 0.8f * s};
222 scalar_t b{math::fabs(s) == 0.f ? 0.2f : 1.2f * s};
227 s = is_bracketed ? 0.5f * (br[1] + br[0]) : s;
230 DETRAY_VERBOSE_HOST(
"Bracketing failed for initial path estimate: s=" << s);
233 [[maybe_unused]]
auto [f_a, df_a] = evaluate_func(br[0]);
234 [[maybe_unused]]
auto [f_b, df_b] = evaluate_func(br[1]);
237 if (!math::signbit(f_a * f_b)) {
238 throw std::runtime_error(
239 "Incorrect bracket around root: No sign change!");
244 if ((math::fabs(br[0]) >= inv) || (math::fabs(br[1]) >= inv)) {
245 throw std::runtime_error(
246 "Incorrect bracket around root: Boundary reached inf!");
250 bool bracket_outside_tol{math::fabs(s) > max_path &&
251 math::fabs(br[0]) >= max_path &&
252 math::fabs(br[1]) >= max_path};
253 if (bracket_outside_tol) {
254 DETRAY_VERBOSE_HOST(
"Root outside maximum search area (s = "
255 << s <<
", a: " << br[0] <<
", b: " << br[1]
257 return std::make_pair(inv, inv);
261 if (math::fabs(f_a) < convergence_tolerance) {
262 DETRAY_DEBUG_HOST(
"Found: s = " << a <<
"mm, err = " << epsilon <<
"mm");
263 return std::make_pair(a, epsilon);
265 if (math::fabs(f_b) < convergence_tolerance) {
266 DETRAY_DEBUG_HOST(
"Found: s = " << b <<
"mm, err = " << epsilon <<
"mm");
267 return std::make_pair(b, epsilon);
272 bool is_lower_a{math::signbit(f_a)};
273 a = br[is_lower_a ? 0u : 1u];
274 b = br[is_lower_a ? 1u : 0u];
278 scalar_t s_prev{0.f};
279 std::size_t n_tries{0u};
280 auto [f_s, df_s] = evaluate_func(s);
281 if (math::fabs(f_s) < convergence_tolerance) {
283 "Found: s = " << s <<
"mm, err = " << convergence_tolerance <<
"mm");
284 return std::make_pair(s, epsilon);
286 if (math::signbit(f_s)) {
292 while (math::fabs(s - s_prev) > convergence_tolerance) {
294 bool bracket_escape{
true};
295 scalar_t s_newton{0.f};
296 if (math::fabs(df_s) != 0.f) {
297 s_newton = s - f_s / df_s;
298 bracket_escape = math::signbit((s_newton - a) * (b - s_newton));
307 bool slow_convergence{
true};
309 if (
const scalar_t ds_bisection{0.5f * (a + b) - s};
313 (2.f * math::fabs(f_s) > math::fabs(df_s * ds_bisection + f_s));
320 (bracket_escape || slow_convergence || math::fabs(df_s) == 0.f)) {
325 if (!is_bracketed && math::fabs(df_s) == 0.f) {
326 DETRAY_VERBOSE_HOST(
"Newton step encountered invalid derivative at s="
327 << s <<
" after " << n_tries
328 <<
" steps - skipping");
330 return std::make_pair(inv, inv);
337 std::tie(f_s, df_s) = evaluate_func(s);
338 if (is_bracketed && math::signbit(f_s)) {
345 if (math::fabs(s) > max_path && math::fabs(s_prev) > max_path &&
346 ((a < -max_path && b < -max_path) || (a > max_path && b > max_path))) {
347 DETRAY_VERBOSE_HOST(
"WARNING: Root finding left the search space at (s = "
348 << s <<
", a: " << a <<
", b: " << b <<
") after "
349 << n_tries <<
" steps - skipping");
351 return std::make_pair(inv, inv);
357 if (n_tries >= max_n_tries) {
360 std::stringstream err_str{};
361 err_str <<
"Helix intersector did not find root for s=" << s <<
" in ["
362 << a <<
", " << b <<
"]";
364 DETRAY_FATAL_HOST(err_str.str());
365 throw std::runtime_error(err_str.str());
367 DETRAY_VERBOSE_HOST(
"Helix intersector did not converge after "
369 <<
" steps unbracketed search - skipping");
371 return std::make_pair(inv, inv);
375 DETRAY_DEBUG_HOST(
"Found: s = " << s <<
"mm, err = " << math::fabs(s - s_prev)
377 return std::make_pair(s, math::fabs(s - s_prev));
393 intersection_t &is,
const trajectory_t &traj,
395 const mask_t &
mask,
const transform3_t &trf,
397 const scalar_t = 0.f) {
399 "Helix intersectors use only one mask tolerance value");
402 if (!detail::is_invalid_value(ip.path)) {
403 is.set_path(ip.path);
405 mask_t::to_local_frame3D(trf, traj.pos(ip.path), traj.dir(ip.path)));
407 const scalar_t cos_incidence_angle = vector::dot(
408 mask_t::get_local_frame().normal(trf, is.local()), traj.dir(ip.path));
412 if (tol >= detail::invalid_value<float>()) {
414 const scalar_t sin_inc2{
415 math::fabs(1.f - cos_incidence_angle * cos_incidence_angle)};
417 tol = math::fabs(ip.
path_err * math::sqrt(sin_inc2));
420 assert(!math::signbit(tol));
424 ? intersection::status::e_inside
425 : intersection::status::e_outside);
426 is.set_surface(sf_desc);
427 is.set_direction(!math::signbit(ip.path));
431 is.set_status(intersection::status::e_outside);
434 std::string intr_status{
"unknown"};
435 if (is.is_inside()) {
436 intr_status =
"inside";
437 }
else if (is.is_edge()) {
438 intr_status =
"edge";
440 intr_status =
"outside";
443 DETRAY_DEBUG_HOST(
"Intersection status: " << intr_status);
constexpr void resolve_mask(intersection_t &is, const trajectory_t &traj, const intersection_point< algebra_t, point_t, intersection::contains_pos > &ip, const surface_descr_t sf_desc, const mask_t &mask, const transform3_t &trf, const intersection::config &cfg={}, const scalar_t external_mask_tolerance=0.f)
Fill an intersection with the result of the intersection alg.
Definition intersector_base.hpp:189
std::pair< scalar_t, scalar_t > newton_raphson_safe(function_t &evaluate_func, scalar_t s, const scalar_t convergence_tolerance=1.f *unit< scalar_t >::um, const std::size_t max_n_tries=1000u, const scalar_t max_path=5.f *unit< scalar_t >::m)
Find a root using the Newton-Raphson and Bisection algorithms.
Definition root_finding.hpp:193
std::pair< scalar_t, scalar_t > newton_raphson(function_t &evaluate_func, scalar_t s, const scalar_t convergence_tolerance=1.f *unit< scalar_t >::um, const std::size_t max_n_tries=1000u, const scalar_t max_path=5.f *unit< scalar_t >::m)
Find a root using the Newton-Raphson algorithm.
Definition root_finding.hpp:121
bool expand_bracket(const scalar_t a, const scalar_t b, function_t &f, darray< scalar_t, 2 > &bracket, const scalar_t k=1.f)
Try to find a bracket around a root.
Definition root_finding.hpp:44