Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-30 10:17:09

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/detail/OrientedBoundingZone.hh
0007 //---------------------------------------------------------------------------//
0008 #pragma once
0009 
0010 #include "corecel/math/Algorithms.hh"
0011 #include "corecel/math/NumericLimits.hh"
0012 #include "orange/OrangeData.hh"
0013 #include "orange/OrangeTypes.hh"
0014 #include "orange/transform/TransformVisitor.hh"
0015 
0016 namespace celeritas
0017 {
0018 namespace detail
0019 {
0020 //---------------------------------------------------------------------------//
0021 /*! Oriented bounding zone for safety distance calculations.
0022  *
0023  * Here, the oriented bounding zone (OBZ) is defined by inner and outer
0024  * bounding boxes transformed by the same transformation --- that of the volume
0025  * they correspond to. The OBZ bounding boxes are stored on the
0026  * OrientedBoundingZoneRecord as:
0027  *
0028  * 1. a vector of half-widths,
0029  *
0030  * 2. an additional *translation* object for each bounding box specifying how
0031  * the center of each bounding box is offset from the center of the OBZ
0032  * coordinate system.
0033  *
0034  * Consequently, points in the unit's coordinate system must be first
0035  * transformed by \c transform_id into the OBZ coordinate system (resulting in
0036  * "trans_pos"), then offset, i.e. translated, by \c inner_offset_id or
0037  * \c outer_offset_id into the inner or outer bbox coordinate system (resulting
0038  * in "offset_pos"). It is noted that these offset positions are always
0039  * automatically reflected into the first quadrant.
0040  *
0041  * The safety distance can be approximated as the minimum distance to the inner
0042  * or outer box. For points lying between the inner and outer boxes, the safety
0043  * distance is zero.
0044  */
0045 class OrientedBoundingZone
0046 {
0047   public:
0048     //!@{
0049     //! \name Type aliases
0050     template<class T>
0051     using StorageItems
0052         = Collection<T, Ownership::const_reference, MemSpace::native>;
0053 
0054     using FastReal3 = OrientedBoundingZoneRecord::Real3;
0055     using fast_real_type = FastReal3::value_type;
0056 
0057     struct StoragePointers
0058     {
0059         StorageItems<TransformRecord> const* transforms;
0060         StorageItems<real_type> const* reals;
0061 
0062         operator bool() const { return transforms && reals; }
0063     };
0064     //!@}
0065 
0066   public:
0067     // Construct from an OBZ record and corresponding storage
0068     inline CELER_FUNCTION
0069     OrientedBoundingZone(OrientedBoundingZoneRecord const& obz_record,
0070                          StoragePointers const& sp);
0071 
0072     // Calculate the safety distance for any position inside the outer box
0073     inline CELER_FUNCTION real_type calc_safety_inside(Real3 const& pos);
0074 
0075     // Calculate the safety distance for any position outside the inner box
0076     inline CELER_FUNCTION real_type calc_safety_outside(Real3 const& pos);
0077 
0078     // Determine the sense of position with respect to the bounding zone
0079     inline CELER_FUNCTION SignedSense calc_sense(Real3 const& pos);
0080 
0081   private:
0082     //!@{
0083     //! \name Type aliases
0084 
0085     //! Enum to distinguish between inner and outer bboxes
0086     enum class BBoxType
0087     {
0088         inner,  //!< inner bbox
0089         outer  //!< outer bbox
0090     };
0091 
0092     //! A position within the innner or outer bbox coordinate system
0093     struct OffsetPos
0094     {
0095         Real3 pos;  //!< the position
0096         BBoxType bbt;  //!< the bbox coordinate system
0097     };
0098     //!@}
0099 
0100     // >> DATA
0101     OrientedBoundingZoneRecord const& obz_record_;
0102     StoragePointers sp_;
0103 
0104     //// HELPER METHODS ////
0105 
0106     // Translate a position into the OBZ coordinate system
0107     inline CELER_FUNCTION Real3 translate(Real3 const& unit_pos);
0108 
0109     // Offset a pre-translated position into a bbox coordinate system
0110     inline CELER_FUNCTION OffsetPos apply_offset(Real3 const& trans_pos,
0111                                                  BBoxType bbt);
0112 
0113     // Determine if an offset position is inside its respective bbox
0114     inline CELER_FUNCTION bool is_inside(OffsetPos const& off_pos);
0115 
0116     // Get half-widths for a bbox
0117     inline CELER_FUNCTION FastReal3 get_hw(BBoxType bbt);
0118 
0119     // Reflect a position into quadrant one
0120     inline CELER_FUNCTION Real3 quadrant_one(Real3 const& pos);
0121 
0122     // Convert BBoxType enum value to int
0123     CELER_CONSTEXPR_FUNCTION int to_int(BBoxType bbt);
0124 };
0125 
0126 //---------------------------------------------------------------------------//
0127 /*!
0128  * Construct from an OBZ record and corresponding storage.
0129  */
0130 CELER_FUNCTION
0131 OrientedBoundingZone::OrientedBoundingZone(
0132     OrientedBoundingZoneRecord const& obz_record, StoragePointers const& sp)
0133     : obz_record_(obz_record), sp_(sp)
0134 {
0135     CELER_EXPECT(obz_record_);
0136     CELER_EXPECT(sp_);
0137 }
0138 
0139 //---------------------------------------------------------------------------//
0140 /*!
0141  * Calculate the safety distance for a position inside the outer box.
0142  *
0143  * There are two cases:
0144  *
0145  * Case 1: the point is between the inner and outer boxes, resulting in a
0146  * safety distance of zero.
0147  *
0148  * Case 2: the point is inside both the inner and outer boxes, in which case
0149  * the safety distance is the minimum distance from the given point to any
0150  * point on the outer box. This is calculated by finding the minimum of the
0151  * distances to each half width.
0152  */
0153 CELER_FUNCTION real_type
0154 OrientedBoundingZone::calc_safety_inside(Real3 const& pos)
0155 {
0156     CELER_EXPECT(this->calc_sense(pos) != SignedSense::outside);
0157 
0158     auto trans_pos = this->translate(pos);
0159 
0160     if (!this->is_inside(this->apply_offset(trans_pos, BBoxType::inner)))
0161     {
0162         // Case 1: between inner and outer boxes
0163         return 0;
0164     }
0165 
0166     // Case 2: outside outer box
0167     auto outer_offset_pos = this->apply_offset(trans_pos, BBoxType::outer);
0168     auto outer_hw = this->get_hw(BBoxType::outer);
0169 
0170     fast_real_type min_dist = numeric_limits<real_type>::infinity();
0171     for (auto ax : range(Axis::size_))
0172     {
0173         min_dist = celeritas::min(
0174             min_dist,
0175             outer_hw[int(ax)]
0176                 - static_cast<fast_real_type>(
0177                     outer_offset_pos.pos[celeritas::to_int(ax)]));
0178     }
0179 
0180     return static_cast<real_type>(min_dist);
0181 }
0182 
0183 //---------------------------------------------------------------------------//
0184 /*!
0185  * Calculate the safety distance for any position outside the inner box.
0186  *
0187  * There are two cases:
0188  *
0189  * Case 1: the point is between the inner and outer boxes, resulting in a
0190  * safety distance of zero.
0191  *
0192  * Case 2: the point is outside both the inner and outer boxes, in which case
0193  * the safety distance is the minimum distance from the given point to any
0194  * point on the inner box. This can be calculated as:
0195  *
0196  * \f[
0197  * d = \sqrt(\max(0, p_x - h_x)^2 + max(0, p_y - h_y)^2 + max(0, p_z - h_z)^2)
0198  * \f]
0199  *
0200  * for a point in quadrant one at (\em p_x, \em p_y, \em p_z) and a box with
0201  * half-widths (\em h_x, \em h_y, \em h_z).
0202  */
0203 CELER_FUNCTION real_type
0204 OrientedBoundingZone::calc_safety_outside(Real3 const& pos)
0205 {
0206     CELER_EXPECT(this->calc_sense(pos) != SignedSense::inside);
0207 
0208     auto trans_pos = this->translate(pos);
0209 
0210     if (this->is_inside(this->apply_offset(trans_pos, BBoxType::outer)))
0211     {
0212         // Case 1: between inner and outer boxes
0213         return 0;
0214     }
0215 
0216     // Case 2: outside outer box
0217     auto inner_offset_pos = this->apply_offset(trans_pos, BBoxType::inner);
0218     auto inner_hw = this->get_hw(BBoxType::inner);
0219 
0220     fast_real_type min_squared = 0;
0221     for (auto ax : range(Axis::size_))
0222     {
0223         auto temp
0224             = celeritas::max(fast_real_type{0},
0225                              static_cast<fast_real_type>(
0226                                  inner_offset_pos.pos[celeritas::to_int(ax)])
0227                                  - inner_hw[celeritas::to_int(ax)]);
0228         min_squared += ipow<2>(temp);
0229     }
0230 
0231     return sqrt(min_squared);
0232 }
0233 
0234 //---------------------------------------------------------------------------//
0235 /*!
0236  * Determine the sense of position with respect to the OBZ.
0237  *
0238  * If the position is between the inner and outer bboxes its sense is
0239  * SignedSense::on.
0240  */
0241 CELER_FUNCTION SignedSense OrientedBoundingZone::calc_sense(Real3 const& pos)
0242 {
0243     auto trans_pos = this->translate(pos);
0244 
0245     if (this->is_inside(this->apply_offset(trans_pos, BBoxType::inner)))
0246     {
0247         return SignedSense::inside;
0248     }
0249     else if (this->is_inside(this->apply_offset(trans_pos, BBoxType::outer)))
0250     {
0251         return SignedSense::on;
0252     }
0253     else
0254     {
0255         return SignedSense::outside;
0256     }
0257 }
0258 
0259 //---------------------------------------------------------------------------//
0260 // HELPER FUNCTIONS
0261 //---------------------------------------------------------------------------//
0262 /*!
0263  * Translate a position into the OBZ coordinate system.
0264  */
0265 CELER_FUNCTION Real3 OrientedBoundingZone::translate(Real3 const& pos)
0266 {
0267     TransformVisitor apply_transform(*sp_.transforms, *sp_.reals);
0268     auto transform_down = [&pos](auto&& t) { return t.transform_down(pos); };
0269 
0270     return apply_transform(transform_down, obz_record_.transform_id);
0271 }
0272 
0273 //---------------------------------------------------------------------------//
0274 /*!
0275  * Offset a pre-translated position into a bbox coordinate system.
0276  *
0277  * This function also reflects the point into quadrant one.
0278  */
0279 CELER_FUNCTION auto
0280 OrientedBoundingZone::apply_offset(Real3 const& trans_pos,
0281                                    BBoxType bbt) -> OffsetPos
0282 {
0283     TransformVisitor apply_transform(*sp_.transforms, *sp_.reals);
0284     auto transform_down
0285         = [&trans_pos](auto&& t) { return t.transform_down(trans_pos); };
0286 
0287     return {this->quadrant_one(apply_transform(
0288                 transform_down, obz_record_.offset_ids[this->to_int(bbt)])),
0289             bbt};
0290 }
0291 
0292 //---------------------------------------------------------------------------//
0293 /*!
0294  * Determine if an offset position is inside its respective bbox.
0295  */
0296 CELER_FUNCTION bool OrientedBoundingZone::is_inside(OffsetPos const& off_pos)
0297 {
0298     auto const& half_widths = this->get_hw(off_pos.bbt);
0299 
0300     for (auto ax : range(Axis::size_))
0301     {
0302         if (off_pos.pos[celeritas::to_int(ax)]
0303             > half_widths[celeritas::to_int(ax)])
0304         {
0305             return false;
0306         }
0307     }
0308 
0309     return true;
0310 }
0311 
0312 //---------------------------------------------------------------------------//
0313 /*!
0314  * Get half-widths for a bbox.
0315  */
0316 CELER_FUNCTION OrientedBoundingZone::FastReal3
0317 OrientedBoundingZone::get_hw(BBoxType bbt)
0318 {
0319     return obz_record_.half_widths[this->to_int(bbt)];
0320 }
0321 
0322 //---------------------------------------------------------------------------//
0323 /*!
0324  * Reflect a position into quadrant one.
0325  */
0326 CELER_FUNCTION Real3 OrientedBoundingZone::quadrant_one(Real3 const& pos)
0327 {
0328     Real3 temp;
0329     for (auto ax : range(Axis::size_))
0330     {
0331         temp[celeritas::to_int(ax)] = std::fabs(pos[celeritas::to_int(ax)]);
0332     }
0333     return temp;
0334 }
0335 
0336 //---------------------------------------------------------------------------//
0337 /*!
0338  * Convert BBoxType enum value to int.
0339  */
0340 CELER_CONSTEXPR_FUNCTION int
0341 OrientedBoundingZone::to_int(OrientedBoundingZone::BBoxType bbt)
0342 {
0343     return static_cast<int>(bbt);
0344 }
0345 
0346 //---------------------------------------------------------------------------//
0347 }  // namespace detail
0348 }  // namespace celeritas