Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-02-22 10:47:11

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