|
|
|||
File indexing completed on 2026-05-23 07:48:32
0001 // This file is part of the ACTS project. 0002 // 0003 // Copyright (C) 2016 CERN for the benefit of the ACTS project 0004 // 0005 // This Source Code Form is subject to the terms of the Mozilla Public 0006 // License, v. 2.0. If a copy of the MPL was not distributed with this 0007 // file, You can obtain one at https://mozilla.org/MPL/2.0/. 0008 0009 #pragma once 0010 0011 #include <cstdint> 0012 #include <span> 0013 #include <stdexcept> 0014 #include <string> 0015 #include <vector> 0016 0017 #include <boost/functional/hash.hpp> 0018 0019 namespace ActsFatras { 0020 0021 /// Particle identifier that encodes additional event information. 0022 /// 0023 /// The barcode has to fulfill two separate requirements: be able to act as 0024 /// unique identifier for particles within an event and to encode details 0025 /// on the event structure for fast lookup. Since we only care about tracking 0026 /// here, we need to support two scenarios: 0027 /// 0028 /// * Identify which primary/secondary vertex particles belong to. No 0029 /// information on intermediate/unstable/invisible particles needs to be 0030 /// retained. This information is already available in the underlying 0031 /// generator event and should not be duplicated. 0032 /// * If (visible) particles convert, decay, or interact with the detector, 0033 /// we need to be able to identify the initial (primary) particle. Typical 0034 /// examples are pion nuclear interactions or electron/gamma conversions. 0035 /// 0036 /// The vertex information is encoded as two numbers that define the 0037 /// primary and secondary vertex. The primary vertex must be non-zero. 0038 /// Particles with a zero secondary vertex originate directly from the primary 0039 /// vertex. 0040 /// 0041 /// Within one vertex (primary+secondary) each particle is identified by 0042 /// a particle, generation, and sub-particle number. Particles originating 0043 /// from the vertex must have zero generation and zero sub-particle number; 0044 /// a consequence is that only non-zero generation can have non-zero 0045 /// sub-particle numbers. A non-zero generation indicates that the particle 0046 /// is a descendant of the original particle, e.g. from interactions or decay, 0047 /// while the sub-particle number identifies the descendant particle. 0048 /// 0049 /// With this encoding, non-primary particles and their primary parent can 0050 /// be easily identified at the expense of not storing the exact decay history. 0051 /// 0052 /// A barcode with all elements set to zero (the default value) is an invalid 0053 /// value that can be used e.g. to mark missing or unknown particles. 0054 /// 0055 /// ## Example 0056 /// 0057 /// A particle generated in a primary interaction might have the barcode 0058 /// 0059 /// 2|0|14|0|0 -> vertex=2 (primary), particle=14, generation=0, sub=0 0060 /// 0061 /// A simulation module might generate an interaction and create two new 0062 /// particles. These are descendants of the initial particle and the simulation 0063 /// module can generate the new barcodes directly by increasing the 0064 /// generation number and choosing sub-particle identifiers: 0065 /// 0066 /// 2|0|14|1|0 -> vertex=2 (primary), particle=14, generation=1, sub=0 0067 /// 2|0|14|1|1 -> vertex=2 (primary), particle=14, generation=1, sub=1 0068 /// 0069 /// If these secondary particles generate further tertiary particles 0070 /// the barcode would be e.g. 0071 /// 0072 /// 2|0|14|2|0 -> vertex=2 (primary), particle=14, generation=2, sub=0 0073 /// 0074 /// ## Possible issues 0075 /// 0076 /// The hierarchical nature of the barcode allows barcode creation without 0077 /// a central service. Since the full history is not stored, generated barcodes 0078 /// for higher-generation particles can overlap when generated by independent 0079 /// interactions. Assuming an initial primary particle with barcode 0080 /// 0081 /// 3|4|5|0|0 -> particle=5 0082 /// 0083 /// a first interaction might create a secondary particle by increasing the 0084 /// generation number (without destroying the initial particle) 0085 /// 0086 /// 3|4|5|1|0 -> particle=5, generation+=1, first sub-particle 0087 /// 0088 /// The initial particle gets simulated further and at another step a second 0089 /// interaction also creates a new particle. Since it knows nothing about 0090 /// the previously created particle (no central service), it will generate 0091 /// 0092 /// 3|4|5|1|0 -> particle=5, generation+=1, first sub-particle 0093 /// 0094 /// which is identical to the previously create barcode. These cases can be 0095 /// easily solved by renumbering the sub-particle identifier within each 0096 /// generation to contain unique values. However, this can only be done when all 0097 /// particles are known. 0098 class Barcode { 0099 public: 0100 /// Identifier type for primary vertex 0101 using PrimaryVertexId = std::uint16_t; 0102 /// Identifier type for secondary vertex 0103 using SecondaryVertexId = std::uint16_t; 0104 /// Identifier type for particle 0105 using ParticleId = std::uint32_t; 0106 /// Identifier type for generation 0107 using GenerationId = std::uint8_t; 0108 /// Identifier type for sub-particle 0109 using SubParticleId = std::uint32_t; 0110 0111 /// Construct an invalid barcode with all levels set to zero. 0112 /// @return An invalid barcode 0113 static constexpr Barcode Invalid() { return Barcode(); } 0114 0115 /// Empty barcode 0116 constexpr Barcode() = default; 0117 /// Copy constructor 0118 constexpr Barcode(const Barcode&) = default; 0119 /// Move constructor 0120 constexpr Barcode(Barcode&&) = default; 0121 /// Copy assignment operator 0122 /// @return Reference to this barcode after copying 0123 Barcode& operator=(const Barcode&) = default; 0124 /// Move assignment operator 0125 /// @return Reference to this barcode after moving 0126 Barcode& operator=(Barcode&&) = default; 0127 0128 /// Compare two barcodes 0129 /// @return True if barcodes are equal 0130 bool operator==(const Barcode&) const = default; 0131 friend constexpr auto operator<=>(Barcode lhs, Barcode rhs) { 0132 return lhs.asVector() <=> rhs.asVector(); 0133 } 0134 0135 /// Check validity of the barcode 0136 /// @param b The barcode to check 0137 /// @return True if barcode is valid 0138 static constexpr bool isValid(const Barcode& b) { return b != Invalid(); } 0139 /// Check if this barcode is valid 0140 /// @return True if this barcode is valid 0141 constexpr bool isValid() const { return isValid(*this); } 0142 0143 /// Return the primary vertex identifier. 0144 /// @return The primary vertex identifier value 0145 constexpr PrimaryVertexId vertexPrimary() const { return vertexPrimaryID; } 0146 0147 /// Return the secondary vertex identifier. 0148 /// @return The secondary vertex identifier value 0149 constexpr SecondaryVertexId vertexSecondary() const { 0150 return vertexSecondaryID; 0151 } 0152 0153 /// Return the particle identifier. 0154 /// @return The particle identifier value 0155 constexpr ParticleId particle() const { return particleID; } 0156 0157 /// Return the generation identifier. 0158 /// @return The generation identifier value 0159 constexpr GenerationId generation() const { return generationID; } 0160 0161 /// Return the sub-particle identifier. 0162 /// @return The sub-particle identifier value 0163 constexpr SubParticleId subParticle() const { return subParticleID; } 0164 0165 /// Export barcode as vector 0166 /// @return Vector of barcode components 0167 constexpr std::vector<std::uint32_t> asVector() const { 0168 return {vertexPrimary(), vertexSecondary(), particle(), generation(), 0169 subParticle()}; 0170 } 0171 0172 /// Create a new barcode with a different primary vertex identifier. 0173 /// @param id Primary vertex identifier to set 0174 /// @return New barcode with modified primary vertex identifier 0175 [[nodiscard]] 0176 constexpr Barcode withVertexPrimary(PrimaryVertexId id) const { 0177 Barcode barcode = *this; 0178 barcode.vertexPrimaryID = id; 0179 return barcode; 0180 } 0181 0182 /// Create a new barcode with a different secondary vertex identifier. 0183 /// @param id Secondary vertex identifier to set 0184 /// @return New barcode with modified secondary vertex identifier 0185 [[nodiscard]] 0186 constexpr Barcode withVertexSecondary(SecondaryVertexId id) const { 0187 Barcode barcode = *this; 0188 barcode.vertexSecondaryID = id; 0189 return barcode; 0190 } 0191 0192 /// Create a new barcode with a different particle identifier. 0193 /// @param id Particle identifier to set 0194 /// @return New barcode with modified particle identifier 0195 [[nodiscard]] 0196 constexpr Barcode withParticle(ParticleId id) const { 0197 Barcode barcode = *this; 0198 barcode.particleID = id; 0199 return barcode; 0200 } 0201 0202 /// Create a new barcode with a different generation identifier. 0203 /// @param id Generation identifier to set 0204 /// @return New barcode with modified generation identifier 0205 [[nodiscard]] 0206 constexpr Barcode withGeneration(GenerationId id) const { 0207 Barcode barcode = *this; 0208 barcode.generationID = id; 0209 return barcode; 0210 } 0211 0212 /// Create a new barcode with a different sub-particle identifier. 0213 /// @param id Sub-particle identifier to set 0214 /// @return New barcode with modified sub-particle identifier 0215 [[nodiscard]] 0216 constexpr Barcode withSubParticle(SubParticleId id) const { 0217 Barcode barcode = *this; 0218 barcode.subParticleID = id; 0219 return barcode; 0220 } 0221 0222 /// Create a new barcode from a vector 0223 /// @param data Vector containing exactly 5 elements 0224 /// @return New barcode with data from the vector 0225 [[nodiscard]] 0226 constexpr Barcode withData(std::span<std::uint32_t> data) { 0227 if (data.size() != 5) { 0228 throw std::invalid_argument( 0229 "Size of the data is " + std::to_string(data.size()) + 0230 " but Barcode requires data vector to have exactly 5 elements"); 0231 } 0232 0233 Barcode barcode = *this; 0234 barcode.vertexPrimaryID = data[0]; 0235 barcode.vertexSecondaryID = data[1]; 0236 barcode.particleID = data[2]; 0237 barcode.generationID = data[3]; 0238 barcode.subParticleID = data[4]; 0239 return barcode; 0240 } 0241 0242 /// Construct a new barcode representing a descendant particle. 0243 /// 0244 /// @param sub sub-particle index of the new barcode. 0245 /// @return New barcode with increased generation and specified sub-particle index 0246 Barcode makeDescendant(SubParticleId sub = 0u) const { 0247 Barcode barcode = *this; 0248 barcode.generationID += 1; 0249 barcode.subParticleID = sub; 0250 return barcode; 0251 } 0252 0253 /// Reduce the barcode to the vertex identifier. 0254 /// @return Barcode containing only vertex and generation information 0255 constexpr Barcode vertexId() const { 0256 // The vertex is identified by primary vertex, secondary vertex, and 0257 // generation. The other components are set to 0 so two particle originating 0258 // from the same vertex will have the same vertex ID. 0259 Barcode barcode = *this; 0260 barcode.particleID = 0u; 0261 barcode.subParticleID = 0u; 0262 return barcode; 0263 } 0264 0265 /// Reduce the barcode to the particle identifier. 0266 /// @return Barcode with subparticle identifier set to zero 0267 constexpr Barcode withoutSubparticle() const { 0268 // Provide a pseudo-barcode that contains all fields but not the 0269 // subparticle counter. This can be used as key in a map to store the 0270 // subparticle information 0271 Barcode barcode = *this; 0272 barcode.subParticleID = 0u; 0273 return barcode; 0274 } 0275 0276 /// Print the barcode 0277 friend inline std::ostream& operator<<(std::ostream& os, Barcode barcode) { 0278 // extra "+" to ensure printing as a number and not as a character 0279 os << "vp=" << +barcode.vertexPrimary() 0280 << "|vs=" << +barcode.vertexSecondary() << "|p=" << +barcode.particle() 0281 << "|g=" << +barcode.generation() << "|sp=" << +barcode.subParticle(); 0282 return os; 0283 } 0284 0285 /// Get hash of the barcode 0286 /// @return Hash value of the barcode 0287 std::size_t hash() const { 0288 std::size_t seed = 0; 0289 boost::hash_combine(seed, vertexPrimary()); 0290 boost::hash_combine(seed, vertexSecondary()); 0291 boost::hash_combine(seed, particle()); 0292 boost::hash_combine(seed, generation()); 0293 boost::hash_combine(seed, subParticle()); 0294 0295 return seed; 0296 } 0297 0298 private: 0299 PrimaryVertexId vertexPrimaryID = 0u; 0300 SecondaryVertexId vertexSecondaryID = 0u; 0301 ParticleId particleID = 0u; 0302 GenerationId generationID = 0u; 0303 SubParticleId subParticleID = 0u; 0304 }; 0305 0306 } // namespace ActsFatras 0307 0308 // specialize std::hash so Barcode can be used e.g. in an unordered_map 0309 namespace std { 0310 template <> 0311 struct hash<ActsFatras::Barcode> { 0312 auto operator()(ActsFatras::Barcode barcode) const noexcept { 0313 return barcode.hash(); 0314 } 0315 }; 0316 } // namespace std
| [ Source navigation ] | [ Diff markup ] | [ Identifier search ] | [ general search ] |
|
This page was automatically generated by the 2.3.7 LXR engine. The LXR team |
|