Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 10:05:58

0001 //----------------------------------*-C++-*----------------------------------//
0002 // Copyright 2021-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/OrangeData.hh
0007 //---------------------------------------------------------------------------//
0008 #pragma once
0009 
0010 #include "corecel/Assert.hh"
0011 #include "corecel/OpaqueId.hh"
0012 #include "corecel/Types.hh"
0013 #include "corecel/cont/Range.hh"
0014 #include "corecel/data/Collection.hh"
0015 #include "corecel/data/CollectionBuilder.hh"
0016 #include "corecel/sys/ThreadId.hh"
0017 #include "geocel/BoundingBox.hh"
0018 
0019 #include "OrangeTypes.hh"
0020 #include "univ/detail/Types.hh"
0021 
0022 #include "detail/BIHData.hh"
0023 
0024 namespace celeritas
0025 {
0026 //---------------------------------------------------------------------------//
0027 // PARAMS
0028 //---------------------------------------------------------------------------//
0029 
0030 //! Local ID of exterior volume for unit-type universes
0031 static inline constexpr LocalVolumeId orange_exterior_volume{0};
0032 
0033 //! ID of the top-level (global/world, level=0) universe (scene)
0034 static inline constexpr UniverseId orange_global_universe{0};
0035 
0036 //---------------------------------------------------------------------------//
0037 /*!
0038  * Scalar values particular to an ORANGE geometry instance.
0039  */
0040 struct OrangeParamsScalars
0041 {
0042     // Maximum universe depth, i.e., depth of the universe tree DAG, equivalent
0043     // to the VecGeom implementation. Has a value of 1 for a non-nested
0044     // geometry.
0045     size_type max_depth{};
0046     size_type max_faces{};
0047     size_type max_intersections{};
0048     size_type max_logic_depth{};
0049 
0050     // Soft comparison and dynamic "bumping" values
0051     Tolerance<> tol;
0052 
0053     //! True if assigned
0054     explicit CELER_FUNCTION operator bool() const
0055     {
0056         return max_depth > 0 && max_faces > 0 && max_intersections > 0 && tol;
0057     }
0058 };
0059 
0060 //---------------------------------------------------------------------------//
0061 /*!
0062  * Data for a single volume definition.
0063  *
0064  * Surface IDs are local to the unit.
0065  *
0066  * \sa VolumeView
0067  */
0068 struct VolumeRecord
0069 {
0070     ItemRange<LocalSurfaceId> faces;
0071     ItemRange<logic_int> logic;
0072 
0073     logic_int max_intersections{0};
0074     logic_int flags{0};
0075     DaughterId daughter_id;
0076     OrientedBoundingZoneId obz_id;
0077 
0078     //! \todo For KENO geometry we will need zorder
0079 
0080     //! Flag values (bit field)
0081     enum Flags : logic_int
0082     {
0083         internal_surfaces = 0x1,  //!< "Complex" distance-to-boundary
0084         implicit_vol = 0x2,  //!< Background/exterior volume
0085         simple_safety = 0x4,  //!< Fast safety calculation
0086         embedded_universe = 0x8  //!< Volume contains embeddded universe
0087     };
0088 };
0089 
0090 //---------------------------------------------------------------------------//
0091 /*!
0092  * Data for surfaces within a single unit.
0093  *
0094  * Surfaces each have a compile-time number of real data needed to define them.
0095  * (These usually are the nonzero coefficients of the quadric equation.) The
0096  * two fields in this record point to the collapsed surface types and
0097  * linearized data for all surfaces in a unit.
0098  *
0099  * The "types" and "data offsets" are both indexed into using the local surface
0100  * ID. The result of accessing "data offset" is an index into the \c real_ids
0101  * array, which then points us to the start address in \c reals. This marks the
0102  * beginning of the data used by the surface. Since the surface type tells us
0103  * the number of real values needed for that surface, we implicitly get a Span
0104  * of real values with a single indirection.
0105  *
0106  * \todo: change "types" and "data offsets" to be `ItemMap` taking local
0107  * surface
0108  */
0109 struct SurfacesRecord
0110 {
0111     using RealId = OpaqueId<real_type>;
0112 
0113     ItemRange<SurfaceType> types;
0114     ItemRange<RealId> data_offsets;
0115 
0116     //! Number of surfaces stored
0117     CELER_FUNCTION size_type size() const { return types.size(); }
0118 
0119     //! True if defined consistently
0120     explicit CELER_FUNCTION operator bool() const
0121     {
0122         return data_offsets.size() == types.size();
0123     }
0124 };
0125 
0126 //---------------------------------------------------------------------------//
0127 /*!
0128  * Data for surface-to-volume connectivity.
0129  *
0130  * This struct is associated with a specific surface; the \c neighbors range is
0131  * a list of local volume IDs for that surface.
0132  */
0133 struct ConnectivityRecord
0134 {
0135     ItemRange<LocalVolumeId> neighbors;
0136 };
0137 
0138 //---------------------------------------------------------------------------//
0139 /*!
0140  * Data for a single OrientedBoundingZone.
0141  */
0142 struct OrientedBoundingZoneRecord
0143 {
0144     using Real3 = Array<fast_real_type, 3>;
0145 
0146     //! Half-widths of the inner and outer boxes
0147     Array<Real3, 2> half_widths;
0148 
0149     //! Offset from to center of inner and outer boxes to center of OBZ
0150     //! coordinate system
0151     Array<TransformId, 2> offset_ids;
0152 
0153     // Transformation from the OBZ coordinate system to the unit coordinate
0154     // system
0155     TransformId transform_id;
0156 
0157     //! True if assigned
0158     explicit CELER_FUNCTION operator bool() const
0159     {
0160         return offset_ids[0] && offset_ids[1] && transform_id;
0161     }
0162 };
0163 
0164 //---------------------------------------------------------------------------//
0165 /*!
0166  * Class for storing offset data for RaggedRightIndexer.
0167  */
0168 template<size_type N>
0169 struct RaggedRightIndexerData
0170 {
0171     using Sizes = Array<size_type, N>;
0172     using Offsets = Array<size_type, N + 1>;
0173 
0174     Offsets offsets;
0175 
0176     //! Construct with the an array denoting the size of each dimension.
0177     static RaggedRightIndexerData from_sizes(Sizes sizes)
0178     {
0179         CELER_EXPECT(sizes.size() > 0);
0180 
0181         Offsets offs;
0182         offs[0] = 0;
0183         for (auto i : range(N))
0184         {
0185             CELER_EXPECT(sizes[i] > 0);
0186             offs[i + 1] = sizes[i] + offs[i];
0187         }
0188         return RaggedRightIndexerData{offs};
0189     }
0190 };
0191 
0192 //---------------------------------------------------------------------------//
0193 /*!
0194  * Type-deleted transform.
0195  */
0196 struct TransformRecord
0197 {
0198     using RealId = OpaqueId<real_type>;
0199     TransformType type{TransformType::size_};
0200     RealId data_offset;
0201 
0202     //! True if values are set
0203     explicit CELER_FUNCTION operator bool() const
0204     {
0205         return type != TransformType::size_ && data_offset;
0206     }
0207 };
0208 
0209 //---------------------------------------------------------------------------//
0210 /*!
0211  * Scalar data for a single "unit" of volumes defined by surfaces.
0212  */
0213 struct SimpleUnitRecord
0214 {
0215     using VolumeRecordId = OpaqueId<VolumeRecord>;
0216 
0217     // Surface data
0218     SurfacesRecord surfaces;
0219     ItemRange<ConnectivityRecord> connectivity;  // Index by LocalSurfaceId
0220 
0221     // Volume data [index by LocalVolumeId]
0222     ItemMap<LocalVolumeId, VolumeRecordId> volumes;
0223 
0224     // Bounding Interval Hierachy tree parameters
0225     detail::BIHTree bih_tree;
0226 
0227     LocalVolumeId background{};  //!< Default if not in any other volume
0228     bool simple_safety{};
0229 
0230     //! True if defined
0231     explicit CELER_FUNCTION operator bool() const
0232     {
0233         return surfaces && connectivity.size() == surfaces.types.size()
0234                && !volumes.empty();
0235     }
0236 };
0237 
0238 //---------------------------------------------------------------------------//
0239 /*!
0240  * Data for a single rectilinear array universe.
0241  */
0242 struct RectArrayRecord
0243 {
0244     using Dims = Array<size_type, 3>;
0245     using Grid = Array<ItemRange<real_type>, 3>;
0246     using SurfaceIndexerData = RaggedRightIndexerData<3>;
0247 
0248     // Daughter data [index by LocalVolumeId]
0249     ItemMap<LocalVolumeId, DaughterId> daughters;
0250 
0251     // Array data
0252     Dims dims;
0253     Grid grid;
0254     SurfaceIndexerData surface_indexer_data;
0255 
0256     //! Cursory check for validity
0257     explicit CELER_FUNCTION operator bool() const
0258     {
0259         return !daughters.empty() && !grid[to_int(Axis::x)].empty()
0260                && !grid[to_int(Axis::y)].empty()
0261                && !grid[to_int(Axis::z)].empty();
0262     }
0263 };
0264 
0265 //---------------------------------------------------------------------------//
0266 /*!
0267  * Surface and volume offsets to convert between local and global indices.
0268  *
0269  * Each collection should be of length num_universes + 1. The first entry is
0270  * zero and the last item should be the total number of surfaces or volumes.
0271  */
0272 template<Ownership W, MemSpace M>
0273 struct UniverseIndexerData
0274 {
0275     Collection<size_type, W, M> surfaces;
0276     Collection<size_type, W, M> volumes;
0277 
0278     template<Ownership W2, MemSpace M2>
0279     UniverseIndexerData& operator=(UniverseIndexerData<W2, M2> const& other)
0280     {
0281         CELER_EXPECT(other);
0282 
0283         surfaces = other.surfaces;
0284         volumes = other.volumes;
0285 
0286         CELER_ENSURE(*this);
0287         return *this;
0288     }
0289 
0290     explicit CELER_FUNCTION operator bool() const
0291     {
0292         return !surfaces.empty() && !volumes.empty();
0293     }
0294 };
0295 
0296 //---------------------------------------------------------------------------//
0297 /*!
0298  * Persistent data used by all BIH trees.
0299  *
0300  * \todo move to orange/BihTreeData
0301  */
0302 template<Ownership W, MemSpace M>
0303 struct BIHTreeData
0304 {
0305     template<class T>
0306     using Items = Collection<T, W, M>;
0307 
0308     // Low-level storage
0309     Items<FastBBox> bboxes;
0310     Items<LocalVolumeId> local_volume_ids;
0311     Items<detail::BIHInnerNode> inner_nodes;
0312     Items<detail::BIHLeafNode> leaf_nodes;
0313 
0314     //! True if assigned
0315     explicit CELER_FUNCTION operator bool() const
0316     {
0317         // Note that inner_nodes may be empty for single-node trees
0318         return !bboxes.empty() && !local_volume_ids.empty()
0319                && !leaf_nodes.empty();
0320     }
0321 
0322     //! Assign from another set of data
0323     template<Ownership W2, MemSpace M2>
0324     BIHTreeData& operator=(BIHTreeData<W2, M2> const& other)
0325     {
0326         bboxes = other.bboxes;
0327         local_volume_ids = other.local_volume_ids;
0328         inner_nodes = other.inner_nodes;
0329         leaf_nodes = other.leaf_nodes;
0330 
0331         CELER_ENSURE(static_cast<bool>(*this) == static_cast<bool>(other));
0332         return *this;
0333     }
0334 };
0335 
0336 //---------------------------------------------------------------------------//
0337 /*!
0338  * Persistent data used by ORANGE implementation.
0339  *
0340  * Most data will be accessed through the invidual units, which reference data
0341  * in the "storage" below. The type and index for a universe ID will determine
0342  * the class type and data of the Tracker to instantiate. If *only* simple
0343  * units are present, then the \c simple_units data structure will just be
0344  * equal to a range (with the total number of universes present). Use
0345  * `universe_types` to switch on the type of universe; then `universe_indices`
0346  * to index into `simple_units` or `rect_arrays` or ...
0347  */
0348 template<Ownership W, MemSpace M>
0349 struct OrangeParamsData
0350 {
0351     template<class T>
0352     using Items = Collection<T, W, M>;
0353     template<class T>
0354     using UnivItems = Collection<T, W, M, UniverseId>;
0355     using RealId = OpaqueId<real_type>;
0356 
0357     //// DATA ////
0358 
0359     // Scalar attributes
0360     OrangeParamsScalars scalars;
0361 
0362     // High-level universe definitions
0363     UnivItems<UniverseType> universe_types;
0364     UnivItems<size_type> universe_indices;
0365     Items<SimpleUnitRecord> simple_units;
0366     Items<RectArrayRecord> rect_arrays;
0367     Items<TransformRecord> transforms;
0368 
0369     // BIH tree storage
0370     BIHTreeData<W, M> bih_tree_data;
0371 
0372     // Low-level storage
0373     Items<LocalSurfaceId> local_surface_ids;
0374     Items<LocalVolumeId> local_volume_ids;
0375     Items<RealId> real_ids;
0376     Items<logic_int> logic_ints;
0377     Items<real_type> reals;
0378     Items<FastReal3> fast_real3s;
0379     Items<SurfaceType> surface_types;
0380     Items<ConnectivityRecord> connectivity_records;
0381     Items<VolumeRecord> volume_records;
0382     Items<Daughter> daughters;
0383     Items<OrientedBoundingZoneRecord> obz_records;
0384 
0385     UniverseIndexerData<W, M> universe_indexer_data;
0386 
0387     //// METHODS ////
0388 
0389     //! True if assigned
0390     explicit CELER_FUNCTION operator bool() const
0391     {
0392         return scalars && !universe_types.empty()
0393                && universe_indices.size() == universe_types.size()
0394                && (bih_tree_data || !simple_units.empty())
0395                && ((!local_volume_ids.empty() && !logic_ints.empty()
0396                     && !reals.empty())
0397                    || surface_types.empty())
0398                && !volume_records.empty() && universe_indexer_data;
0399     }
0400 
0401     //! Assign from another set of data
0402     template<Ownership W2, MemSpace M2>
0403     OrangeParamsData& operator=(OrangeParamsData<W2, M2> const& other)
0404     {
0405         scalars = other.scalars;
0406 
0407         universe_types = other.universe_types;
0408         universe_indices = other.universe_indices;
0409         simple_units = other.simple_units;
0410         rect_arrays = other.rect_arrays;
0411         transforms = other.transforms;
0412 
0413         bih_tree_data = other.bih_tree_data;
0414 
0415         local_surface_ids = other.local_surface_ids;
0416         local_volume_ids = other.local_volume_ids;
0417         real_ids = other.real_ids;
0418         logic_ints = other.logic_ints;
0419         reals = other.reals;
0420         surface_types = other.surface_types;
0421         connectivity_records = other.connectivity_records;
0422         volume_records = other.volume_records;
0423         obz_records = other.obz_records;
0424         daughters = other.daughters;
0425         universe_indexer_data = other.universe_indexer_data;
0426 
0427         CELER_ENSURE(static_cast<bool>(*this) == static_cast<bool>(other));
0428         return *this;
0429     }
0430 };
0431 
0432 //---------------------------------------------------------------------------//
0433 // STATE
0434 //---------------------------------------------------------------------------//
0435 /*!
0436  * ORANGE state data.
0437  */
0438 template<Ownership W, MemSpace M>
0439 struct OrangeStateData
0440 {
0441     //// TYPES ////
0442 
0443     template<class T>
0444     using StateItems = celeritas::StateCollection<T, W, M>;
0445     template<class T>
0446     using Items = celeritas::Collection<T, W, M>;
0447 
0448     //// DATA ////
0449 
0450     // Note: this is duplicated from the associated OrangeParamsData .
0451     // It defines the stride into the preceding pseudo-2D Collections (pos,
0452     // dir, ..., etc.)
0453     size_type max_depth{0};
0454 
0455     // State with dimensions {num_tracks}
0456     StateItems<LevelId> level;
0457     StateItems<LevelId> surface_level;
0458     StateItems<LocalSurfaceId> surf;
0459     StateItems<Sense> sense;
0460     StateItems<BoundaryResult> boundary;
0461 
0462     // "Local" state, needed for Shift {num_tracks}
0463     StateItems<LevelId> next_level;
0464     StateItems<real_type> next_step;
0465     StateItems<LocalSurfaceId> next_surf;
0466     StateItems<Sense> next_sense;
0467 
0468     // State with dimensions {num_tracks, max_depth}
0469     Items<Real3> pos;
0470     Items<Real3> dir;
0471     Items<LocalVolumeId> vol;
0472     Items<UniverseId> universe;
0473 
0474     // Scratch space with dimensions {track}{max_faces}
0475     Items<Sense> temp_sense;
0476 
0477     // Scratch space with dimensions {track}{max_intersections}
0478     Items<FaceId> temp_face;
0479     Items<real_type> temp_distance;
0480     Items<size_type> temp_isect;
0481 
0482     //// METHODS ////
0483 
0484     //! True if sizes are consistent and nonzero
0485     explicit CELER_FUNCTION operator bool() const
0486     {
0487         // clang-format off
0488         return max_depth > 0
0489             && !level.empty()
0490             && surface_level.size() == this->size()
0491             && surf.size() == this->size()
0492             && sense.size() == this->size()
0493             && boundary.size() == this->size()
0494             && next_level.size() == this->size()
0495             && next_step.size() == this->size()
0496             && next_surf.size() == this->size()
0497             && next_sense.size() == this->size()
0498             && pos.size() == max_depth * this->size()
0499             && dir.size() == max_depth  * this->size()
0500             && vol.size() == max_depth  * this->size()
0501             && universe.size() == max_depth  * this->size()
0502             && !temp_sense.empty()
0503             && !temp_face.empty()
0504             && temp_distance.size() == temp_face.size()
0505             && temp_isect.size() == temp_face.size();
0506         // clang-format on
0507     }
0508 
0509     //! State size
0510     CELER_FUNCTION TrackSlotId::size_type size() const { return level.size(); }
0511 
0512     //! Assign from another set of data
0513     template<Ownership W2, MemSpace M2>
0514     OrangeStateData& operator=(OrangeStateData<W2, M2>& other)
0515     {
0516         CELER_EXPECT(other);
0517         max_depth = other.max_depth;
0518 
0519         level = other.level;
0520         surface_level = other.surface_level;
0521         surf = other.surf;
0522         sense = other.sense;
0523         boundary = other.boundary;
0524 
0525         next_level = other.next_level;
0526         next_step = other.next_step;
0527         next_surf = other.next_surf;
0528         next_sense = other.next_sense;
0529 
0530         pos = other.pos;
0531         dir = other.dir;
0532         vol = other.vol;
0533         universe = other.universe;
0534 
0535         temp_sense = other.temp_sense;
0536 
0537         temp_face = other.temp_face;
0538         temp_distance = other.temp_distance;
0539         temp_isect = other.temp_isect;
0540 
0541         CELER_ENSURE(*this);
0542         return *this;
0543     }
0544 };
0545 
0546 //---------------------------------------------------------------------------//
0547 /*!
0548  * Resize geometry tracking states.
0549  */
0550 template<MemSpace M>
0551 inline void resize(OrangeStateData<Ownership::value, M>* data,
0552                    HostCRef<OrangeParamsData> const& params,
0553                    size_type num_tracks)
0554 {
0555     CELER_EXPECT(data);
0556     CELER_EXPECT(num_tracks > 0);
0557 
0558     data->max_depth = params.scalars.max_depth;
0559 
0560     resize(&data->level, num_tracks);
0561     resize(&data->surface_level, num_tracks);
0562     resize(&data->surf, num_tracks);
0563     resize(&data->sense, num_tracks);
0564     resize(&data->boundary, num_tracks);
0565 
0566     resize(&data->next_level, num_tracks);
0567     resize(&data->next_step, num_tracks);
0568     resize(&data->next_surf, num_tracks);
0569     resize(&data->next_sense, num_tracks);
0570 
0571     size_type level_states = params.scalars.max_depth * num_tracks;
0572     resize(&data->pos, level_states);
0573     resize(&data->dir, level_states);
0574     resize(&data->vol, level_states);
0575     resize(&data->universe, level_states);
0576 
0577     size_type face_states = params.scalars.max_faces * num_tracks;
0578     resize(&data->temp_sense, face_states);
0579 
0580     size_type isect_states = params.scalars.max_intersections * num_tracks;
0581     resize(&data->temp_face, isect_states);
0582     resize(&data->temp_distance, isect_states);
0583     resize(&data->temp_isect, isect_states);
0584 
0585     CELER_ENSURE(*data);
0586 }
0587 
0588 //---------------------------------------------------------------------------//
0589 }  // namespace celeritas