Back to home page

EIC code displayed by LXR

 
 

    


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