Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-11-01 10:19:51

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/Involute.hh
0006 //---------------------------------------------------------------------------//
0007 #pragma once
0008 
0009 #include <cassert>
0010 #include <cmath>
0011 
0012 #include "corecel/Constants.hh"
0013 #include "corecel/Types.hh"
0014 #include "corecel/cont/Array.hh"
0015 #include "corecel/cont/Span.hh"
0016 #include "corecel/math/Algorithms.hh"
0017 #include "corecel/math/ArrayUtils.hh"
0018 #include "orange/OrangeTypes.hh"
0019 
0020 #include "detail/InvolutePoint.hh"
0021 #include "detail/InvoluteSolver.hh"
0022 
0023 namespace celeritas
0024 {
0025 //---------------------------------------------------------------------------//
0026 /*!
0027  * Z-aligned circular involute.
0028  *
0029  * An involute is a curve created by unwinding a chord from a shape.
0030  * The involute implemented here is constructed from a circle and can be made
0031  * to be clockwise (negative) or anti-clockwise (positive).
0032  * This involute is the same type of that found in HFIR, and is necessary for
0033  * building accurate models.
0034  *
0035  * While the parameters define the curve itself have no restriction, except for
0036  * the radius of involute being positive, the involute needs to be bounded for
0037  * there to be finite solutions to the roots of the intersection points.
0038  * Additionally this bound cannot exceed an interval of size of 2 to be able to
0039  * determine if whether a particle is in, out, or on the involute surface.
0040  * Lastly the involute geometry is fixed to be z-aligned.
0041  *
0042  * \f[
0043  *   x = r_b (\cos(t+a) + t \sin(t+a)) + x_0
0044  * \f]
0045  * \f[
0046  *   y = r_b (\sin(t+a) - t \cos(t+a)) + y_0
0047  * \f]
0048  *
0049  * where \em t is the normal angle of the tangent to the circle of involute
0050  * with radius \em r_b from a starting angle of \em a (\f$r/h\f$ for a finite
0051  * cone.
0052  */
0053 class Involute
0054 {
0055   public:
0056     //@{
0057     //! \name Type aliases
0058     using Intersections = Array<real_type, 3>;
0059     using StorageSpan = Span<real_type const, 6>;
0060     using Real2 = Array<real_type, 2>;
0061     //@}
0062 
0063   public:
0064     //// CLASS ATTRIBUTES ////
0065 
0066     // Surface type identifier
0067     static CELER_CONSTEXPR_FUNCTION SurfaceType surface_type()
0068 
0069     {
0070         return SurfaceType::inv;
0071     }
0072 
0073     //! Safety cannot be trivially calculated
0074     static CELER_CONSTEXPR_FUNCTION bool simple_safety() { return false; }
0075 
0076   public:
0077     //// CONSTRUCTORS ////
0078 
0079     explicit Involute(Real2 const& origin,
0080                       real_type radius,
0081                       real_type displacement,
0082                       Chirality sign,
0083                       real_type tmin,
0084                       real_type tmax);
0085 
0086     // Construct from raw data
0087     template<class R>
0088     explicit inline CELER_FUNCTION Involute(Span<R, StorageSpan::extent>);
0089 
0090     //// ACCESSORS ////
0091 
0092     //! X-Y center of the circular base of the involute
0093     CELER_FUNCTION Real2 const& origin() const { return origin_; }
0094 
0095     //! Involute circle's radius
0096     CELER_FUNCTION real_type r_b() const { return std::fabs(r_b_); }
0097 
0098     //! Displacement angle
0099     CELER_FUNCTION real_type displacement_angle() const { return a_; }
0100 
0101     // Orientation of the involute curve
0102     inline CELER_FUNCTION Chirality sign() const;
0103 
0104     //! Get bounds of the involute
0105     CELER_FUNCTION real_type tmin() const { return tmin_; }
0106     CELER_FUNCTION real_type tmax() const { return tmax_; }
0107 
0108     //! Get a view to the data for type-deleted storage
0109     CELER_FUNCTION StorageSpan data() const { return {&origin_[0], 6}; }
0110 
0111     //// CALCULATION ////
0112 
0113     // Determine the sense of the position relative to this surface
0114     inline CELER_FUNCTION SignedSense calc_sense(Real3 const& pos) const;
0115 
0116     // Calculate all possible straight-line intersections with this surface
0117     inline CELER_FUNCTION Intersections calc_intersections(
0118         Real3 const& pos, Real3 const& dir, SurfaceState on_surface) const;
0119 
0120     // Calculate outward normal at a position
0121     inline CELER_FUNCTION Real3 calc_normal(Real3 const& pos) const;
0122 
0123   private:
0124     // Location of the center of circle of involute
0125     Real2 origin_;
0126 
0127     // Involute parameters
0128     real_type r_b_;  // Radius, negative if "clockwise" (flipped)
0129     real_type a_;
0130 
0131     // Bounds
0132     real_type tmin_;
0133     real_type tmax_;
0134 };
0135 
0136 //---------------------------------------------------------------------------//
0137 // INLINE DEFINITIONS
0138 //---------------------------------------------------------------------------//
0139 /*!
0140  * Construct from raw data.
0141  */
0142 template<class R>
0143 CELER_FUNCTION Involute::Involute(Span<R, StorageSpan::extent> data)
0144     : origin_{data[0], data[1]}
0145     , r_b_{data[2]}
0146     , a_{data[3]}
0147     , tmin_{data[4]}
0148     , tmax_{data[5]}
0149 {
0150 }
0151 
0152 //---------------------------------------------------------------------------//
0153 /*!
0154  * Orientation of the involute curve.
0155  */
0156 CELER_FUNCTION auto Involute::sign() const -> Chirality
0157 {
0158     return r_b_ > 0 ? Chirality::left : Chirality::right;
0159 }
0160 
0161 //---------------------------------------------------------------------------//
0162 /*!
0163  * Determine the sense of the position relative to this surface.
0164  *
0165  * This is performed by first checking if the particle is outside the bounding
0166  * circles given by tmin and tmax.
0167  *
0168  * Then position is compared to a point on the involute at the same radius from
0169  * the origin.
0170  *
0171  * Next the point is determined to be inside if an involute that passes it,
0172  * has an \em t that is within the bounds and an \em a greater than the
0173  * involute being tested for. To do this, the tangent to the involute must be
0174  * determined by calculating the intercepts of the circle of involute and
0175  * circle centered at the midpoint of the origin and the point being tested for
0176  * that extends to the origin. After which the angle of tangent point can be
0177  * determined and used to calculate \em a via: \f[
0178  * a = angle - t_point
0179  * \f]
0180  *
0181  * To calculate the intercept of two circles it is assumed that the two circles
0182  * are \em x_prime axis. The coordinate along \em x_prime originating from the
0183  * center of one of the circles is given by: \f[ x_prime = (d^2 - r^2 +
0184  * R^2)/(2d) \f] Where \em d is the distance between the center of the two
0185  * circles, \em r is the radius of the displaced circle, and \em R is the
0186  * radius of the circle at the origin. Then the point \em y_prime can be
0187  * obatined from: \f[ y_prime = \pm sqrt(R^2 - x_prime^2) \f] Then to convert (
0188  * \em x_prime , \em y_prime ) to ( \em x, \em y ) the following rotation is
0189  * applied:\f[ x = x_prime*cos(theta) - y_prime*sin(theta) y =
0190  * y_prime*cos(theta) + x_prime*sin(theta) \f] Where \em theta is the angle
0191  * between the \em x_prime axis and the \em x axis.
0192  */
0193 CELER_FUNCTION SignedSense Involute::calc_sense(Real3 const& pos) const
0194 {
0195     using constants::pi;
0196 
0197     Real2 xy;
0198     xy[0] = pos[0] - origin_[0];
0199     xy[1] = pos[1] - origin_[1];
0200 
0201     if (this->sign() == Chirality::right)
0202     {
0203         xy[0] = negate(xy[0]);
0204     }
0205 
0206     // Calculate distance to origin and obtain t value for distance.
0207     real_type const t_point_sq = dot_product(xy, xy) / ipow<2>(r_b_) - 1;
0208 
0209     // Check if point is in defined bounds.
0210     if (t_point_sq < ipow<2>(tmin_))
0211     {
0212         return SignedSense::outside;
0213     }
0214     if (t_point_sq > ipow<2>(tmax_))
0215     {
0216         return SignedSense::outside;
0217     }
0218 
0219     // Check if Point is on involute.
0220     detail::InvolutePoint calc_point{this->r_b(), a_};
0221     Real2 point = calc_point(clamp_to_nonneg(t_point_sq));
0222 
0223     if (xy == point)
0224     {
0225         return SignedSense::on;
0226     }
0227 
0228     // Check if point is in interval
0229 
0230     // Calculate tangent point
0231     // TODO: check that compiler avoids recomputing trig functions
0232     real_type x_prime = ipow<2>(r_b_) / norm(xy);
0233     real_type y_prime = std::sqrt(ipow<2>(r_b_) - ipow<2>(x_prime));
0234 
0235     // TODO: check that compiler avoids recomputing trig functions
0236     point[0] = (x_prime * xy[0] - y_prime * xy[1]) / norm(xy);
0237     point[1] = (y_prime * xy[0] + x_prime * xy[1]) / norm(xy);
0238 
0239     // Calculate angle of tangent
0240     real_type theta = std::acos(point[0] / norm(point));
0241     if (point[1] < 0)
0242     {
0243         theta = 2 * pi - theta;
0244     }
0245     // Count number of positive rotations around involute
0246     using namespace celeritas::literals;
0247     theta += max<real_type>(0_r, std::floor((tmax_ + a_ - theta) / (2 * pi)))
0248              * 2 * pi;
0249 
0250     // Calculate the displacement angle of the point
0251     real_type a1 = theta - std::sqrt(clamp_to_nonneg(t_point_sq));
0252 
0253     // Check if point is inside bounds
0254     if (theta < tmax_ + a_ && a1 > a_)
0255     {
0256         return SignedSense::inside;
0257     }
0258 
0259     return SignedSense::outside;
0260 }
0261 
0262 //---------------------------------------------------------------------------//
0263 /*!
0264  * Calculate all possible straight-line intersections with this surface.
0265  */
0266 CELER_FUNCTION auto Involute::calc_intersections(
0267     Real3 const& pos, Real3 const& dir, SurfaceState on_surface) const
0268     -> Intersections
0269 {
0270     // Expand translated positions into 'xyz' coordinate system
0271     Real3 rel_pos{pos};
0272     rel_pos[0] -= origin_[0];
0273     rel_pos[1] -= origin_[1];
0274 
0275     detail::InvoluteSolver solve(this->r_b(), a_, this->sign(), tmin_, tmax_);
0276 
0277     return solve(rel_pos, dir, on_surface);
0278 }
0279 
0280 //---------------------------------------------------------------------------//
0281 /*!
0282  * Calculate outward normal at a position on the surface.
0283  *
0284  * This is done by taking the derivative of the involute equation : \f[
0285  * n = {\sin(t+a) - \cos(t+a), 0}
0286  * \f]
0287  */
0288 CELER_FORCEINLINE_FUNCTION Real3 Involute::calc_normal(Real3 const& pos) const
0289 {
0290     Real2 xy;
0291     xy[0] = pos[0] - origin_[0];
0292     xy[1] = pos[1] - origin_[1];
0293 
0294     // Calculate normal
0295     real_type const angle
0296         = std::sqrt(clamp_to_nonneg(dot_product(xy, xy) / ipow<2>(r_b_) - 1))
0297           + a_;
0298     Real3 normal_ = {std::sin(angle), -std::cos(angle), 0};
0299 
0300     if (this->sign() == Chirality::right)
0301     {
0302         normal_[0] = negate(normal_[0]);
0303     }
0304 
0305     return normal_;
0306 }
0307 
0308 //---------------------------------------------------------------------------//
0309 }  // namespace celeritas