Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-05-27 07:24:01

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/indexing.hpp"
0013 #include "detray/definitions/math.hpp"
0014 #include "detray/geometry/tracking_volume.hpp"
0015 #include "detray/utils/ranges.hpp"
0016 #include "detray/utils/type_traits.hpp"
0017 
0018 // System include(s)
0019 #include <algorithm>
0020 #include <cstddef>
0021 #include <iterator>
0022 #include <queue>
0023 #include <sstream>
0024 #include <string>
0025 #include <utility>
0026 
0027 namespace detray {
0028 
0029 /// @brief Placeholder struct for the implementation of an inspection function
0030 /// that will be executed, when a node is visited.
0031 template <typename node_t>
0032 struct void_node_inspector {
0033   bool operator()(const node_t & /*n*/) const { return true; }
0034 };
0035 
0036 /// @brief Placeholder struct for an action while walking through the graph.
0037 template <typename node_t>
0038 struct void_actor {
0039   void operator()(const node_t & /*n*/,
0040                   const dindex_range & /*edge_range*/) const {
0041     /*Do nothing*/
0042   }
0043 };
0044 
0045 /// @brief Uses the geometry implementations to walk through their graph-like
0046 /// structure breadth first.
0047 ///
0048 /// @tparam detector_t the type of geometry we want to walk along.
0049 /// @tparam node_inspector the type of inspection to perform when a node is
0050 ///         visited
0051 ///
0052 /// @note The detector has to expose the volume/portal interface.
0053 template <typename detector_t,
0054           typename node_inspector =
0055               void_node_inspector<typename detector_t::volume_type>,
0056           template <typename...> class vector_t = dvector>
0057 class volume_graph {
0058  public:
0059   using geo_obj_ids = typename detector_t::geo_obj_ids;
0060   using volume_container_t = vector_t<typename detector_t::volume_type>;
0061   using mask_container_t = typename detector_t::mask_container;
0062 
0063   /// @brief Builds a graph node from the detector collections on the fly.
0064   ///
0065   /// The node collection builds a graph node from a volume and the mask
0066   /// links in its surfaces, which are half edges of the graph. Every node
0067   /// has its index in the detector volume container and a vector of half
0068   /// edges.
0069   struct node_generator
0070       : public detray::ranges::view_interface<node_generator> {
0071     /// A functor to retrieve the half-edges of a node
0072     struct node_builder {
0073       inline void operator()(
0074           const typename detector_t::surface_type &sf,
0075           vector_t<typename detector_t::surface_type::mask_link> &half_edges)
0076           const {
0077         half_edges.push_back(sf.mask());
0078       }
0079     };
0080 
0081     /// A node in the graph has an index (volume index) and a collection of
0082     /// edges that belong to it (mask link of every surface in the volume).
0083     /// One mask link is a half edge in the graph.
0084     struct node {
0085       /// Constructor from a detectors volume and surface collections
0086       explicit node(const tracking_volume<detector_t> &volume)
0087           : m_idx(volume.index()) {
0088         // @TODO: Remove duplicates from multiple placements of surfaces
0089         volume.template visit_surfaces<surface_id::e_all, node_builder>(
0090             m_half_edges);
0091       }
0092 
0093       /// @returns volume index of the node
0094       dindex index() const { return m_idx; }
0095 
0096       /// @returns edges of the node
0097       const auto &half_edges() const { return m_half_edges; }
0098 
0099       /// Node(volume) index
0100       dindex m_idx;
0101       /// Vector of half edges towards other volumes
0102       vector_t<typename detector_t::surface_type::mask_link> m_half_edges;
0103     };
0104 
0105     /// @brief Iterator over the graph nodes.
0106     struct iterator {
0107       // Iterator type defs
0108       using volume_iter = detray::ranges::const_iterator_t<volume_container_t>;
0109 
0110       using difference_type = std::iter_difference_t<volume_iter>;
0111       using value_type = node;
0112       using pointer = typename std::iterator_traits<volume_iter>::pointer;
0113       using reference = std::iter_reference_t<volume_iter>;
0114       using iterator_category =
0115           typename std::iterator_traits<volume_iter>::iterator_category;
0116 
0117       /// No default construction because of reference member
0118       iterator() = delete;
0119 
0120       /// Constructor from an iterator on the detector volume container
0121       /// and a reference to its surface container.
0122       iterator(volume_iter &&vol_itr, const detector_t &det)
0123           : m_vol_itr(std::move(vol_itr)), m_det(det) {}
0124 
0125       /// Equality operator
0126       bool operator==(const iterator &rhs) const {
0127         return m_vol_itr == rhs.m_vol_itr;
0128       }
0129 
0130       /// Dereference operator @returns a graph node
0131       node operator*() { return node({m_det, m_vol_itr->index()}); }
0132 
0133       /// Prefix increment. No postfix increment implemented
0134       iterator &operator++() {
0135         ++m_vol_itr;
0136         return *this;
0137       }
0138 
0139       /// Prefix decrement. No postfix decrement implemented
0140       iterator &operator--() {
0141         --m_vol_itr;
0142         return *this;
0143       }
0144 
0145       /// Advances iterator by @param j
0146       constexpr auto operator+=(const difference_type j) -> iterator & {
0147         m_vol_itr += j;
0148         return *this;
0149       }
0150 
0151       /// Advances iterator by - @param j
0152       constexpr auto operator-=(const difference_type j) -> iterator & {
0153         return *this += -j;
0154       }
0155 
0156      protected:
0157       /// @returns an iterator that has been advanced by @param j
0158       friend constexpr auto operator+(const difference_type j,
0159                                       const iterator &itr) -> iterator {
0160         return {itr.m_vol_itr + j, itr.m_det};
0161       }
0162 
0163       /// @returns an iterator that has been advanced by - @param j
0164       friend constexpr auto operator-(const difference_type j,
0165                                       const iterator &itr) -> iterator {
0166         return itr + -j;
0167       }
0168 
0169       /// @returns distance between two iterators
0170       friend constexpr auto operator-(const iterator &lhs, const iterator &rhs)
0171           -> difference_type {
0172         return lhs.m_vol_itr - rhs.m_vol_itr;
0173       }
0174 
0175      private:
0176       /// Iterator over the detector volume container.
0177       volume_iter m_vol_itr;
0178       /// Access to detector surfaces
0179       const detector_t &m_det;
0180     };
0181 
0182     /// Node iterator type
0183     using iterator_t = iterator;
0184 
0185     /// No default constructor (holds member reference)
0186     node_generator() = delete;
0187 
0188     /// Constructor from a detector containers
0189     node_generator(const volume_container_t &volumes, const detector_t &det)
0190         : m_volumes(volumes), m_det(det) {}
0191 
0192     /// @returns beginning of collection
0193     iterator begin() const { return iterator(m_volumes.begin(), m_det); }
0194 
0195     /// @returns end of collection
0196     iterator end() const { return iterator(m_volumes.end(), m_det); }
0197 
0198     /// @returns the number of nodes
0199     dindex size() const { return static_cast<dindex>(m_volumes.size()); }
0200 
0201     const volume_container_t &m_volumes;
0202     const detector_t &m_det;
0203   };
0204 
0205   /// @brief Builds graph edges from the detector mask collection on the fly.
0206   ///
0207   /// Iterates through the detectors mask store given a volume id (graph
0208   /// node) The graph edges are constructed for the node on the fly from the
0209   /// half edges in the @c _edges collection (detector masks).
0210   struct edge_generator
0211       : public detray::ranges::view_interface<edge_generator> {
0212     /// The link to the next volume
0213     using mask_edge_t = typename detector_t::surface_type::navigation_link;
0214     /// The link to the surfaces mask
0215     using mask_link_t = typename detector_t::surface_type::mask_link;
0216 
0217     /// An edge in the graph connects two nodes (volumes). It is constructed
0218     /// from a surface and its mask.
0219     struct edge {
0220       edge(const dindex volume_id, const dindex link)
0221           : _from(volume_id), _to(link) {}
0222 
0223       dindex from() const { return _from; }
0224       dindex to() const { return _to; }
0225 
0226       dindex _from;
0227       dindex _to;
0228     };
0229 
0230     /// Nested functor that fills the edges from a mask container
0231     struct edges_builder {
0232       /// @brief Builds the collection of graph edges for a given
0233       /// node.
0234       ///
0235       /// From the volume index and a mask link owned by one of the
0236       /// volumes surfaces a vector of graph edges is built.
0237       /// The mask container calls this functor and provides the correct
0238       /// mask group from which the surface masks can be obtained from
0239       /// the surfaces mask range.
0240       ///
0241       /// @param mask_group the group of masks in the mask container
0242       ///                   of the detector
0243       /// @param volume_id the index of the volume/node for which to
0244       ///                  build edges
0245       /// @param mask_range the range of masks in the group for which
0246       ///                   to build edges
0247       template <typename mask_group_t, typename mask_range_t>
0248       inline vector_t<edge> operator()(const mask_group_t &mask_group,
0249                                        const mask_range_t &mask_range,
0250                                        const dindex volume_id) {
0251         vector_t<edge> edges{};
0252         for (const auto &mask :
0253              detray::ranges::subrange(mask_group, mask_range)) {
0254           edges.emplace_back(volume_id, mask.volume_link());
0255         }
0256         return edges;
0257       }
0258     };
0259 
0260     /// No default constructor (holds member reference)
0261     edge_generator() = delete;
0262 
0263     /// Constructor from the detector masks store.
0264     explicit edge_generator(const typename detector_t::mask_container &masks,
0265                             const dindex volume_id = 0,
0266                             const mask_link_t mask_link = {})
0267         : _masks(masks), _volume_id(volume_id), _mask_link{mask_link} {
0268       _edges = _masks.template visit<edges_builder>(_mask_link, _volume_id);
0269     }
0270 
0271     /// @returns begging of graph edges container
0272     auto begin() { return _edges.begin(); }
0273 
0274     /// @returns end of graph edges container
0275     auto end() { return _edges.end(); }
0276 
0277     /// @return updated version of @c edge_generator
0278     edge_generator &operator()(dindex volume_id, const mask_link_t mask_link) {
0279       _volume_id = volume_id;
0280       _mask_link = mask_link;
0281       _edges.clear();
0282       _edges = _masks.template visit<edges_builder>(_mask_link, _volume_id);
0283 
0284       return *this;
0285     }
0286 
0287     const mask_container_t &_masks;
0288     vector_t<edge> _edges;
0289     dindex _volume_id;
0290     mask_link_t _mask_link;
0291   };
0292 
0293   // Graph nodes
0294   using node_type = typename node_generator::node;
0295   // Graph edges
0296   using edge_type = typename edge_generator::edge;
0297 
0298   /// Default constructor
0299   volume_graph() = delete;
0300 
0301   /// @brief Build from existing nodes and edges, which are provide by the
0302   /// detector.
0303   ///
0304   /// @param det provides: geometry volumes that become the graph nodes,
0305   /// surfaces which are needed to index the correct masks and the
0306   /// masks that link to volumes and become graph edges.
0307   explicit volume_graph(const detector_t &det)
0308       : _nodes(det.volumes(), det), _edges(det.mask_store()), _adj_matrix{0} {
0309     build();
0310   }
0311 
0312   /// Default destructor: we don't own anything.
0313   ~volume_graph() = default;
0314 
0315   /// @return number of nodes
0316   dindex n_nodes() const { return static_cast<dindex>(_nodes.size()); }
0317 
0318   /// @return node collection - const access. */
0319   const auto &nodes() const { return _nodes; }
0320 
0321   /// @return number of surfaces/portals in the geometry */
0322   dindex n_edges(dindex volume_id) const {
0323     const node_type n = _nodes[volume_id];
0324     // Number of half_edges is equal to number of edges for volume
0325     return static_cast<dindex>(n.half_edges().size());
0326   }
0327 
0328   /// @return edges collection - const access.
0329   const auto &edges() const { return _edges; }
0330 
0331   /// @return graph adjacency - const access.
0332   const auto &adjacency_matrix() const { return _adj_matrix; }
0333 
0334   /// Walks breadth first through the geometry objects.
0335   /*template <typename action_t = void_actor<node_type>>
0336   void bfs(action_t actor = {}) const {
0337       // Do node inspection
0338       const auto inspector = node_inspector();
0339 
0340       node_type const *current = nullptr;
0341       vector_t<bool> visited(_nodes.size(), false);
0342 
0343       // Nodes to be visited. Start at first node
0344       std::queue<node_type const *> node_queue;
0345       node first_node = _nodes.front();
0346       node_queue.push(&(first_node));
0347 
0348       // Visit adjacent nodes and check current one
0349       while (!node_queue.empty()) {
0350           // Inspect
0351           current = node_queue.front();
0352           if (visited[current->index()]) {
0353               node_queue.pop();
0354               continue;
0355           }
0356           inspector(*current);
0357 
0358           // Visit neighbors and perform action
0359           actor(*current, current->template
0360   range<static_cast<geo_obj_ids>(0)>());
0361 
0362           // Add neighbors to queue
0363           for (const auto &edg_link : current->edges()) {
0364               for (const auto &edg :
0365   edge_generator::iterator(current->index(), edg_link, _edges)) { dindex nbr
0366   = edg.to();
0367                   // If not leaving world and if not visited, enqueue the node
0368                   if ((nbr != dindex_invalid && nbr > 0) && not
0369   visited[nbr]) { node_queue.push(&(_nodes[nbr]));
0370                   }
0371               }
0372           }
0373 
0374           // This node has been visited
0375           visited[current->index()] = true;
0376           node_queue.pop();
0377       }
0378   }*/
0379 
0380   /// @returns the linking description as a string.
0381   inline std::string to_string() const {
0382     std::stringstream stream;
0383     dindex dim = n_nodes() + 1u;
0384     for (const auto &n : _nodes) {
0385       stream << "[>>] Node with index " << n.index() << std::endl;
0386       stream << " -> edges: " << std::endl;
0387       for (unsigned int i = 0u; i < dim; ++i) {
0388         const dindex degr = _adj_matrix[dim * n.index() + i];
0389         if (degr == 0) {
0390           continue;
0391         }
0392         std::string n_occur =
0393             degr > 1 ? "\t\t\t\t(" + std::to_string(degr) + "x)" : "";
0394 
0395         // Edge that leads out of the detector world
0396         if (i == dim - 1u && degr != 0u) {
0397           stream << "    -> leaving world " + n_occur << std::endl;
0398         } else {
0399           stream << "    -> " << std::to_string(i) + "\t" + n_occur
0400                  << std::endl;
0401         }
0402       }
0403     }
0404     return stream.str();
0405   }
0406 
0407   /// @returns the linking description as a string in DOT syntax.
0408   inline std::string to_dot_string() const {
0409     std::stringstream stream;
0410     dindex dim = n_nodes() + 1u;
0411 
0412     stream << "strict graph {" << std::endl;
0413     stream << "    layout=neato;" << std::endl;
0414     stream << "    node [margin=0,shape=circle,style=filled];" << std::endl;
0415     stream << "    overlap=scale;" << std::endl;
0416     stream << "    splines=true;" << std::endl;
0417     stream << "    mode=KK;" << std::endl;
0418     stream << std::endl;
0419     stream << R"(    exit [label="OOB",fillcolor="firebrick1"];)" << std::endl;
0420 
0421     for (const auto &n : _nodes) {
0422       stream << "    v" << n.index() << ";" << std::endl;
0423     }
0424 
0425     stream << std::endl;
0426 
0427     for (const auto &n : _nodes) {
0428       for (unsigned int i = 0u; i < dim; ++i) {
0429         const dindex degr = _adj_matrix[dim * n.index() + i];
0430         if (degr == 0) {
0431           continue;
0432         }
0433 
0434         bool to_oob = i == dim - 1u;
0435         bool to_self = n.index() == i;
0436 
0437         std::string label = degr > 1 ? std::to_string(degr) : "";
0438         std::string dest = to_oob ? "exit" : ("v" + std::to_string(i));
0439         std::string dir = (to_oob || to_self) ? "forward" : "both";
0440 
0441         stream << "    v" << n.index() << " -- " << dest << " [label=\""
0442                << label << "\", dir=" << dir << "];" << std::endl;
0443       }
0444     }
0445 
0446     stream << "}" << std::endl;
0447 
0448     return stream.str();
0449   }
0450 
0451  private:
0452   /// @brief Go through the nodes and fill adjacency matrix.
0453   ///
0454   /// Root node is always at zero.
0455   void build() {
0456     // Leave space for the world volume (links to dindex_invalid)
0457     const dindex dim{n_nodes() + 1u};
0458     _adj_matrix.resize(dim * dim);
0459 
0460     for (const auto &n : _nodes) {
0461       // Only works for non batched geometries
0462       for (const auto &edg_link : n.half_edges()) {
0463         // Build an edge
0464         for (const auto edg : _edges(n.index(), edg_link)) {
0465           const dindex elem =
0466               edg.to() < detail::invalid_value<
0467                              typename edge_generator::mask_edge_t>()
0468                   ? dim * n.index() + edg.to()
0469                   : dim * n.index() + dim - 1u;
0470           _adj_matrix[elem]++;
0471         }
0472       }
0473     }
0474   }
0475 
0476   /// Graph nodes
0477   node_generator _nodes;
0478 
0479   /// Graph edges
0480   edge_generator _edges;
0481 
0482   /// Adjacency matrix
0483   vector_t<dindex> _adj_matrix;
0484 };
0485 
0486 }  // namespace detray