Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-10-17 08:32:08

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 // Project include(s)
0012 #include "detray/definitions/containers.hpp"
0013 #include "detray/definitions/indexing.hpp"
0014 #include "detray/material/material.hpp"
0015 #include "detray/material/material_rod.hpp"
0016 #include "detray/material/material_slab.hpp"
0017 
0018 // System include(s)
0019 #include <algorithm>
0020 #include <cstddef>
0021 #include <functional>
0022 #include <optional>
0023 #include <unordered_map>
0024 #include <utility>
0025 
0026 /// Helpers to find identical surface material in the detector material store
0027 /// while building, so that several surfaces can share one material entry.
0028 ///
0029 /// @note The comparisons are stricter than the @c operator== of the material
0030 /// types: two entries are only considered identical, if every parameter that
0031 /// enters the material interaction is bitwise equal (up to signed zero), so
0032 /// that sharing an entry cannot change any material lookup.
0033 namespace detray::detail {
0034 
0035 /// @returns true if all parameters of the materials @param lhs and @param rhs
0036 /// are equal
0037 template <concepts::scalar scalar_t>
0038 DETRAY_HOST constexpr bool is_identical(const material<scalar_t> &lhs,
0039                                         const material<scalar_t> &rhs) {
0040   return lhs == rhs && lhs.mass_density() == rhs.mass_density() &&
0041          lhs.molar_density() == rhs.molar_density() &&
0042          lhs.state() == rhs.state() &&
0043          lhs.has_density_effect_data() == rhs.has_density_effect_data() &&
0044          lhs.density_effect_data() == rhs.density_effect_data();
0045 }
0046 
0047 /// @returns true if the material slabs @param lhs and @param rhs are identical
0048 template <concepts::scalar scalar_t>
0049 DETRAY_HOST constexpr bool is_identical(const material_slab<scalar_t> &lhs,
0050                                         const material_slab<scalar_t> &rhs) {
0051   return is_identical(lhs.get_material(), rhs.get_material()) &&
0052          lhs.thickness() == rhs.thickness() &&
0053          lhs.thickness_in_X0() == rhs.thickness_in_X0() &&
0054          lhs.thickness_in_L0() == rhs.thickness_in_L0();
0055 }
0056 
0057 /// @returns true if the material rods @param lhs and @param rhs are identical
0058 template <concepts::scalar scalar_t>
0059 DETRAY_HOST constexpr bool is_identical(const material_rod<scalar_t> &lhs,
0060                                         const material_rod<scalar_t> &rhs) {
0061   // The radius in X0/L0 is derived from the radius and the material
0062   return is_identical(lhs.get_material(), rhs.get_material()) &&
0063          lhs.radius() == rhs.radius();
0064 }
0065 
0066 /// @returns true if the axes @param lhs and @param rhs have the same type
0067 /// and bin edges
0068 template <typename axis_lhs_t, typename axis_rhs_t>
0069 DETRAY_HOST bool is_identical_axis(const axis_lhs_t &lhs,
0070                                    const axis_rhs_t &rhs) {
0071   if (lhs.label() != rhs.label() || lhs.bounds() != rhs.bounds() ||
0072       lhs.binning() != rhs.binning() || lhs.nbins() != rhs.nbins() ||
0073       lhs.span() != rhs.span()) {
0074     return false;
0075   }
0076   const auto lhs_edges = lhs.bin_edges();
0077   const auto rhs_edges = rhs.bin_edges();
0078 
0079   return std::ranges::equal(lhs_edges, rhs_edges);
0080 }
0081 
0082 /// @returns true if the material grids @param lhs and @param rhs have
0083 /// identical axes and bin content
0084 ///
0085 /// @note Works across owning and non-owning grids. The @c operator== of the
0086 /// grid cannot be used here: For non-owning grids from the same collection it
0087 /// compares the bin edge offsets, i.e. the storage location, not the values
0088 template <typename grid_lhs_t, typename grid_rhs_t>
0089 DETRAY_HOST bool is_identical_grid(const grid_lhs_t &lhs,
0090                                    const grid_rhs_t &rhs) {
0091   static_assert(grid_lhs_t::dim == grid_rhs_t::dim);
0092 
0093   if (lhs.nbins() != rhs.nbins()) {
0094     return false;
0095   }
0096 
0097   // Compare the axes
0098   const bool same_axes = [&]<std::size_t... I>(std::index_sequence<I...>) {
0099     return (is_identical_axis(lhs.template get_axis<I>(),
0100                               rhs.template get_axis<I>()) &&
0101             ...);
0102   }(std::make_index_sequence<grid_lhs_t::dim>{});
0103 
0104   if (!same_axes) {
0105     return false;
0106   }
0107 
0108   // Compare the bin content
0109   for (dindex gbin = 0u; gbin < lhs.nbins(); ++gbin) {
0110     const auto &lhs_bin = lhs.bin(gbin);
0111     const auto &rhs_bin = rhs.bin(gbin);
0112 
0113     auto rhs_itr = rhs_bin.begin();
0114     for (const auto &lhs_entry : lhs_bin) {
0115       if (rhs_itr == rhs_bin.end() || !is_identical(lhs_entry, *rhs_itr)) {
0116         return false;
0117       }
0118       ++rhs_itr;
0119     }
0120     if (rhs_itr != rhs_bin.end()) {
0121       return false;
0122     }
0123   }
0124 
0125   return true;
0126 }
0127 
0128 /// @returns a hash of a homogeneous material entry (slab or rod), which is
0129 /// consistent with @c is_identical
0130 template <typename material_entry_t>
0131 DETRAY_HOST std::size_t material_hash(const material_entry_t &entry) {
0132   using scalar_t = typename material_entry_t::scalar_type;
0133 
0134   const auto &mat = entry.get_material();
0135   std::size_t seed{0u};
0136   for (const scalar_t v :
0137        {entry.thickness(), mat.X0(), mat.L0(), mat.Ar(), mat.Z()}) {
0138     // Same combination as boost::hash_combine
0139     seed ^=
0140         std::hash<scalar_t>{}(v) + 0x9e3779b9u + (seed << 6u) + (seed >> 2u);
0141   }
0142   return seed;
0143 }
0144 
0145 /// @brief Lookup of identical homogeneous material entries in a collection.
0146 ///
0147 /// Holds a hash index over the collection @param coll, which has to outlive
0148 /// the lookup. Entries that are appended to the collection via @c insert are
0149 /// indexed as well.
0150 template <typename collection_t>
0151 class homogeneous_material_lookup {
0152   using value_type = typename collection_t::value_type;
0153 
0154  public:
0155   /// Index all entries that are currently in the collection @param coll
0156   DETRAY_HOST explicit homogeneous_material_lookup(collection_t &coll)
0157       : m_coll{coll} {
0158     for (dindex i = 0u; i < static_cast<dindex>(m_coll.size()); ++i) {
0159       m_index.emplace(material_hash(m_coll[i]), i);
0160     }
0161   }
0162 
0163   /// @returns the index of an entry identical to @param entry in the
0164   /// collection, if one exists
0165   DETRAY_HOST std::optional<dindex> find(const value_type &entry) const {
0166     auto [first, last] = m_index.equal_range(material_hash(entry));
0167     for (auto itr = first; itr != last; ++itr) {
0168       if (is_identical(m_coll[itr->second], entry)) {
0169         return itr->second;
0170       }
0171     }
0172     return std::nullopt;
0173   }
0174 
0175   /// @returns the index of an entry identical to @param entry in the
0176   /// collection. The entry is appended, if no such entry exists yet.
0177   DETRAY_HOST dindex insert(const value_type &entry) {
0178     if (auto idx = find(entry); idx.has_value()) {
0179       return *idx;
0180     }
0181     const auto idx{static_cast<dindex>(m_coll.size())};
0182     m_coll.push_back(entry);
0183     m_index.emplace(material_hash(entry), idx);
0184 
0185     return idx;
0186   }
0187 
0188  private:
0189   collection_t &m_coll;
0190   std::unordered_multimap<std::size_t, dindex> m_index{};
0191 };
0192 
0193 }  // namespace detray::detail