Back to home page

EIC code displayed by LXR

 
 

    


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