|
|
|||
File indexing completed on 2025-12-16 09:41:49
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 using PrimaryVertexId = std::uint16_t; 0101 using SecondaryVertexId = std::uint16_t; 0102 using ParticleId = std::uint32_t; 0103 using GenerationId = std::uint8_t; 0104 using SubParticleId = std::uint32_t; 0105 0106 // Construct an invalid barcode with all levels set to zero. 0107 static constexpr Barcode Invalid() { return Barcode(); } 0108 0109 /// Empty barcode 0110 constexpr Barcode() = default; 0111 /// Copy constructor 0112 constexpr Barcode(const Barcode&) = default; 0113 /// Move constructor 0114 constexpr Barcode(Barcode&&) = default; 0115 /// Copy assignment operator 0116 /// @return Reference to this barcode after copying 0117 Barcode& operator=(const Barcode&) = default; 0118 /// Move assignment operator 0119 /// @return Reference to this barcode after moving 0120 Barcode& operator=(Barcode&&) = default; 0121 0122 /// Compare two barcodes 0123 bool operator==(const Barcode&) const = default; 0124 friend constexpr auto operator<=>(Barcode lhs, Barcode rhs) { 0125 return lhs.asVector() <=> rhs.asVector(); 0126 } 0127 0128 /// Check validity of the barcode 0129 static constexpr bool isValid(const Barcode& b) { return b != Invalid(); } 0130 constexpr bool isValid() const { return isValid(*this); } 0131 0132 /// Return the primary vertex identifier. 0133 /// @return The primary vertex identifier value 0134 constexpr PrimaryVertexId vertexPrimary() const { return vertexPrimaryID; } 0135 0136 /// Return the secondary vertex identifier. 0137 /// @return The secondary vertex identifier value 0138 constexpr SecondaryVertexId vertexSecondary() const { 0139 return vertexSecondaryID; 0140 } 0141 0142 /// Return the particle identifier. 0143 /// @return The particle identifier value 0144 constexpr ParticleId particle() const { return particleID; } 0145 0146 /// Return the generation identifier. 0147 /// @return The generation identifier value 0148 constexpr GenerationId generation() const { return generationID; } 0149 0150 /// Return the sub-particle identifier. 0151 /// @return The sub-particle identifier value 0152 constexpr SubParticleId subParticle() const { return subParticleID; } 0153 0154 /// Export barcode as vector 0155 constexpr std::vector<std::uint32_t> asVector() const { 0156 return {vertexPrimary(), vertexSecondary(), particle(), generation(), 0157 subParticle()}; 0158 } 0159 0160 /// Set the primary vertex identifier. 0161 /// @param id Primary vertex identifier to set 0162 /// @return Reference to this barcode for chaining 0163 [[deprecated("Use withVertexPrimary() instead")]] 0164 constexpr Barcode& setVertexPrimary(PrimaryVertexId id) { 0165 vertexPrimaryID = id; 0166 return *this; 0167 } 0168 0169 /// Set the secondary vertex identifier. 0170 /// @param id Secondary vertex identifier to set 0171 /// @return Reference to this barcode for chaining 0172 [[deprecated("Use withVertexSecondary() instead")]] 0173 constexpr Barcode& setVertexSecondary(SecondaryVertexId id) { 0174 vertexSecondaryID = id; 0175 return *this; 0176 } 0177 0178 /// Set the parent particle identifier. 0179 /// @param id Particle identifier to set 0180 /// @return Reference to this barcode for chaining 0181 [[deprecated("Use withParticle() instead")]] 0182 constexpr Barcode& setParticle(ParticleId id) { 0183 particleID = id; 0184 return *this; 0185 } 0186 0187 /// Set the particle identifier. 0188 /// @param id Generation identifier to set 0189 /// @return Reference to this barcode for chaining 0190 [[deprecated("Use withGeneration() instead")]] 0191 constexpr Barcode& setGeneration(GenerationId id) { 0192 generationID = id; 0193 return *this; 0194 } 0195 0196 /// Set the process identifier. 0197 /// @param id Sub-particle identifier to set 0198 /// @return Reference to this barcode for chaining 0199 [[deprecated("Use withSubParticle() instead")]] 0200 constexpr Barcode& setSubParticle(SubParticleId id) { 0201 subParticleID = id; 0202 return *this; 0203 } 0204 0205 /// Create a new barcode with a different primary vertex identifier. 0206 /// @param id Primary vertex identifier to set 0207 /// @return New barcode with modified primary vertex identifier 0208 [[nodiscard]] 0209 constexpr Barcode withVertexPrimary(PrimaryVertexId id) const { 0210 Barcode barcode = *this; 0211 barcode.vertexPrimaryID = id; 0212 return barcode; 0213 } 0214 0215 /// Create a new barcode with a different secondary vertex identifier. 0216 /// @param id Secondary vertex identifier to set 0217 /// @return New barcode with modified secondary vertex identifier 0218 [[nodiscard]] 0219 constexpr Barcode withVertexSecondary(SecondaryVertexId id) const { 0220 Barcode barcode = *this; 0221 barcode.vertexSecondaryID = id; 0222 return barcode; 0223 } 0224 0225 /// Create a new barcode with a different particle identifier. 0226 /// @param id Particle identifier to set 0227 /// @return New barcode with modified particle identifier 0228 [[nodiscard]] 0229 constexpr Barcode withParticle(ParticleId id) const { 0230 Barcode barcode = *this; 0231 barcode.particleID = id; 0232 return barcode; 0233 } 0234 0235 /// Create a new barcode with a different generation identifier. 0236 /// @param id Generation identifier to set 0237 /// @return New barcode with modified generation identifier 0238 [[nodiscard]] 0239 constexpr Barcode withGeneration(GenerationId id) const { 0240 Barcode barcode = *this; 0241 barcode.generationID = id; 0242 return barcode; 0243 } 0244 0245 /// Create a new barcode with a different sub-particle identifier. 0246 /// @param id Sub-particle identifier to set 0247 /// @return New barcode with modified sub-particle identifier 0248 [[nodiscard]] 0249 constexpr Barcode withSubParticle(SubParticleId id) const { 0250 Barcode barcode = *this; 0251 barcode.subParticleID = id; 0252 return barcode; 0253 } 0254 0255 /// Create a new barcode from a vector 0256 /// @param data Vector containing exactly 5 elements 0257 /// @return New barcode with data from the vector 0258 [[nodiscard]] 0259 constexpr Barcode withData(std::span<std::uint32_t> data) { 0260 if (data.size() != 5) { 0261 throw std::invalid_argument( 0262 "Size of the data is " + std::to_string(data.size()) + 0263 " but Barcode requires data vector to have exactly 5 elements"); 0264 } 0265 0266 Barcode barcode = *this; 0267 barcode.vertexPrimaryID = data[0]; 0268 barcode.vertexSecondaryID = data[1]; 0269 barcode.particleID = data[2]; 0270 barcode.generationID = data[3]; 0271 barcode.subParticleID = data[4]; 0272 return barcode; 0273 } 0274 0275 /// Construct a new barcode representing a descendant particle. 0276 /// 0277 /// @param sub sub-particle index of the new barcode. 0278 /// @return New barcode with increased generation and specified sub-particle index 0279 Barcode makeDescendant(SubParticleId sub = 0u) const { 0280 Barcode barcode = *this; 0281 barcode.generationID += 1; 0282 barcode.subParticleID = sub; 0283 return barcode; 0284 } 0285 0286 /// Reduce the barcode to the vertex identifier. 0287 /// @return Barcode containing only vertex and generation information 0288 constexpr Barcode vertexId() const { 0289 // The vertex is identified by primary vertex, secondary vertex, and 0290 // generation. The other components are set to 0 so two particle originating 0291 // from the same vertex will have the same vertex ID. 0292 Barcode barcode = *this; 0293 barcode.particleID = 0u; 0294 barcode.subParticleID = 0u; 0295 return barcode; 0296 } 0297 0298 /// Reduce the barcode to the particle identifier. 0299 constexpr Barcode withoutSubparticle() const { 0300 // Provide a pseudo-barcode that contains all fields but not the 0301 // subparticle counter. This can be used as key in a map to store the 0302 // subparticle information 0303 Barcode barcode = *this; 0304 barcode.subParticleID = 0u; 0305 return barcode; 0306 } 0307 0308 /// Print the barcode 0309 friend inline std::ostream& operator<<(std::ostream& os, Barcode barcode) { 0310 // extra "+" to ensure printing as a number and not as a character 0311 os << "vp=" << +barcode.vertexPrimary() 0312 << "|vs=" << +barcode.vertexSecondary() << "|p=" << +barcode.particle() 0313 << "|g=" << +barcode.generation() << "|sp=" << +barcode.subParticle(); 0314 return os; 0315 } 0316 0317 /// Get hash of the barcode 0318 std::size_t hash() const { 0319 std::size_t seed = 0; 0320 boost::hash_combine(seed, vertexPrimary()); 0321 boost::hash_combine(seed, vertexSecondary()); 0322 boost::hash_combine(seed, particle()); 0323 boost::hash_combine(seed, generation()); 0324 boost::hash_combine(seed, subParticle()); 0325 0326 return seed; 0327 } 0328 0329 private: 0330 PrimaryVertexId vertexPrimaryID = 0u; 0331 SecondaryVertexId vertexSecondaryID = 0u; 0332 ParticleId particleID = 0u; 0333 GenerationId generationID = 0u; 0334 SubParticleId subParticleID = 0u; 0335 }; 0336 0337 } // namespace ActsFatras 0338 0339 // specialize std::hash so Barcode can be used e.g. in an unordered_map 0340 namespace std { 0341 template <> 0342 struct hash<ActsFatras::Barcode> { 0343 auto operator()(ActsFatras::Barcode barcode) const noexcept { 0344 return barcode.hash(); 0345 } 0346 }; 0347 } // 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 |
|