File indexing completed on 2026-05-27 07:24:01
0001
0002
0003
0004
0005
0006
0007
0008
0009 #pragma once
0010
0011
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
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
0030
0031 template <typename node_t>
0032 struct void_node_inspector {
0033 bool operator()(const node_t & ) const { return true; }
0034 };
0035
0036
0037 template <typename node_t>
0038 struct void_actor {
0039 void operator()(const node_t & ,
0040 const dindex_range & ) const {
0041
0042 }
0043 };
0044
0045
0046
0047
0048
0049
0050
0051
0052
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
0064
0065
0066
0067
0068
0069 struct node_generator
0070 : public detray::ranges::view_interface<node_generator> {
0071
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
0082
0083
0084 struct node {
0085
0086 explicit node(const tracking_volume<detector_t> &volume)
0087 : m_idx(volume.index()) {
0088
0089 volume.template visit_surfaces<surface_id::e_all, node_builder>(
0090 m_half_edges);
0091 }
0092
0093
0094 dindex index() const { return m_idx; }
0095
0096
0097 const auto &half_edges() const { return m_half_edges; }
0098
0099
0100 dindex m_idx;
0101
0102 vector_t<typename detector_t::surface_type::mask_link> m_half_edges;
0103 };
0104
0105
0106 struct iterator {
0107
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
0118 iterator() = delete;
0119
0120
0121
0122 iterator(volume_iter &&vol_itr, const detector_t &det)
0123 : m_vol_itr(std::move(vol_itr)), m_det(det) {}
0124
0125
0126 bool operator==(const iterator &rhs) const {
0127 return m_vol_itr == rhs.m_vol_itr;
0128 }
0129
0130
0131 node operator*() { return node({m_det, m_vol_itr->index()}); }
0132
0133
0134 iterator &operator++() {
0135 ++m_vol_itr;
0136 return *this;
0137 }
0138
0139
0140 iterator &operator--() {
0141 --m_vol_itr;
0142 return *this;
0143 }
0144
0145
0146 constexpr auto operator+=(const difference_type j) -> iterator & {
0147 m_vol_itr += j;
0148 return *this;
0149 }
0150
0151
0152 constexpr auto operator-=(const difference_type j) -> iterator & {
0153 return *this += -j;
0154 }
0155
0156 protected:
0157
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
0164 friend constexpr auto operator-(const difference_type j,
0165 const iterator &itr) -> iterator {
0166 return itr + -j;
0167 }
0168
0169
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
0177 volume_iter m_vol_itr;
0178
0179 const detector_t &m_det;
0180 };
0181
0182
0183 using iterator_t = iterator;
0184
0185
0186 node_generator() = delete;
0187
0188
0189 node_generator(const volume_container_t &volumes, const detector_t &det)
0190 : m_volumes(volumes), m_det(det) {}
0191
0192
0193 iterator begin() const { return iterator(m_volumes.begin(), m_det); }
0194
0195
0196 iterator end() const { return iterator(m_volumes.end(), m_det); }
0197
0198
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
0206
0207
0208
0209
0210 struct edge_generator
0211 : public detray::ranges::view_interface<edge_generator> {
0212
0213 using mask_edge_t = typename detector_t::surface_type::navigation_link;
0214
0215 using mask_link_t = typename detector_t::surface_type::mask_link;
0216
0217
0218
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
0231 struct edges_builder {
0232
0233
0234
0235
0236
0237
0238
0239
0240
0241
0242
0243
0244
0245
0246
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
0261 edge_generator() = delete;
0262
0263
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
0272 auto begin() { return _edges.begin(); }
0273
0274
0275 auto end() { return _edges.end(); }
0276
0277
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
0294 using node_type = typename node_generator::node;
0295
0296 using edge_type = typename edge_generator::edge;
0297
0298
0299 volume_graph() = delete;
0300
0301
0302
0303
0304
0305
0306
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
0313 ~volume_graph() = default;
0314
0315
0316 dindex n_nodes() const { return static_cast<dindex>(_nodes.size()); }
0317
0318
0319 const auto &nodes() const { return _nodes; }
0320
0321
0322 dindex n_edges(dindex volume_id) const {
0323 const node_type n = _nodes[volume_id];
0324
0325 return static_cast<dindex>(n.half_edges().size());
0326 }
0327
0328
0329 const auto &edges() const { return _edges; }
0330
0331
0332 const auto &adjacency_matrix() const { return _adj_matrix; }
0333
0334
0335
0336
0337
0338
0339
0340
0341
0342
0343
0344
0345
0346
0347
0348
0349
0350
0351
0352
0353
0354
0355
0356
0357
0358
0359
0360
0361
0362
0363
0364
0365
0366
0367
0368
0369
0370
0371
0372
0373
0374
0375
0376
0377
0378
0379
0380
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
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
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
0453
0454
0455 void build() {
0456
0457 const dindex dim{n_nodes() + 1u};
0458 _adj_matrix.resize(dim * dim);
0459
0460 for (const auto &n : _nodes) {
0461
0462 for (const auto &edg_link : n.half_edges()) {
0463
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
0477 node_generator _nodes;
0478
0479
0480 edge_generator _edges;
0481
0482
0483 vector_t<dindex> _adj_matrix;
0484 };
0485
0486 }