|
|
|||
File indexing completed on 2026-10-29 09:13:54
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 geocel/VolumePathFinder.hh 0006 //! \sa test/geocel/Volume.test.cc 0007 //---------------------------------------------------------------------------// 0008 #pragma once 0009 0010 #include "corecel/Assert.hh" 0011 #include "corecel/Macros.hh" 0012 #include "corecel/cont/Span.hh" 0013 #include "corecel/math/Algorithms.hh" 0014 0015 #include "VolumeData.hh" 0016 #include "VolumeView.hh" 0017 0018 namespace celeritas 0019 { 0020 //---------------------------------------------------------------------------// 0021 /*! 0022 * Reconstruct the volume-instance path for a \c VolumeUniqueInstanceId. 0023 * 0024 * Each \c VolumeUniqueInstanceId uniquely identifies a root-to-node path 0025 * (i.e., a Geant4 "touchable") in the volume DAG. This class performs the 0026 * inverse of \c VolumePathAccumulator: given an ID it fills a 0027 * caller-supplied scratch buffer with the \c VolumeInstanceId sequence and 0028 * returns a (possibly shorter) span of the result. 0029 * 0030 * \c VolumeUniqueInstanceId{0} (or \c world_unique_instance ) always denotes 0031 * the world volume itself (empty path), and \c VolumeUniqueInstanceId{} 0032 * (null/invalid) is rejected with a precondition failure. 0033 * The valid range is \f$[0,\, N_\text{unique})\f$. 0034 * 0035 * The algorithm descends from the world volume level by level, always seeding 0036 * from the world's direct children. 0037 * At each level it scans the current volume's children to find the unique 0038 * child whose subtree contains the remaining UID, exploiting the fact that 0039 * sibling offsets are strictly increasing. 0040 * The cost is \f$O(D \log C)\f$ where \f$D\f$ is the path depth and \f$C\f$ is 0041 * the maximum number of children of any volume. 0042 * 0043 * The scratch buffer must be at least \c num_volume_levels - 1 long (the 0044 * maximum possible path depth). 0045 * Successive calls reuse the same buffer, so callers must consume the returned 0046 * span before the next call. 0047 * 0048 * \par Example: 0049 * \code 0050 std::vector<VolumeInstanceId> buf(params.num_volume_levels()); 0051 VolumePathFinder find_path{params.host_ref(), make_span(buf)}; 0052 0053 VolumeUniqueInstanceId uid = ...; 0054 auto path = find_path(uid); // [vi_0, vi_1, ..., vi_k] 0055 * \endcode 0056 */ 0057 class VolumePathFinder 0058 { 0059 public: 0060 //!@{ 0061 //! \name Type aliases 0062 using ParamsRef = NativeCRef<VolumeParamsData>; 0063 using SpanVI = Span<VolumeInstanceId>; 0064 //!@} 0065 0066 public: 0067 //! Construct from volume hierarchy data and a scratch buffer 0068 CELER_FUNCTION VolumePathFinder(ParamsRef const& params, SpanVI scratch) 0069 : params_(params), scratch_(scratch) 0070 { 0071 CELER_EXPECT(scratch_.size() + 1 >= params_.scalars.num_volume_levels); 0072 } 0073 0074 // Reconstruct the path whose unique instance ID equals uid 0075 inline CELER_FUNCTION SpanVI operator()(VolumeUniqueInstanceId uid) const; 0076 0077 private: 0078 ParamsRef const& params_; 0079 SpanVI scratch_; 0080 }; 0081 0082 //---------------------------------------------------------------------------// 0083 // INLINE DEFINITIONS 0084 //---------------------------------------------------------------------------// 0085 /*! 0086 * Reconstruct the path whose unique instance ID equals \c uid. 0087 * 0088 * Returns an empty span for UID 0 (the world volume, before any instance is 0089 * entered). Otherwise writes the sequence of \c VolumeInstanceId values 0090 * from the world's first child down to the node identified by \c uid, and 0091 * returns a sub-span of the scratch buffer containing exactly those entries. 0092 */ 0093 CELER_FUNCTION auto VolumePathFinder::operator()( 0094 VolumeUniqueInstanceId uid) const -> SpanVI 0095 { 0096 CELER_EXPECT(uid < params_.scalars.num_unique_instances); 0097 using size_type = VolumeUniqueInstanceId::size_type; 0098 0099 size_type offset = uid.unchecked_get(); 0100 0101 // Always seed from world's direct children 0102 auto parents = VolumeView{params_, params_.scalars.world}.children(); 0103 VolumeLevelId::size_type depth = 0; 0104 0105 while (offset > 0) 0106 { 0107 CELER_ASSERT(depth < scratch_.size()); 0108 CELER_ASSERT(!parents.empty()); 0109 0110 // Sibling offsets are strictly increasing prefix sums; use 0111 // lower_bound to find the first child whose offset >= offset, 0112 // then step back one to get the chosen child. The offset of the first 0113 // parent is always greater than the current offset, so leave it out of 0114 // the search. 0115 auto comp = [this](VolumeInstanceId vi, size_type val) { 0116 return params_.unique_instance_offsets[vi] < val; 0117 }; 0118 auto it = lower_bound(parents.begin() + 1, parents.end(), offset, comp); 0119 VolumeInstanceId chosen = *--it; 0120 0121 scratch_[depth++] = chosen; 0122 offset -= params_.unique_instance_offsets[chosen] + size_type{1}; 0123 parents = VolumeView{params_, params_.volume_ids[chosen]}.children(); 0124 } 0125 0126 return scratch_.first(depth); 0127 } 0128 0129 //---------------------------------------------------------------------------// 0130 } // 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 |
|