Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-09-17 09:07:49

0001 //------------------------------- -*- C++ -*- -------------------------------//
0002 // Copyright Celeritas contributors: see top-level COPYRIGHT file for details
0003 // SPDX-License-Identifier: (Apache-2.0 OR MIT)
0004 //---------------------------------------------------------------------------//
0005 //! \file orange/surf/detail/InvoluteSolver.hh
0006 //---------------------------------------------------------------------------//
0007 #pragma once
0008 
0009 #include <cmath>
0010 
0011 #include "corecel/Constants.hh"
0012 #include "corecel/Types.hh"
0013 #include "corecel/cont/Array.hh"
0014 #include "corecel/math/Algorithms.hh"
0015 #include "corecel/math/ArrayOperators.hh"
0016 #include "corecel/math/BisectionRootFinder.hh"
0017 #include "corecel/math/IllinoisRootFinder.hh"
0018 #include "corecel/math/RegulaFalsiRootFinder.hh"
0019 #include "orange/OrangeTypes.hh"
0020 
0021 #include "InvolutePoint.hh"
0022 
0023 namespace celeritas
0024 {
0025 namespace detail
0026 {
0027 //---------------------------------------------------------------------------//
0028 /*!
0029  * Find positive, real, nonzero roots for involute intersection function.
0030  *
0031  * The involute intersection equation \f[
0032    r_b * [v{cos(t+a)+tsin(t+a)} + u{sin(t+a)-tcos(t+a)}] + xv - yu = 0
0033  * \f]
0034  * has n solutions mathematically, but we only want solutions where t results
0035  * in a real, positive, and in the defined bounds.  Furthermore the equation is
0036  * subject to catastrophic roundoff due to floating point precision (see
0037  * \c Tolerance::sqrt_quadratic and the derivation in \c CylAligned ).
0038  *
0039  * \return An Intersections array where each item is a positive valid
0040  * intersection or the sentinel result \c no_intersection() .
0041  */
0042 class InvoluteSolver
0043 {
0044   public:
0045     //!@{
0046     //! \name Type aliases
0047     using Intersections = Array<real_type, 3>;
0048     using SurfaceState = celeritas::SurfaceState;
0049     using Real2 = Array<real_type, 2>;
0050     //@}
0051 
0052     static inline CELER_FUNCTION real_type line_angle_param(real_type u,
0053                                                             real_type v);
0054 
0055   public:
0056     // Construct Involute from parameters
0057     inline CELER_FUNCTION InvoluteSolver(real_type r_b,
0058                                          real_type a,
0059                                          Chirality sign,
0060                                          real_type tmin,
0061                                          real_type tmax);
0062 
0063     // Solve fully general case
0064     inline CELER_FUNCTION Intersections operator()(
0065         Real3 const& pos, Real3 const& dir, SurfaceState on_surface) const;
0066 
0067     //// CALCULATION ////
0068 
0069     inline CELER_FUNCTION real_type calc_dist(
0070         real_type x, real_type y, real_type u, real_type v, real_type t) const;
0071 
0072     static CELER_CONSTEXPR_FUNCTION real_type tol()
0073     {
0074         if constexpr (std::is_same_v<real_type, double>)
0075             return 1e-8;
0076         else if constexpr (std::is_same_v<real_type, float>)
0077             return 1e-5;
0078     }
0079 
0080     //// ACCESSORS ////
0081 
0082     //! Get involute parameters
0083     CELER_FUNCTION real_type r_b() const { return r_b_; }
0084     CELER_FUNCTION real_type a() const { return a_; }
0085     CELER_FUNCTION Chirality sign() const { return sign_; }
0086 
0087     //! Get bounds of the involute
0088     CELER_FUNCTION real_type tmin() const { return tmin_; }
0089     CELER_FUNCTION real_type tmax() const { return tmax_; }
0090 
0091   private:
0092     //// DATA ////
0093     // Involute parameters
0094     real_type r_b_;
0095     real_type a_;
0096     Chirality sign_;
0097 
0098     // Bounds
0099     real_type tmin_;
0100     real_type tmax_;
0101 };
0102 
0103 //---------------------------------------------------------------------------//
0104 // INLINE DEFINITIONS
0105 //---------------------------------------------------------------------------//
0106 /*!
0107  * Construct from involute parameters.
0108  */
0109 CELER_FUNCTION InvoluteSolver::InvoluteSolver(
0110     real_type r_b, real_type a, Chirality sign, real_type tmin, real_type tmax)
0111     : r_b_(r_b), a_(a), sign_(sign), tmin_(tmin), tmax_(tmax)
0112 {
0113     CELER_EXPECT(r_b > 0);
0114     CELER_EXPECT(a >= -constants::pi && a_ <= 2 * constants::pi);
0115     CELER_EXPECT(tmax > 0);
0116     CELER_EXPECT(tmin >= 0);
0117     CELER_EXPECT(tmax < 2 * constants::pi + tmin);
0118 }
0119 
0120 //---------------------------------------------------------------------------//
0121 /*!
0122  * Find all roots for involute surfaces that are within the bounds and result
0123  * in positive distances.
0124  *
0125  * Performed by doing a Regula Falsi Iteration on the
0126  * root function, \f[
0127  * f(t) = r_b * [v{cos(a+t) + tsin(a+t)} + u{sin(a+t) - tcos(a+t)}] + xv - yu
0128  * \f]
0129  * where the Regula Falsi Iteration is given by: \f[
0130  * t_c = [t_a*f(t_b) - t_b*f(t_a)] / [f(t_b) - f(t_a)]
0131  * \f]
0132  * where \em t_c replaces the bound with the same sign (e.g. \em t_a and \em
0133  * t_b
0134  * ). The initial bounds can be determined by the set of: \f[
0135  * {0, beta - a, beta - a - pi, beta - a + pi, beta - a - 2pi, beta - a + 2pi
0136  * ...} /f] Where \em beta is: \f[ beta = arctan(-v/u) \f]
0137  */
0138 CELER_FUNCTION auto
0139 InvoluteSolver::operator()(Real3 const& pos,
0140                            Real3 const& dir,
0141                            SurfaceState on_surface) const -> Intersections
0142 {
0143     using constants::pi;
0144     // Flatten pos and dir in xyz and uv respectively
0145     real_type x = pos[0];
0146     real_type const y = pos[1];
0147 
0148     real_type u = dir[0];
0149     real_type v = dir[1];
0150 
0151     // Mirror systemm for counterclockwise involutes
0152     if (sign_ == Chirality::right)
0153     {
0154         x = -x;
0155         u = -u;
0156     }
0157 
0158     // Results initalization and root counter
0159     Intersections result;
0160     int j = 0;
0161     // Initial result vector.
0162     result = {no_intersection(), no_intersection(), no_intersection()};
0163 
0164     // Return result if particle is travelling along z-axis.
0165     if (u == 0 && v == 0)
0166     {
0167         return result;
0168     }
0169 
0170     // Conversion constant for 2-D distance to 3-D distance
0171 
0172     real_type convert = 1 / hypot(v, u);
0173     u *= convert;
0174     v *= convert;
0175 
0176     // Line angle parameter
0177 
0178     real_type beta = line_angle_param(u, v);
0179 
0180     // Setting first interval bounds, needs to be done to ensure roots are
0181     // found
0182     real_type t_lower = 0;
0183     real_type t_upper = beta - a_;
0184 
0185     // Round t_upper to the first positive multiple of pi
0186     t_upper += max<real_type>(real_type{0}, -std::floor(t_upper / pi)) * pi;
0187 
0188     // Slow down factor to increment bounds when a root cannot be found
0189     int i = 1;
0190 
0191     // Lambda used for calculating the roots using Regula Falsi Iteration
0192     auto calc_t_intersect = [&](real_type t) {
0193         real_type alpha = u * std::sin(t + a_) - v * std::cos(t + a_);
0194         real_type beta = t * (u * std::cos(t + a_) + v * std::sin(t + a_));
0195         return r_b_ * (alpha - beta) + x * v - y * u;
0196     };
0197 
0198     /*
0199      * Define tolerance.
0200      * tol_point gives the tolerance level for a point when on the surface,
0201      * to account for the floating point error when performing square roots and
0202      * Regula Falsi iterations.
0203      */
0204     real_type tol_point
0205         = (on_surface == SurfaceState::on ? r_b_ * tol() * 100 : 0);
0206 
0207     // Iteration method for finding roots with convergence tolerance of
0208     // r_b_*tol
0209     IllinoisRootFinder find_root_between{calc_t_intersect, r_b_ * tol()};
0210 
0211     // Iterate on roots
0212     while (t_lower < tmax_)
0213     {
0214         // Find value in root function
0215         real_type ft_lower = calc_t_intersect(t_lower);
0216         real_type ft_upper = calc_t_intersect(t_upper);
0217 
0218         // Only iterate when roots have different signs
0219         if (signum<real_type>(ft_lower) != signum<real_type>(ft_upper))
0220         {
0221             // Regula Falsi Iteration
0222             real_type t_gamma = find_root_between(t_lower, t_upper);
0223 
0224             // Convert root to distance and store if positive and in interval
0225             real_type dist = calc_dist(x, y, u, v, t_gamma);
0226             if (dist > tol_point)
0227             {
0228                 result[j] = convert * dist;
0229                 j++;
0230             }
0231 
0232             // Obtain next bounds
0233             t_lower = t_upper;
0234             t_upper += pi;
0235         }
0236         else
0237         {
0238             // Incremet interval slowly until root is in interval
0239             // i used to slow down approach to next root
0240             t_lower = t_upper;
0241             t_upper += pi / i;
0242             i++;
0243         }
0244     }
0245     return result;
0246 }
0247 
0248 //---------------------------------------------------------------------------//
0249 /*!
0250  * Calculate the line-angle parameter \em beta used to find bounds of roots.
0251  *
0252  * \f[ \beta = \arctan(-v/u) \f]
0253  */
0254 CELER_FUNCTION real_type InvoluteSolver::line_angle_param(real_type u,
0255                                                           real_type v)
0256 {
0257     using constants::pi;
0258 
0259     if (u != 0)
0260     {
0261         // Standard method
0262         return std::atan(-v / u);
0263     }
0264     else if (-v < 0)
0265     {
0266         // Edge case
0267         return pi * -0.5;
0268     }
0269     else
0270     {
0271         return pi * 0.5;
0272     }
0273 }
0274 
0275 //---------------------------------------------------------------------------//
0276 /*!
0277  * Convert root to distance by calculating the point on the involute given by
0278  * the root and then taking the distance to that point from the particle.
0279  */
0280 CELER_FUNCTION real_type InvoluteSolver::calc_dist(
0281     real_type x, real_type y, real_type u, real_type v, real_type t) const
0282 {
0283     detail::InvolutePoint calc_point{r_b_, a_};
0284     Real2 point = calc_point(clamp_to_nonneg(t));
0285     real_type dist = 0;
0286 
0287     // Check if point is interval
0288     if (t >= tmin_ && t <= tmax_)
0289     {
0290         // Obtain direction to point on Involute
0291         real_type u_point = point[0] - x;
0292         real_type v_point = point[1] - y;
0293 
0294         // Dot with direction of particle
0295         real_type dot = u * u_point + v * v_point;
0296         real_type dot_sign = signum<real_type>(dot);
0297         // Obtain distance to point
0298         dist = std::sqrt(ipow<2>(u_point) + ipow<2>(v_point)) * dot_sign;
0299     }
0300     return dist;
0301 }
0302 
0303 //---------------------------------------------------------------------------//
0304 }  // namespace detail
0305 }  // namespace celeritas