|
|
|||
File indexing completed on 2026-11-13 10:31:45
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 corecel/math/ArrayUtils.hh 0006 //! \brief Math functions using celeritas::Array 0007 //---------------------------------------------------------------------------// 0008 #pragma once 0009 0010 #include <cmath> 0011 0012 #include "corecel/Assert.hh" 0013 #include "corecel/Types.hh" 0014 #include "corecel/cont/Array.hh" 0015 0016 #include "Algorithms.hh" 0017 #include "ArraySoftUnit.hh" 0018 #include "SoftEqual.hh" 0019 0020 #include "detail/ArrayUtilsImpl.hh" 0021 0022 namespace celeritas 0023 { 0024 //---------------------------------------------------------------------------// 0025 // Perform y <- ax + y 0026 template<class T, std::size_t N> 0027 inline CELER_FUNCTION void axpy(T a, Array<T, N> const& x, Array<T, N>* y); 0028 0029 //---------------------------------------------------------------------------// 0030 // Calculate product of two vectors 0031 template<class T, std::size_t N> 0032 [[nodiscard]] inline CELER_FUNCTION T dot_product(Array<T, N> const& x, 0033 Array<T, N> const& y); 0034 0035 //---------------------------------------------------------------------------// 0036 // Calculate product of two vectors 0037 template<class T> 0038 [[nodiscard]] inline CELER_FUNCTION Array<T, 3> cross_product( 0039 Array<T, 3> const& x, Array<T, 3> const& y); 0040 0041 //---------------------------------------------------------------------------// 0042 // Calculate the Euclidean (2) norm of a vector 0043 template<class T, std::size_t N> 0044 [[nodiscard]] inline CELER_FUNCTION T norm(Array<T, N> const& vec); 0045 0046 //---------------------------------------------------------------------------// 0047 // Construct a vector with unit magnitude 0048 template<class T, std::size_t N> 0049 [[nodiscard]] inline CELER_FUNCTION Array<T, N> make_unit_vector( 0050 Array<T, N> const& v); 0051 0052 //---------------------------------------------------------------------------// 0053 // Return x - (x . y) * y for a unit vector y 0054 template<class T, std::size_t N> 0055 [[nodiscard]] inline CELER_FUNCTION Array<T, N> make_orthogonal( 0056 Array<T, N> const& x, Array<T, N> const& y); 0057 0058 //---------------------------------------------------------------------------// 0059 // Check whether two vectors are approximately orthogonal 0060 template<class T, std::size_t N> 0061 inline CELER_FUNCTION bool is_soft_orthogonal(Array<T, N> const& x, 0062 Array<T, N> const& y); 0063 0064 //---------------------------------------------------------------------------// 0065 // Check whether two vectors are approximately collinear 0066 template<class T, std::size_t N> 0067 inline CELER_FUNCTION bool is_soft_collinear(Array<T, N> const& x, 0068 Array<T, N> const& y); 0069 0070 //---------------------------------------------------------------------------// 0071 // Calculate the Euclidean (2) distance between two points 0072 template<class T, std::size_t N> 0073 [[nodiscard]] inline CELER_FUNCTION T distance(Array<T, N> const& x, 0074 Array<T, N> const& y); 0075 0076 //---------------------------------------------------------------------------// 0077 // Calculate a cartesian unit vector from spherical coordinates 0078 template<class T> 0079 [[nodiscard]] inline CELER_FUNCTION Array<T, 3> from_spherical(T costheta, 0080 T phi); 0081 0082 //---------------------------------------------------------------------------// 0083 // Rotate the direction 'dir' according to the reference rotation axis 'rot' 0084 template<class T> 0085 [[nodiscard]] inline CELER_FUNCTION Array<T, 3> rotate(Array<T, 3> const& dir, 0086 Array<T, 3> const& rot); 0087 0088 //---------------------------------------------------------------------------// 0089 // INLINE DEFINITIONS 0090 //---------------------------------------------------------------------------// 0091 /*! 0092 * Increment a vector by another vector multiplied by a scalar. 0093 * 0094 * \f[ 0095 * y \gets \alpha x + y 0096 * \f] 0097 * 0098 * Note that this uses \c celeritas::fma which supports types other than 0099 * floating point. 0100 */ 0101 template<class T, std::size_t N> 0102 CELER_FUNCTION void axpy(T a, Array<T, N> const& x, Array<T, N>* y) 0103 { 0104 CELER_EXPECT(y); 0105 for (size_type i = 0; i != N; ++i) 0106 { 0107 (*y)[i] = fma(a, x[i], (*y)[i]); 0108 } 0109 } 0110 0111 //---------------------------------------------------------------------------// 0112 /*! 0113 * Dot product of two vectors. 0114 * 0115 * Note that this uses \c celeritas::fma which supports types other than 0116 * floating point. 0117 */ 0118 template<class T, std::size_t N> 0119 CELER_FUNCTION T dot_product(Array<T, N> const& x, Array<T, N> const& y) 0120 { 0121 T result{}; 0122 for (size_type i = 0; i != N; ++i) 0123 { 0124 result = fma(x[i], y[i], result); 0125 } 0126 return result; 0127 } 0128 0129 //---------------------------------------------------------------------------// 0130 /*! 0131 * Cross product of two space vectors. 0132 */ 0133 template<class T> 0134 CELER_FUNCTION Array<T, 3> cross_product(Array<T, 3> const& x, 0135 Array<T, 3> const& y) 0136 { 0137 return {x[1] * y[2] - x[2] * y[1], 0138 x[2] * y[0] - x[0] * y[2], 0139 x[0] * y[1] - x[1] * y[0]}; 0140 } 0141 0142 //---------------------------------------------------------------------------// 0143 /*! 0144 * Calculate the Euclidean (2) norm of a vector. 0145 */ 0146 template<class T, std::size_t N> 0147 CELER_FUNCTION T norm(Array<T, N> const& v) 0148 { 0149 return std::sqrt(dot_product(v, v)); 0150 } 0151 0152 //---------------------------------------------------------------------------// 0153 /*! 0154 * Construct a unit vector. 0155 * 0156 * Unit vectors have an Euclidean norm magnitude of 1. 0157 */ 0158 template<class T, std::size_t N> 0159 CELER_FUNCTION Array<T, N> make_unit_vector(Array<T, N> const& v) 0160 { 0161 Array<T, N> result{v}; 0162 T const scale_factor = 1 / norm(result); 0163 for (auto& el : result) 0164 { 0165 el *= scale_factor; 0166 } 0167 return result; 0168 } 0169 0170 //---------------------------------------------------------------------------// 0171 /*! 0172 * Return the component of \em x that is orthogonal to the unit vector \em y. 0173 * 0174 * In this implementation, \em y must be normalized, and the result is not 0175 * normalized. 0176 * 0177 * \f[ 0178 \mathbf{x}' \gets \mathbf{x} - (\mathbf{x} \cdot \mathbf{y}) \mathbf{y} 0179 \, , \quad \|\mathbf{y}\| = 1 0180 \f] 0181 */ 0182 template<class T, std::size_t N> 0183 [[nodiscard]] inline CELER_FUNCTION Array<T, N> make_orthogonal( 0184 Array<T, N> const& x, Array<T, N> const& y) 0185 { 0186 CELER_EXPECT(is_soft_unit_vector(y)); 0187 Array<T, N> result{x}; 0188 axpy(-dot_product(x, y), y, &result); 0189 return result; 0190 } 0191 0192 //---------------------------------------------------------------------------// 0193 /*! 0194 * Check whether two unit vectors are approximately orthogonal. 0195 * 0196 * Note that the test for orthogonality should use relative tolerance, not 0197 * absolute. 0198 */ 0199 template<class T, std::size_t N> 0200 inline CELER_FUNCTION bool is_soft_orthogonal(Array<T, N> const& x, 0201 Array<T, N> const& y) 0202 { 0203 SoftZero const soft_zero{SoftEqual<T>{}.rel()}; 0204 return soft_zero(dot_product(x, y)); 0205 } 0206 0207 //---------------------------------------------------------------------------// 0208 /*! 0209 * Check whether two vectors are approximately collinear. 0210 * 0211 * \pre Vectors must be normalized, i.e., have unit magnitude. 0212 */ 0213 template<class T, std::size_t N> 0214 inline CELER_FUNCTION bool is_soft_collinear(Array<T, N> const& x, 0215 Array<T, N> const& y) 0216 { 0217 CELER_EXPECT(is_soft_unit_vector(x) && is_soft_unit_vector(y)); 0218 SoftEqual<T> const soft_eq; 0219 return soft_eq(dot_product(x, y), 1); 0220 } 0221 0222 //---------------------------------------------------------------------------// 0223 /*! 0224 * Calculate the Euclidean (2) distance between two points. 0225 */ 0226 template<class T, std::size_t N> 0227 CELER_FUNCTION T distance(Array<T, N> const& x, Array<T, N> const& y) 0228 { 0229 T dist_sq = 0; 0230 for (size_type i = 0; i != N; ++i) 0231 { 0232 dist_sq += ipow<2>(y[i] - x[i]); 0233 } 0234 return std::sqrt(dist_sq); 0235 } 0236 0237 //---------------------------------------------------------------------------// 0238 /*! 0239 * Calculate a Cartesian vector from spherical coordinates. 0240 * 0241 * Theta is the angle between the \em z axis and the outgoing vector, and \c 0242 * phi is the angle between the \em x axis and the projection of the vector 0243 * onto the \em x-y plane. 0244 */ 0245 template<class T> 0246 inline CELER_FUNCTION Array<T, 3> from_spherical(T costheta, T phi) 0247 { 0248 CELER_EXPECT(costheta >= -1 && costheta <= 1); 0249 0250 T const sintheta = std::sqrt(1 - costheta * costheta); 0251 return {sintheta * std::cos(phi), sintheta * std::sin(phi), costheta}; 0252 } 0253 0254 //---------------------------------------------------------------------------// 0255 /*! 0256 * Rotate a direction about the given scattering direction. 0257 * 0258 * The equivalent to calling the Shift transport code's \code 0259 void cartesian_vector_transform( 0260 double costheta, 0261 double phi, 0262 Vector_View vector); 0263 * \endcode 0264 * is the call 0265 * \code 0266 vector = rotate(from_spherical(costheta, phi), vector); 0267 * \endcode 0268 * 0269 * This code effectively decomposes the given rotation vector \c rot into two 0270 * sequential transform matrices, one with an angle \em theta about the \em y 0271 * axis and one about \em phi rotating around the \em z axis. These two angles 0272 * are the spherical coordinate transform of the given \c rot cartesian 0273 * direction vector. 0274 * 0275 * This function is often used for calculating exiting scattering angles. In 0276 * that case, \c dir is the exiting angle from the scattering calculation, and 0277 * \c rot is the original direction of the particle. The direction vectors are 0278 * defined as 0279 * \f[ 0280 \vec{\Omega} 0281 = \sin\theta\cos\phi\vec{i} 0282 + \sin\theta\sin\phi\vec{j} 0283 + \cos\theta\vec{k} \,. 0284 * \f] 0285 * 0286 * There is some extra code in here to deal with loss of precision when the 0287 * incident direction is along the \em z axis. As \c rot approaches \em z, the 0288 * azimuthal angle \f$ \phi \f$ must be calculated carefully from both the 0289 * \em x and \em y components of the vector, not independently. 0290 * If \c rot actually equals \em z 0291 * then the azimuthal angle is completely indeterminate so we arbitrarily 0292 * choose \f$ \phi = 0 \f$. 0293 */ 0294 template<class T> 0295 inline CELER_FUNCTION Array<T, 3> rotate(Array<T, 3> const& dir, 0296 Array<T, 3> const& rot) 0297 { 0298 CELER_EXPECT(is_soft_unit_vector(dir)); 0299 CELER_EXPECT(is_soft_unit_vector(rot)); 0300 0301 // Direction enumeration 0302 using TraitsT = detail::RealVecTraits<T>; 0303 0304 constexpr int X = 0, Y = 1, Z = 2; 0305 0306 // Transform direction vector into theta, phi so we can use it as a 0307 // rotation matrix 0308 T sintheta = std::sqrt(1 - ipow<2>(rot[Z])); 0309 T cosphi; 0310 T sinphi; 0311 0312 if (sintheta >= TraitsT::min_accurate_sintheta) 0313 { 0314 // Typical case: far enough from z axis to assume the X and Y 0315 // components have a hypotenuse of 1 within epsilon tolerance 0316 T const inv_sintheta = 1 / sintheta; 0317 cosphi = rot[X] * inv_sintheta; 0318 sinphi = rot[Y] * inv_sintheta; 0319 } 0320 else if (rot[X] != 0 || rot[Y] != 0) 0321 { 0322 // Avoid catastrophic roundoff error by normalizing x/y components 0323 cosphi = rot[X] / hypot(rot[X], rot[Y]); 0324 sinphi = std::sqrt(1 - ipow<2>(cosphi)); 0325 } 0326 else 0327 { 0328 // NaNs or coincident Z: force sin theta to zero and choose an 0329 // arbitrary azimuthal angle for the incident dir 0330 sintheta = 0; 0331 cosphi = 1; 0332 sinphi = 0; 0333 } 0334 0335 Array<T, 3> result 0336 = {(rot[Z] * dir[X] + sintheta * dir[Z]) * cosphi - sinphi * dir[Y], 0337 (rot[Z] * dir[X] + sintheta * dir[Z]) * sinphi + cosphi * dir[Y], 0338 -sintheta * dir[X] + rot[Z] * dir[Z]}; 0339 0340 // Always normalize to prevent roundoff error from propagating 0341 return make_unit_vector(result); 0342 } 0343 0344 //---------------------------------------------------------------------------// 0345 } // namespace celeritas
| [ Source navigation ] | [ Diff markup ] | [ Identifier search ] | [ general search ] |
|
This page was automatically generated by the 2.3.7 LXR engine. The LXR team |
|