Back to home page

EIC code displayed by LXR

 
 

    


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

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/builders/surface_factory_interface.hpp"
0013 #include "detray/builders/volume_builder_interface.hpp"
0014 #include "detray/definitions/geometry.hpp"
0015 #include "detray/geometry/surface.hpp"
0016 #include "detray/utils/concepts.hpp"
0017 #include "detray/utils/grid/concepts.hpp"
0018 #include "detray/utils/logging.hpp"
0019 
0020 // System include(s)
0021 #include <algorithm>
0022 #include <memory>
0023 #include <string>
0024 
0025 namespace detray {
0026 
0027 namespace detail {
0028 
0029 /// A functor to update the mask index in surface descriptors
0030 struct mask_index_update;
0031 
0032 }  // namespace detail
0033 
0034 /// @brief Provides basic functionality to build detector volumes
0035 template <typename detector_t>
0036 class volume_builder : public volume_builder_interface<detector_t> {
0037  public:
0038   using scalar_t = dscalar<typename detector_t::algebra_type>;
0039   using volume_type = typename detector_t::volume_type;
0040   using geo_obj_ids = typename detector_t::geo_obj_ids;
0041 
0042   /// Parametrized Constructor
0043   ///
0044   /// @param id flags the type of volume geometry (e.g. cylindrical, cuboid)
0045   /// @param idx the index of the volume in the detector volume container
0046   explicit volume_builder(const volume_id id, const dindex idx = 0)
0047       : m_volume{id} {
0048     m_volume.set_index(idx);
0049     m_volume.set_material(volume_type::material_id::e_none, dindex_invalid);
0050 
0051     // The first acceleration data structure in every volume is a brute
0052     // force method that will at least contain the portals
0053     m_volume.template set_accel_link<
0054         static_cast<typename volume_type::object_id>(0)>(
0055         detector_t::accel::id::e_surface_default, 0);
0056 
0057     DETRAY_VERBOSE_HOST("Created builder for volume: " << idx);
0058   };
0059 
0060   /// @returns the volume index in the detector volume container
0061   DETRAY_HOST
0062   auto vol_index() const -> dindex override { return m_volume.index(); }
0063 
0064   /// Toggles whether sensitive surfaces are added to the brute force method
0065   DETRAY_HOST
0066   void has_accel(bool toggle) override { m_has_accel = toggle; }
0067 
0068   /// @returns whether sensitive surfaces are added to the brute force method
0069   DETRAY_HOST
0070   bool has_accel() const override { return m_has_accel; }
0071 
0072   /// Sets the name @param volume_name for the volume
0073   DETRAY_HOST void set_name(std::string volume_name) override {
0074     m_volume_name = std::move(volume_name);
0075     DETRAY_VERBOSE_HOST("Set volume name: " << m_volume_name);
0076   }
0077 
0078   /// @returns the name of the volume
0079   DETRAY_HOST std::string_view name() override {
0080     if (m_volume_name.empty()) {
0081       // Consistent default after volume index is known
0082       m_volume_name = "unknown(volume_" + std::to_string(vol_index()) + ")";
0083     }
0084     return m_volume_name;
0085   }
0086 
0087   /// Access to the volume under construction - const
0088   DETRAY_HOST
0089   auto operator()() const -> const typename detector_t::volume_type& override {
0090     return m_volume;
0091   }
0092 
0093   /// Access to the volume under construction - non-const
0094   DETRAY_HOST
0095   auto operator()() -> typename detector_t::volume_type& override {
0096     return m_volume;
0097   }
0098 
0099   /// Build the volume with internal surfaces and portals and add it to the
0100   /// detector instance @param det
0101   DETRAY_HOST
0102   auto build(detector_t& det, typename detector_t::geometry_context ctx = {}) ->
0103       typename detector_t::volume_type* override {
0104     DETRAY_VERBOSE_HOST("Build surfaces...");
0105 
0106     assert(!m_surfaces.empty());
0107     assert(!m_transforms.empty());
0108     assert(!m_masks.all_empty());
0109 
0110     // Prepare volume data
0111     m_volume.set_index(static_cast<dindex>(det.volumes().size()));
0112 
0113     m_volume.set_transform(det.transform_store().size());
0114     det._transforms.push_back(m_trf);
0115 
0116     // Add all data from the builder to the detector containers
0117     add_to_detector(ctx, det);
0118 
0119     // Reset after the data was added to the detector
0120     m_surfaces.clear();
0121     m_transforms.clear(ctx);
0122     m_masks.clear_all();
0123 
0124     DETRAY_VERBOSE_HOST("Successfully built "
0125                         << m_volume.n_surfaces()
0126                         << " surfaces for volume: " << this->name());
0127 
0128     // Pass to decorator builders
0129     return &(det._volumes.back());
0130   }
0131 
0132   /// Adds a placement transform @param trf for the volume
0133   DETRAY_HOST
0134   void add_volume_placement(
0135       const typename detector_t::transform3_type& trf = {}) override {
0136     m_trf = trf;
0137   }
0138 
0139   /// Constructs a placement transform with identity rotation and translation
0140   /// @param t for the volume
0141   DETRAY_HOST
0142   void add_volume_placement(
0143       const typename detector_t::point3_type& t) override {
0144     m_trf = typename detector_t::transform3_type{t};
0145   }
0146 
0147   /// Constructs a placement transform from axes @param x and @param z
0148   /// and the translation @param t for the volume
0149   DETRAY_HOST
0150   void add_volume_placement(
0151       const typename detector_t::point3_type& t,
0152       const typename detector_t::vector3_type& x,
0153       const typename detector_t::vector3_type& z) override {
0154     m_trf = typename detector_t::transform3_type{t, z, x};
0155   }
0156 
0157   /// Add data for (a) new surface(s) to the builder
0158   DETRAY_HOST
0159   void add_surfaces(
0160       std::shared_ptr<surface_factory_interface<detector_t>> sf_factory,
0161       typename detector_t::geometry_context ctx = {}) override {
0162     DETRAY_VERBOSE_HOST("Add surface factory:");
0163 
0164     (*sf_factory)(m_volume, m_surfaces, m_transforms, m_masks, ctx);
0165   }
0166 
0167  protected:
0168   /// @returns Access to the surface descriptor data
0169   typename detector_t::surface_lookup_container& surfaces() override {
0170     return m_surfaces;
0171   }
0172 
0173   /// @returns Access to the surface/volume transform data
0174   typename detector_t::transform_container& transforms() override {
0175     return m_transforms;
0176   }
0177 
0178   /// @returns Access to the surface mask data
0179   typename detector_t::mask_container& masks() override { return m_masks; }
0180 
0181   /// Adds a new full set of volume components (e.g. transforms or masks)
0182   /// to the global detector data stores and updates all links.
0183   ///
0184   /// @param ctx is the geometry_context of the call
0185   /// @param det is the detector instance that the volume should be added to
0186   ///
0187   /// @note can throw an exception if input data is inconsistent
0188   template <geo_obj_ids surface_id = static_cast<geo_obj_ids>(0)>
0189   DETRAY_HOST auto add_to_detector(
0190       const typename detector_t::geometry_context ctx,
0191       detector_t& det) noexcept(false) -> void {
0192     // Append transforms
0193     const auto trf_offset = det.transform_store().size(ctx);
0194     det._transforms.append(std::move(m_transforms), ctx);
0195 
0196     // Surface index offset in the global detector container
0197     auto sf_offset{static_cast<dindex>(det.surfaces().size())};
0198 
0199     /// Find the surface range specified by @param sf_id
0200     auto find_range = [&](auto sf_id) {
0201       // Compare given id to surface identifier
0202       auto is_sf_type = [sf_id](const auto& sf) { return sf.id() == sf_id; };
0203 
0204       auto first = static_cast<dindex>(
0205           math::abs(std::ranges::find_if(m_surfaces, is_sf_type) -
0206                     std::begin(m_surfaces)));
0207 
0208       auto last = static_cast<dindex>(math::abs(
0209           std::ranges::rend(m_surfaces) -
0210           std::ranges::find_if(std::ranges::rbegin(m_surfaces),
0211                                std::ranges::rend(m_surfaces), is_sf_type)));
0212 
0213       // Set correct empty range, otherwise shift by global surface offset
0214       return (first >= last)
0215                  ? dindex_range{}
0216                  : dindex_range{first + sf_offset, last + sf_offset};
0217     };
0218 
0219     m_volume.template update_sf_link<surface_id::e_portal>(
0220         find_range(surface_id::e_portal));
0221 
0222     m_volume.template update_sf_link<surface_id::e_sensitive>(
0223         find_range(surface_id::e_sensitive));
0224 
0225     m_volume.template update_sf_link<surface_id::e_passive>(
0226         find_range(surface_id::e_passive));
0227 
0228     // Update mask and transform index of surfaces and set the
0229     // correct index of the surface in container
0230     std::size_t n_portals{0u};
0231     for (auto& sf_desc : m_surfaces) {
0232       det._masks.template visit<detail::mask_index_update>(sf_desc.mask(),
0233                                                            sf_desc);
0234       sf_desc.set_volume(m_volume.index());
0235       sf_desc.update_transform(trf_offset);
0236       sf_desc.set_index(sf_offset++);
0237 
0238       if (sf_desc.is_portal()) {
0239         ++n_portals;
0240       }
0241 
0242       det._surfaces.insert(sf_desc);
0243     }
0244 
0245     // Place the appropriate surfaces in the brute force search method.
0246     constexpr auto default_acc_id{detector_t::accel::id::e_surface_default};
0247 
0248     // Strip the source link from the lookup data structure
0249     typename detector_t::surface_container descriptors;
0250     descriptors.reserve(m_surfaces.size());
0251     std::ranges::transform(
0252         m_surfaces, std::back_inserter(descriptors),
0253         [](typename detector_t::surface_lookup_container::value_type& sf) {
0254           return static_cast<
0255               typename detector_t::surface_container::value_type>(sf);
0256         });
0257 
0258     // Add portals to brute force navigation method
0259     if (m_has_accel) {
0260       DETRAY_VERBOSE_HOST("-> Volume has acceleration structure:");
0261 
0262       typename detector_t::surface_container portals{};
0263       portals.reserve(n_portals);
0264 
0265       std::ranges::copy_if(
0266           descriptors, std::back_inserter(portals),
0267           [](auto& sf_desc) { return !sf_desc.is_sensitive(); });
0268 
0269       // Add only the portals to the brute force method
0270       DETRAY_VERBOSE_HOST(
0271           "-> Register only portals/passives with brute force "
0272           "accelerator");
0273 
0274       det._accelerators.template push_back<default_acc_id>(std::move(portals));
0275     } else {
0276       DETRAY_VERBOSE_HOST(
0277           "-> Register all surfaces with brute force accelerator");
0278 
0279       // Add all surfaces to the brute force method
0280       det._accelerators.template push_back<default_acc_id>(
0281           std::move(descriptors));
0282     }
0283 
0284     m_volume.template set_accel_link<surface_id>(
0285         default_acc_id,
0286         det.accelerator_store().template size<default_acc_id>() - 1u);
0287 
0288     // Append masks
0289     det._masks.append(std::move(m_masks));
0290 
0291     // Finally, add the volume descriptor to the detector
0292     det._volumes.push_back(m_volume);
0293   }
0294 
0295  private:
0296   /// Whether the volume will get an acceleration structure
0297   bool m_has_accel{false};
0298 
0299   /// The name of the volume
0300   std::string m_volume_name{};
0301 
0302   /// Volume descriptor of the volume under construction
0303   typename detector_t::volume_type m_volume{};
0304   /// Placement of the volume under construction
0305   typename detector_t::transform3_type m_trf{};
0306 
0307   /// Data of contained surfaces
0308   /// @{
0309   typename detector_t::surface_lookup_container m_surfaces{};
0310   typename detector_t::transform_container m_transforms{};
0311   typename detector_t::mask_container m_masks{};
0312   /// @}
0313 };
0314 
0315 namespace detail {
0316 
0317 /// A functor to update the mask index in surface objects
0318 struct mask_index_update {
0319   template <typename group_t, typename index_t, typename surface_t>
0320   DETRAY_HOST inline void operator()(const group_t& group,
0321                                      const index_t& /*index*/,
0322                                      surface_t& sf) const {
0323     sf.update_mask(static_cast<dindex>(std::size(group)));
0324   }
0325 };
0326 
0327 }  // namespace detail
0328 
0329 }  // namespace detray