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