Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-03-13 09:20:01

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