Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-01-01 09:53:32

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