Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-05-27 07:23:59

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/core/name_map.hpp"
0013 #include "detray/definitions/containers.hpp"
0014 #include "detray/definitions/detail/qualifiers.hpp"
0015 #include "detray/definitions/geometry.hpp"
0016 #include "detray/definitions/indexing.hpp"
0017 #include "detray/geometry/detail/volume_kernels.hpp"
0018 #include "detray/material/material.hpp"
0019 #include "detray/navigation/accelerators/search_window.hpp"
0020 #include "detray/utils/logging.hpp"
0021 #include "detray/utils/ranges.hpp"
0022 
0023 // System include(s)
0024 #include <algorithm>
0025 #include <iostream>
0026 #include <sstream>
0027 
0028 namespace detray {
0029 
0030 /// @brief Facade for a detray detector volume.
0031 ///
0032 /// Volume class that acts as a logical container in the detector for geometry
0033 /// objects, i.e. surfaces. The volume boundary surfaces, the so-called portals,
0034 /// carry index links that join adjacent volumes. The volume class itself does
0035 /// not contain any data itself, but keeps a descriptor with index-based links
0036 /// into the data containers that are managed by the detector.
0037 /// Every type of surface that is known by the volume (determined by the type
0038 /// and size of the ID enum in the descriptor) lives in it's own geometry
0039 /// accelerator data structure, e.g. portals reside in a brute force
0040 /// accelerator (a simple vector), while sensitive surfaces are usually sorted
0041 /// into a spatial grid.
0042 template <typename detector_t>  // @TODO: This needs a concept
0043 class tracking_volume {
0044   /// Linear algebra types
0045   using algebra_type = typename detector_t::algebra_type;
0046   using scalar_type = dscalar<algebra_type>;
0047   using point3_type = dpoint3D<algebra_type>;
0048 
0049   /// Volume descriptor type
0050   using descr_t = typename detector_t::volume_type;
0051   using context_t = typename detector_t::geometry_context;
0052 
0053  public:
0054   /// In case the geometry needs to be printed
0055   using name_map = detray::name_map;
0056   using object_id = descr_t::object_id;
0057 
0058   /// Not allowed: always needs a detector and a descriptor.
0059   tracking_volume() = delete;
0060 
0061   /// Constructor from detector @param det and volume descriptor
0062   /// @param vol_idx from that detector.
0063   constexpr tracking_volume(const detector_t &det, const descr_t &desc)
0064       : m_detector{det}, m_desc{desc} {
0065     assert(m_desc.index() < det.volumes().size());
0066     assert(m_desc.id() != volume_id::e_unknown);
0067   }
0068 
0069   /// Constructor from detector @param det and volume index @param vol_idx in
0070   /// that detector.
0071   constexpr tracking_volume(const detector_t &det, const dindex vol_idx)
0072       : tracking_volume(det, det.volume(vol_idx)) {}
0073 
0074   /// @returns access to the underlying detector
0075   DETRAY_HOST_DEVICE
0076   auto detector() const -> const detector_t & { return m_detector; }
0077 
0078   /// Equality operator
0079   ///
0080   /// @param rhs is the right hand side to be compared to
0081   DETRAY_HOST_DEVICE
0082   constexpr auto operator==(const tracking_volume &rhs) const -> bool {
0083     return (&m_detector == &(rhs.m_detector) && m_desc == rhs.m_desc);
0084   }
0085 
0086   /// @returns the volume shape id, e.g. 'cylinder'.
0087   DETRAY_HOST_DEVICE
0088   constexpr auto id() const -> volume_id { return m_desc.id(); }
0089 
0090   /// @returns the index of the volume in the detector volume container.
0091   DETRAY_HOST_DEVICE
0092   constexpr auto index() const -> dindex { return m_desc.index(); }
0093 
0094   /// @returns the (non contextual) transform for the placement of the
0095   /// volume in the detector geometry.
0096   DETRAY_HOST_DEVICE
0097   constexpr auto transform() const -> const
0098       typename detector_t::transform3_type & {
0099     return m_detector.transform_store().at(m_desc.transform());
0100   }
0101 
0102   /// @returns the center point of the volume.
0103   DETRAY_HOST_DEVICE
0104   constexpr auto center() const -> point3_type {
0105     return transform().translation();
0106   }
0107 
0108   /// @returns a pointer to the material parameters at the local position
0109   /// @param loc_p
0110   DETRAY_HOST_DEVICE constexpr auto material_parameters(
0111       const point3_type &loc_p) const -> const detray::material<scalar_type> * {
0112     return visit_material<typename detail::get_material_params>(loc_p);
0113   }
0114 
0115   /// @returns true if the volume carries material.
0116   DETRAY_HOST_DEVICE
0117   constexpr bool has_material() const { return m_desc.has_material(); }
0118 
0119   /// @returns an iterator pair for the requested type of surfaces.
0120   template <surface_id sf_type = surface_id::e_all>
0121   DETRAY_HOST_DEVICE constexpr decltype(auto) surfaces() const {
0122     if constexpr (sf_type == surface_id::e_all) {
0123       return detray::ranges::subrange{m_detector.surfaces(),
0124                                       m_desc.full_sf_range()};
0125     } else {
0126       return detray::ranges::subrange{m_detector.surfaces(),
0127                                       m_desc.template sf_link<sf_type>()};
0128     }
0129   }
0130 
0131   /// @returns an iterator pair for the volume portals.
0132   DETRAY_HOST_DEVICE constexpr decltype(auto) portals() const {
0133     return surfaces<surface_id::e_portal>();
0134   }
0135 
0136   /// @returns the total number of portal surfaces contained in the volume
0137   DETRAY_HOST_DEVICE constexpr dindex n_portals() const {
0138     const auto pt_idx_range = m_desc.template sf_link<surface_id::e_portal>();
0139 
0140     assert(pt_idx_range[1] > pt_idx_range[0]);
0141     return pt_idx_range[1] - pt_idx_range[0];
0142   }
0143 
0144   /// @returns the total number of sensitive surfaces contained in the volume
0145   DETRAY_HOST_DEVICE constexpr dindex n_sensitives() const {
0146     const auto sens_idx_range =
0147         m_desc.template sf_link<surface_id::e_sensitive>();
0148 
0149     assert(sens_idx_range[1] >= sens_idx_range[0]);
0150     return sens_idx_range[1] - sens_idx_range[0];
0151   }
0152 
0153   /// @returns the total number of passive surfaces contained in the volume
0154   DETRAY_HOST_DEVICE constexpr dindex n_passives() const {
0155     const auto ps_idx_range = m_desc.template sf_link<surface_id::e_passive>();
0156 
0157     assert(ps_idx_range[1] >= ps_idx_range[0]);
0158     return ps_idx_range[1] - ps_idx_range[0];
0159   }
0160 
0161   /// Apply a functor to all surfaces in one of the volume's acceleration
0162   /// structures
0163   ///
0164   /// @tparam I type of object to retrieve (passive, portal, sensitive etc)
0165   /// @tparam functor_t the prescription to be applied to the surfaces
0166   /// @tparam Args      types of additional arguments to the functor
0167   template <object_id I, typename functor_t, typename... Args>
0168   DETRAY_HOST_DEVICE constexpr void visit_accelerator(Args &&...args) const {
0169     static_assert(I < object_id::e_all);
0170 
0171     if (const auto &link{m_desc.template accel_link<I>()}; !link.is_invalid()) {
0172       // Run over the surfaces in a single acceleration data structure
0173       // and apply the functor to the resulting neighborhood
0174       m_detector.accelerator_store().template visit<functor_t>(
0175           link, std::forward<Args>(args)...);
0176     }
0177   }
0178 
0179   /// Apply a functor to all acceleration structures of this volume.
0180   ///
0181   /// @tparam I type of object to retrieve (surface types, daughters etc)
0182   /// @tparam functor_t the prescription to be applied to the acc structure
0183   /// @tparam Args      types of additional arguments to the functor
0184   template <typename functor_t, int I = static_cast<int>(object_id::e_all) - 1,
0185             typename... Args>
0186   DETRAY_HOST_DEVICE constexpr void visit_accelerators(Args &&...args) const {
0187     // Get the acceleration data structures for this volume and only visit,
0188     // if object type is contained in volume
0189     for (std::size_t id = 0u; id < static_cast<std::size_t>(object_id::e_size);
0190          ++id) {
0191       if (const auto &link{m_desc.accel_link()[id]}; !link.is_invalid()) {
0192         // Run over the surfaces in a single acceleration data structure
0193         // and apply the functor to the resulting neighborhood
0194         m_detector.accelerator_store().template visit<functor_t>(
0195             link, std::forward<Args>(args)...);
0196       }
0197     }
0198   }
0199 
0200   /// Apply a functor to all surfaces of a given surface id (portal, passive,
0201   /// sensitive) in the volume
0202   ///
0203   /// Translates the detray surface type id to the volume geometry object id
0204   ///
0205   /// @tparam functor_t the prescription to be applied to the surfaces
0206   /// @tparam Args      types of additional arguments to the functor
0207   template <surface_id I, typename functor_t, typename... Args>
0208   DETRAY_HOST_DEVICE constexpr void visit_surfaces(Args &&...args) const {
0209     using surface_getter_t = detail::apply_to_surfaces<functor_t>;
0210 
0211     // Dispatch to the correct acceleration structure
0212     if constexpr (I == surface_id::e_portal) {
0213       visit_accelerator<object_id::e_portal, surface_getter_t>(
0214           std::forward<Args>(args)...);
0215     } else if constexpr (I == surface_id::e_sensitive) {
0216       visit_accelerator<object_id::e_sensitive, surface_getter_t>(
0217           std::forward<Args>(args)...);
0218     } else if constexpr (I == surface_id::e_passive) {
0219       visit_accelerator<object_id::e_passive, surface_getter_t>(
0220           std::forward<Args>(args)...);
0221     } else {
0222       // Visit all surface types, but not other geometric objects
0223       // (e.g. daughter volumes)
0224       visit_accelerator<object_id::e_portal, surface_getter_t>(
0225           std::forward<Args>(args)...);
0226       if constexpr (object_id::e_portal != object_id::e_passive) {
0227         visit_accelerator<object_id::e_passive, surface_getter_t>(
0228             std::forward<Args>(args)...);
0229       }
0230       visit_accelerator<object_id::e_sensitive, surface_getter_t>(
0231           std::forward<Args>(args)...);
0232     }
0233   }
0234 
0235   /// Apply a functor to all daughter volumes
0236   ///
0237   /// @tparam functor_t the prescription to be applied to the daughter volumes
0238   /// @tparam Args      types of additional arguments to the functor
0239   template <typename functor_t, typename... Args>
0240   DETRAY_HOST_DEVICE constexpr void visit_daughter_volumes(
0241       Args &&...args) const {
0242     using volume_getter_t = detail::apply_to_volumes<functor_t>;
0243 
0244     visit_accelerator<object_id::e_volume, volume_getter_t>(
0245         std::forward<Args>(args)...);
0246   }
0247 
0248   /// Apply a functor to a neighborhood of geometric objects around a
0249   /// track position in the volume.
0250   ///
0251   /// @note: The acceleration structures in the volume might return different
0252   /// geometric objects (e.g. surfaces vs. volumes). The passed functor must
0253   /// provide corresponding overloads of the call operator.
0254   ///
0255   /// @tparam functor_t the prescription to be applied to the surfaces
0256   ///                   (customization point for the navigation)
0257   /// @tparam track_t   the track around which to build up the neighborhood
0258   /// @tparam Args      types of additional arguments to the functor
0259   template <object_id I, typename functor_t, typename track_t,
0260             concepts::arithmetic window_size_t, typename... Args>
0261   DETRAY_HOST_DEVICE constexpr void visit_neighborhood(
0262       const track_t &track, const search_window<window_size_t, 2> &win_size,
0263       const context_t &ctx, Args &&...args) const {
0264     if constexpr (I == object_id::e_all) {
0265       visit_accelerators<detail::apply_to_neighbourhood<functor_t>>(
0266           m_detector, m_desc, track, win_size, ctx,
0267           std::forward<Args>(args)...);
0268     } else {
0269       visit_accelerator<I, detail::apply_to_neighbourhood<functor_t>>(
0270           m_detector, m_desc, track, win_size, ctx,
0271           std::forward<Args>(args)...);
0272     }
0273   }
0274 
0275   /// Call a functor on the volume material with additional arguments.
0276   ///
0277   /// @tparam functor_t the prescription to be applied to the material
0278   /// @tparam Args      types of additional arguments to the functor
0279   template <typename functor_t, typename... Args>
0280   DETRAY_HOST_DEVICE constexpr auto visit_material(Args &&...args) const {
0281     assert(has_material());
0282     const auto &materials = m_detector.material_store();
0283     return materials.template visit<functor_t>(m_desc.material(),
0284                                                std::forward<Args>(args)...);
0285   }
0286 
0287   /// Do a consistency check on the volume after building the detector.
0288   ///
0289   /// @param os output stream for error messages.
0290   ///
0291   /// @returns true if the volume is consistent
0292   DETRAY_HOST bool self_check(std::ostream &os) const {
0293     if (id() == volume_id::e_unknown) {
0294       os << "DETRAY ERROR (HOST): Unknown volume shape type in volume:\n"
0295          << *this << std::endl;
0296       return false;
0297     }
0298     if (detail::is_invalid_value(index())) {
0299       os << "DETRAY ERROR (HOST): Volume index undefined in volume:\n"
0300          << *this << std::endl;
0301       return false;
0302     }
0303     if (index() >= m_detector.volumes().size()) {
0304       os << "DETRAY ERROR (HOST): Volume index out of bounds in volume:\n"
0305          << *this << std::endl;
0306       return false;
0307     }
0308     if (detail::is_invalid_value(m_desc.transform())) {
0309       os << "DETRAY ERROR (HOST): Volume transform undefined in volume:\n"
0310          << *this << std::endl;
0311       return false;
0312     }
0313     if (m_desc.transform() >= m_detector.transform_store().size()) {
0314       os << "DETRAY ERROR (HOST): Volume transform index out of bounds "
0315             "in volume:\n"
0316          << *this << std::endl;
0317       return false;
0318     }
0319     // Only check, if there is material in the detector
0320     if (!m_detector.material_store().all_empty() && has_material() &&
0321         m_desc.material().is_invalid_index()) {
0322       os << "DETRAY ERROR (HOST): Volume does not have valid material "
0323             "link:\n"
0324          << *this << std::endl;
0325       return false;
0326     }
0327     const auto &acc_link = m_desc.accel_link();
0328     if (detail::is_invalid_value(acc_link[0])) {
0329       os << "DETRAY ERROR (HOST): Link to portal lookup broken: " << acc_link[0]
0330          << "\n in volume: " << *this << std::endl;
0331       return false;
0332     }
0333     if (const auto &pt_link = m_desc.template sf_link<surface_id::e_portal>();
0334         detail::is_invalid_value(pt_link)) {
0335       os << "DETRAY ERROR (HOST): Link to portal surfaces broken: " << pt_link
0336          << "\n in volume: " << *this << std::endl;
0337       return false;
0338     }
0339     // Check consistency of surface ranges
0340     std::vector<dindex_range> sf_ranges = {
0341         m_desc.template sf_link<surface_id::e_portal>()};
0342 
0343     // Only add the other ranges in case they are not empty
0344     if (const auto &sens_range =
0345             m_desc.template sf_link<surface_id::e_sensitive>();
0346         sens_range[0] != sens_range[1]) {
0347       sf_ranges.push_back(sens_range);
0348     }
0349     if (const auto &psv_range =
0350             m_desc.template sf_link<surface_id::e_passive>();
0351         psv_range[0] != psv_range[1]) {
0352       sf_ranges.push_back(psv_range);
0353     }
0354 
0355     // Sort and check that the ranges are contiguous
0356     auto compare_ranges = [](const dindex_range &rg1, const dindex_range &rg2) {
0357       return rg1[0] < rg2[0];
0358     };
0359 
0360     std::ranges::sort(sf_ranges, compare_ranges);
0361 
0362     if ((sf_ranges.size() > 1 && sf_ranges[0][1] != sf_ranges[1][0]) ||
0363         (sf_ranges.size() > 2 && sf_ranges[1][1] != sf_ranges[2][0])) {
0364       os << "DETRAY ERROR (HOST): Surface index ranges not contiguous: "
0365          << m_desc.sf_link() << "\n in volume: " << *this << std::endl;
0366       return false;
0367     }
0368 
0369     // Warnings
0370     bool suspicious_links = false;
0371     std::stringstream warnings{};
0372     for (std::size_t i = 1u; i < acc_link.size(); ++i) {
0373       // An acceleration data structure link was set, but is invalid
0374       if (!acc_link[i].is_invalid_id() && acc_link[i].is_invalid_index()) {
0375         suspicious_links = true;
0376         warnings << "Link to acceleration data structure "
0377                  << static_cast<int>(acc_link[i].id()) << " is invalid"
0378                  << std::endl;
0379       }
0380     }
0381     if (suspicious_links) {
0382       DETRAY_WARN_HOST(warnings.str() << " in volume: " << *this);
0383     }
0384 
0385     return true;
0386   }
0387 
0388   /// @returns the volume name (add an offset for the detector name).
0389   DETRAY_HOST_DEVICE
0390   auto name(const name_map &names) const -> std::string {
0391     return names.empty() ? "volume " + std::to_string(m_desc.index())
0392                          : names.at(m_desc.index());
0393   }
0394 
0395   /// @returns a string stream that prints the volume details
0396   DETRAY_HOST
0397   friend std::ostream &operator<<(std::ostream &os, const tracking_volume &v) {
0398     os << v.m_desc;
0399     return os;
0400   }
0401 
0402  private:
0403   /// Access to the detector stores
0404   const detector_t &m_detector;
0405   /// Access to the descriptor
0406   const descr_t &m_desc;
0407 };
0408 
0409 }  // namespace detray