File indexing completed on 2026-05-27 07:24:15
0001
0002
0003
0004
0005
0006
0007
0008
0009 #pragma once
0010
0011
0012 #include "detray/utils/logging.hpp"
0013 #include "detray/utils/ranges.hpp"
0014
0015
0016 #include "detray/plugins/svgtools/illustrator.hpp"
0017
0018
0019 #include "detray/test/validation/svg_display.hpp"
0020
0021
0022 #include <algorithm>
0023 #include <functional>
0024 #include <iostream>
0025 #include <iterator>
0026 #include <map>
0027 #include <ranges>
0028 #include <set>
0029 #include <sstream>
0030 #include <string>
0031 #include <tuple>
0032 #include <type_traits>
0033 #include <unordered_set>
0034 #include <utility>
0035 #include <vector>
0036
0037 namespace detray::detector_scanner {
0038
0039
0040
0041
0042
0043
0044
0045 template <typename record_container>
0046 inline dindex_range overlaps_removal(record_container &intersection_records,
0047 const float tol = 1e-4f *
0048 unit<float>::mm) {
0049
0050 constexpr auto inv_idx{detray::detail::invalid_value<dindex>()};
0051 dindex_range overlap_idx{inv_idx, inv_idx};
0052
0053 const std::size_t n_rec{intersection_records.size()};
0054 std::size_t n_eq_intrs{1u};
0055 for (std::size_t i = 0u; i < n_rec - 1u; ++i) {
0056 const auto &rec = intersection_records.at(i);
0057 const auto &next_rec = intersection_records.at(i + 1u);
0058
0059
0060
0061 if (math::fabs(next_rec.intersection.path() - rec.intersection.path()) <
0062 tol &&
0063 i != (n_rec - 2u)) {
0064 ++n_eq_intrs;
0065 continue;
0066 }
0067
0068
0069 if (n_eq_intrs == 1u) {
0070 continue;
0071 }
0072
0073 if (n_eq_intrs == 2u) {
0074
0075 if (const auto &prev_rec = intersection_records.at(i - 1u);
0076 !(rec.intersection.surface().is_portal() &&
0077 prev_rec.intersection.surface().is_portal())) {
0078 auto prev_sf_desc = prev_rec.intersection.surface();
0079 auto sf_desc = rec.intersection.surface();
0080
0081
0082 std::stringstream err_stream;
0083 err_stream << "The following surfaces overlap at\n"
0084 << "POS:\n"
0085 << "glob: " << prev_rec.track_param.pos()
0086 << ", loc: " << prev_rec.intersection.local()
0087 << "\nvs.\nglob: " << rec.track_param.pos()
0088 << ", loc: " << rec.intersection.local() << std::endl;
0089 err_stream << "SURFACES:\n -> " << prev_sf_desc << std::endl;
0090 err_stream << " -> " << sf_desc << std::endl;
0091
0092
0093
0094 DETRAY_ERROR_HOST(err_stream.str());
0095 overlap_idx = {static_cast<dindex>(i - 1u), static_cast<dindex>(i)};
0096 }
0097
0098 n_eq_intrs = 1u;
0099 continue;
0100 }
0101
0102
0103
0104 std::size_t first{(i - (n_eq_intrs - 1u))};
0105 std::size_t last{i};
0106
0107
0108
0109 std::multimap<std::size_t, std::size_t> pt_buckets{};
0110
0111 bool is_all_portals{true};
0112 for (std::size_t j = first; j <= last; ++j) {
0113 const auto &intr = intersection_records.at(j).intersection;
0114 if (!intr.surface().is_portal()) {
0115 is_all_portals = false;
0116 }
0117 pt_buckets.insert({static_cast<std::size_t>(intr.volume_link()), j});
0118 }
0119
0120
0121
0122 auto exit_idx{std::numeric_limits<std::size_t>::max()};
0123 const auto n_erased =
0124 std::erase_if(pt_buckets, [&pt_buckets, &exit_idx](const auto &item) {
0125 const auto &[vol_link, rec_idx] = item;
0126 if (pt_buckets.count(vol_link) == 1u) {
0127 exit_idx = rec_idx;
0128 return true;
0129 }
0130 return false;
0131 });
0132
0133
0134 if (n_erased != 1u || pt_buckets.empty() || !is_all_portals) {
0135 DETRAY_ERROR_HOST("Could not resolve exit portal in overlap correction");
0136
0137 overlap_idx = {static_cast<dindex>(first), static_cast<dindex>(last)};
0138 } else {
0139
0140
0141 const auto &exit_rec = intersection_records.at(exit_idx);
0142 auto itr = intersection_records.cbegin();
0143
0144
0145 for (const auto &[v_link, idx] : pt_buckets | std::views::reverse) {
0146
0147 assert(v_link == exit_rec.vol_idx);
0148
0149 auto test_elem = std::next(itr, static_cast<int>(idx));
0150 if (exit_rec.intersection.volume_link() != test_elem->vol_idx) {
0151 intersection_records.erase(test_elem);
0152 }
0153 }
0154 }
0155
0156
0157 n_eq_intrs = 1u;
0158 }
0159
0160 return overlap_idx;
0161 }
0162
0163
0164
0165
0166
0167
0168
0169
0170
0171
0172
0173
0174
0175
0176
0177
0178
0179
0180
0181
0182
0183
0184
0185
0186
0187
0188 template <dindex invalid_value = dindex_invalid, bool check_sorted_trace = true,
0189 typename entry_type = std::pair<dindex, dindex>>
0190 inline bool check_connectivity(
0191 std::vector<std::pair<entry_type, entry_type>> trace,
0192 dindex start_volume = 0u) {
0193
0194 std::stringstream err_stream;
0195
0196
0197 auto print_err = [](const std::stringstream &stream) {
0198 std::cerr << "\n<<<<<<<<<<<<<<< ERROR in connectivity check\n" << std::endl;
0199 std::cerr << stream.str() << std::endl;
0200 std::cerr << "\n>>>>>>>>>>>>>>>\n" << std::endl;
0201 };
0202
0203
0204 if (trace.empty()) {
0205 err_stream << "Trace empty!";
0206 print_err(err_stream);
0207
0208 return false;
0209 }
0210
0211 std::stringstream record_stream;
0212
0213
0214 dindex current_volume = start_volume;
0215
0216
0217
0218 using vector_t = decltype(trace);
0219 using records_iterator_t = typename vector_t::iterator;
0220 using index_t = std::iter_difference_t<records_iterator_t>;
0221 std::function<records_iterator_t(index_t)> get_connected_record;
0222 if constexpr (check_sorted_trace) {
0223
0224 get_connected_record =
0225 [&trace, ¤t_volume](index_t next) -> records_iterator_t {
0226
0227
0228 if (auto rec = trace.begin() + next;
0229 rec != trace.end() &&
0230 ((std::get<1>(rec->first) == current_volume) ||
0231 (std::get<1>(rec->second) == current_volume))) {
0232 return rec;
0233 }
0234 return trace.end();
0235 };
0236 } else {
0237
0238 get_connected_record =
0239 [&trace, ¤t_volume](index_t ) -> records_iterator_t {
0240 return std::ranges::find_if(
0241 trace, [&](const std::pair<entry_type, entry_type> &rec) -> bool {
0242 return (std::get<1>(rec.first) == current_volume) ||
0243 (std::get<1>(rec.second) == current_volume);
0244 });
0245 };
0246 }
0247
0248
0249 index_t i{0};
0250 auto record = get_connected_record(i);
0251
0252
0253 if (std::get<1>(record->first) != start_volume) {
0254 err_stream << "First record does not start at given initial volume: "
0255 << std::get<1>(record->first) << " vs. " << start_volume;
0256
0257 print_err(err_stream);
0258
0259 return false;
0260 }
0261
0262
0263 while (record != trace.end()) {
0264 auto first_vol = std::get<1>(record->first);
0265 auto second_vol = std::get<1>(record->second);
0266
0267 record_stream << "On volume: " << current_volume << " and record ("
0268 << first_vol << ", " << second_vol << ")";
0269
0270
0271 current_volume = (current_volume == first_vol ? second_vol : first_vol);
0272
0273 record_stream << " -> next volume: " << current_volume << std::endl;
0274
0275
0276
0277 if constexpr (!check_sorted_trace) {
0278 trace.erase(record);
0279 }
0280
0281
0282 record = get_connected_record(++i);
0283 }
0284
0285
0286
0287 if (current_volume != invalid_value) {
0288 err_stream << "Didn't leave world or unconnected elements left in trace:"
0289 << "\n\nValid connections that were found:" << std::endl;
0290 err_stream << record_stream.str();
0291
0292 err_stream << "\nPairs left to match:" << std::endl;
0293 for (auto j = static_cast<std::size_t>(i); j < trace.size(); ++j) {
0294 auto first_vol = std::get<1>(trace[j].first);
0295 auto second_vol = std::get<1>(trace[j].second);
0296
0297 err_stream << "(" << first_vol << ", " << second_vol << ")" << std::endl;
0298 }
0299
0300 print_err(err_stream);
0301
0302 return false;
0303 }
0304
0305 return true;
0306 }
0307
0308
0309
0310
0311
0312
0313
0314
0315
0316
0317
0318
0319
0320
0321
0322
0323
0324
0325
0326
0327
0328 template <dindex invalid_value = dindex_invalid, typename record_container>
0329 inline auto trace_intersections(const record_container &intersection_records,
0330 dindex start_volume = 0u) {
0331
0332 using trace_entry = std::pair<dindex, dindex>;
0333
0334 std::vector<std::pair<trace_entry, trace_entry>> portal_trace = {};
0335
0336 std::vector<trace_entry> module_trace = {};
0337
0338 std::stringstream record_stream;
0339
0340 std::stringstream err_stream;
0341 bool error_code{true};
0342
0343
0344 struct record {
0345 const typename record_container::value_type &entry;
0346
0347
0348
0349 inline bool is_invalid() const {
0350 return entry.intersection.surface().identifier().is_invalid();
0351 }
0352 inline auto surface_idx() const {
0353 return entry.intersection.surface().index();
0354 }
0355 inline auto surface_volume_idx() const {
0356 return entry.intersection.surface().volume();
0357 }
0358 inline auto &inters() const { return entry.intersection; }
0359 inline auto volume_idx() const { return entry.vol_idx; }
0360 inline auto volume_link() const { return entry.intersection.volume_link(); }
0361 inline auto dist() const { return entry.intersection.path(); }
0362 inline bool is_portal() const {
0363 return entry.intersection.surface().is_portal();
0364 }
0365 inline bool is_sensitive() const {
0366 return entry.intersection.surface().is_sensitive();
0367 }
0368 inline bool is_passive() const {
0369 return entry.intersection.surface().is_passive();
0370 }
0371
0372 };
0373
0374
0375 auto print_err = [&error_code](const std::stringstream &stream) {
0376 std::cerr << "\n<<<<<<<<<<<<<<< ERROR intersection trace\n" << std::endl;
0377 std::cerr << stream.str() << std::endl;
0378 std::cerr << "\n>>>>>>>>>>>>>>>\n" << std::endl;
0379 error_code = false;
0380 };
0381
0382
0383 if (intersection_records.empty()) {
0384 err_stream << "No surfaces found in detector!";
0385 print_err(err_stream);
0386
0387 return std::make_tuple(portal_trace, module_trace, error_code);
0388 }
0389
0390
0391 if (intersection_records.size() == 1u) {
0392 const record rec{intersection_records.at(0u)};
0393
0394
0395 if (!rec.is_portal()) {
0396 if (rec.is_invalid()) {
0397 err_stream << "No surfaces found in detector!";
0398 print_err(err_stream);
0399 } else {
0400 const std::string sf_type{rec.is_sensitive() ? "sensitive" : "passive"};
0401
0402 err_stream << "We don't leave the detector by portal!" << std::endl;
0403 err_stream << "Only found single " << sf_type
0404 << " surface: portal(s) missing!";
0405
0406 print_err(err_stream);
0407 }
0408
0409 return std::make_tuple(portal_trace, module_trace, error_code);
0410 }
0411 }
0412
0413
0414 dindex current_vol = start_volume;
0415
0416
0417 const std::size_t start_idx{
0418 record{intersection_records.at(0)}.is_invalid() ? 1u : 0u};
0419 for (std::size_t rec = start_idx; rec < (intersection_records.size() - 1u);) {
0420 const record current_rec = record{intersection_records.at(rec)};
0421 const record next_rec = record{intersection_records.at(rec + 1u)};
0422
0423
0424 record_stream << current_rec.volume_idx() << "\t" << current_rec.inters()
0425 << std::endl;
0426
0427
0428
0429
0430 if (!current_rec.is_portal()) {
0431
0432
0433 const bool is_in_volume =
0434 (current_rec.volume_idx() == current_rec.surface_volume_idx()) &&
0435 (current_rec.surface_volume_idx() == current_vol);
0436 if (is_in_volume) {
0437 module_trace.emplace_back(current_rec.surface_idx(),
0438 current_rec.volume_idx());
0439 } else {
0440 err_stream << "\n(!!) Surface " << current_rec.surface_idx()
0441 << " outside of its volume (Found in: " << current_vol
0442 << ", belongs in: " << current_rec.surface_volume_idx()
0443 << ")\n";
0444
0445 err_stream << record_stream.str();
0446
0447 print_err(err_stream);
0448
0449 return std::make_tuple(portal_trace, module_trace, error_code);
0450 }
0451 ++rec;
0452 continue;
0453 }
0454
0455
0456 else if (current_vol == current_rec.volume_idx()) {
0457 current_vol = current_rec.volume_link();
0458 } else if (current_vol == next_rec.volume_idx()) {
0459 current_vol = next_rec.volume_link();
0460 }
0461
0462 record_stream << next_rec.volume_idx() << "\t" << next_rec.inters()
0463 << std::endl;
0464
0465
0466
0467 const bool is_in_volume =
0468 next_rec.volume_idx() == next_rec.surface_volume_idx();
0469
0470 const bool is_valid =
0471 (current_rec.inters() == next_rec.inters()) &&
0472 (current_rec.volume_idx() == next_rec.volume_link()) &&
0473 (next_rec.volume_idx() == current_rec.volume_link());
0474
0475 const bool is_self_link = current_rec.volume_idx() == next_rec.volume_idx();
0476
0477 const bool is_mixed = (current_rec.is_portal() && !next_rec.is_portal()) ||
0478 (next_rec.is_portal() && !current_rec.is_portal());
0479
0480 if (!is_in_volume) {
0481 record_stream << "\n(!!) Surface outside of its volume (Found: "
0482 << next_rec.volume_idx()
0483 << ", belongs in: " << next_rec.surface_volume_idx() << ")"
0484 << std::endl;
0485 }
0486 if (!is_valid) {
0487 record_stream << "\n(!!) Not a valid portal crossing ("
0488 << current_rec.volume_idx() << " <-> "
0489 << next_rec.volume_idx() << "):\nPortals are not "
0490 << "connected, either geometrically or by linking!"
0491 << std::endl;
0492 }
0493 if (is_self_link) {
0494 record_stream << "\n(!!) Found portal crossing inside volume ("
0495 << current_rec.volume_idx() << ")!" << std::endl;
0496 }
0497 if (is_mixed) {
0498 record_stream << "\n(!!) Portal crossing involves module surface ("
0499 << current_rec.volume_idx() << " <-> "
0500 << next_rec.volume_idx() << ")! The second surface in"
0501 << " this portal crossing is not a portal!" << std::endl;
0502 }
0503 if (is_in_volume && is_valid && !is_self_link && !is_mixed) {
0504
0505 trace_entry lower{current_rec.surface_idx(), current_rec.volume_idx()};
0506 trace_entry upper{next_rec.surface_idx(), next_rec.volume_idx()};
0507 portal_trace.emplace_back(lower, upper);
0508 }
0509
0510 else {
0511
0512 err_stream << "\nError in portal matching:\n" << std::endl;
0513 err_stream << "volume id\t(intersection info)" << std::endl;
0514
0515 err_stream << record_stream.str() << std::endl;
0516
0517 err_stream << "-----\nINFO: Ray terminated at portal x-ing "
0518 << (rec + 1) / 2 << ":\n"
0519 << current_rec.inters() << " <-> " << next_rec.inters()
0520 << std::endl;
0521
0522 const record rec_front{record{intersection_records.front()}.is_invalid()
0523 ? intersection_records[1u]
0524 : intersection_records.front()};
0525 const record rec_back{intersection_records.back()};
0526 err_stream << "Start volume : " << start_volume << std::endl;
0527 err_stream << "- first recorded intersection: (sf id:"
0528 << rec_front.surface_idx() << ", dist:" << rec_front.dist()
0529 << ")," << std::endl;
0530 err_stream << "- last recorded intersection: (sf id:"
0531 << rec_back.surface_idx() << ", dist:" << rec_back.dist()
0532 << "),";
0533
0534 print_err(err_stream);
0535
0536 return std::make_tuple(portal_trace, module_trace, error_code);
0537 }
0538
0539
0540 rec += 2u;
0541 }
0542
0543
0544 if (const record rec_back{intersection_records.back()};
0545 !rec_back.is_portal()) {
0546 err_stream << "We don't leave the detector by portal!";
0547 print_err(err_stream);
0548 } else {
0549 trace_entry lower(rec_back.surface_idx(), rec_back.volume_idx());
0550 trace_entry upper(rec_back.surface_idx(), rec_back.volume_link());
0551 portal_trace.emplace_back(lower, upper);
0552 }
0553
0554 return std::make_tuple(portal_trace, module_trace, error_code);
0555 }
0556
0557
0558
0559
0560
0561
0562
0563
0564
0565
0566
0567
0568 template <dindex invalid_value = dindex_invalid, typename portal_trace_type,
0569 typename module_trace_type,
0570 typename entry_type = std::pair<dindex, dindex>>
0571 requires std::is_same_v<typename portal_trace_type::value_type,
0572 std::pair<entry_type, entry_type>> &&
0573 std::is_same_v<typename module_trace_type::value_type, entry_type>
0574 inline auto build_adjacency(
0575 const portal_trace_type &portal_trace,
0576 const module_trace_type &module_trace,
0577 std::map<dindex, std::map<dindex, dindex>> &adj_list,
0578 std::unordered_set<dindex> &obj_hashes) {
0579
0580 for (const auto &record : module_trace) {
0581 const auto sf_index = std::get<0>(record);
0582 const auto vol_index = std::get<1>(record);
0583
0584 if (obj_hashes.find(sf_index) == obj_hashes.end()) {
0585 adj_list[vol_index][vol_index]++;
0586 obj_hashes.insert(sf_index);
0587 }
0588 }
0589
0590
0591 for (const auto &record : portal_trace) {
0592 const auto pt_index_1 = std::get<0>(record.first);
0593 const auto vol_index_1 = std::get<1>(record.first);
0594 const auto pt_index_2 = std::get<0>(record.second);
0595 const auto vol_index_2 = std::get<1>(record.second);
0596
0597 if (obj_hashes.find(pt_index_1) == obj_hashes.end()) {
0598 adj_list[vol_index_1][vol_index_2]++;
0599 obj_hashes.insert(pt_index_1);
0600 }
0601
0602 if (vol_index_2 != invalid_value &&
0603 obj_hashes.find(pt_index_2) == obj_hashes.end()) {
0604 adj_list[vol_index_2][vol_index_1]++;
0605 obj_hashes.insert(pt_index_2);
0606 }
0607 }
0608
0609 return adj_list;
0610 }
0611
0612
0613
0614
0615
0616
0617
0618
0619
0620
0621
0622
0623 template <dindex invalid_value = dindex_invalid, typename portal_trace_type,
0624 typename module_trace_type,
0625 typename entry_type = std::pair<dindex, dindex>>
0626 requires std::is_same_v<typename portal_trace_type::value_type,
0627 std::pair<entry_type, entry_type>> &&
0628 std::is_same_v<typename module_trace_type::value_type, entry_type>
0629 inline auto build_adjacency(const portal_trace_type &portal_trace,
0630 const module_trace_type &module_trace,
0631 dvector<dindex> &adj_matrix,
0632 std::unordered_set<dindex> &obj_hashes) {
0633 const auto dim = static_cast<dindex>(math::sqrt(adj_matrix.size()));
0634
0635
0636 for (const auto &record : module_trace) {
0637 const auto sf_index = std::get<0>(record);
0638 const auto vol_index = std::get<1>(record);
0639
0640 if (obj_hashes.find(sf_index) == obj_hashes.end()) {
0641 adj_matrix[dim * vol_index + vol_index]++;
0642 obj_hashes.insert(sf_index);
0643 }
0644 }
0645
0646
0647 for (const auto &record : portal_trace) {
0648 const auto pt_index_1 = std::get<0>(record.first);
0649 const auto vol_index_1 = std::get<1>(record.first);
0650 const auto pt_index_2 = std::get<0>(record.second);
0651 const auto vol_index_2 = std::get<1>(record.second);
0652
0653 if (obj_hashes.find(pt_index_1) == obj_hashes.end()) {
0654 dindex mat_elem_vol1{dindex_invalid};
0655
0656
0657 if (vol_index_2 != invalid_value) {
0658 mat_elem_vol1 = dim * vol_index_1 + vol_index_2;
0659
0660 if (obj_hashes.find(pt_index_2) == obj_hashes.end()) {
0661 adj_matrix[dim * vol_index_2 + vol_index_1]++;
0662 obj_hashes.insert(pt_index_2);
0663 }
0664 } else {
0665 mat_elem_vol1 = dim * vol_index_1 + dim - 1;
0666 }
0667 adj_matrix[mat_elem_vol1]++;
0668 obj_hashes.insert(pt_index_1);
0669 }
0670 }
0671
0672 return adj_matrix;
0673 }
0674
0675
0676
0677
0678
0679
0680
0681
0682
0683 template <typename detector_t, typename record_t>
0684 inline bool check_trace(const std::vector<record_t> &intersection_trace,
0685 const dindex start_index, dvector<dindex> &adj_mat_scan,
0686 std::unordered_set<dindex> &obj_hashes) {
0687 using nav_link_t = typename detector_t::surface_type::navigation_link;
0688 static constexpr auto leaving_world{
0689 detray::detail::invalid_value<nav_link_t>()};
0690
0691
0692
0693 auto [portal_trace, surface_trace, err_code] =
0694 detector_scanner::trace_intersections<leaving_world>(intersection_trace,
0695 start_index);
0696
0697
0698 err_code = err_code &&
0699 detector_scanner::check_connectivity<leaving_world>(portal_trace);
0700
0701 if (!adj_mat_scan.empty()) {
0702
0703
0704 detector_scanner::build_adjacency<leaving_world>(
0705 portal_trace, surface_trace, adj_mat_scan, obj_hashes);
0706 }
0707
0708 return err_code;
0709 }
0710
0711
0712
0713
0714
0715
0716
0717
0718
0719
0720
0721
0722 template <typename detector_t, typename trajectory_t, typename truth_trace_t,
0723 typename recorded_trace_t>
0724 inline void display_error(
0725 const typename detector_t::geometry_context gctx, const detector_t &det,
0726 const typename detector_t::name_map &vol_names,
0727 const std::string &test_name, const trajectory_t &test_track,
0728 const truth_trace_t &truth_trace,
0729 const detray::svgtools::styling::style &svg_style,
0730 const std::size_t i_track, [[maybe_unused]] const std::size_t n_tracks,
0731 const recorded_trace_t &recorded_trace = {},
0732 const dindex_range overlap_idx = {detray::detail::invalid_value<dindex>(),
0733 detray::detail::invalid_value<dindex>()},
0734 const bool verbose = true) {
0735
0736 detray::svgtools::illustrator il{det, vol_names, svg_style};
0737 il.show_info(true);
0738 il.hide_eta_lines(true);
0739 il.hide_portals(false);
0740 il.hide_passives(false);
0741
0742 std::string track_type{};
0743 static constexpr auto is_ray{
0744 std::is_same_v<trajectory_t,
0745 detray::detail::ray<typename detector_t::algebra_type>>};
0746 if constexpr (is_ray) {
0747 track_type = "ray";
0748 } else {
0749 track_type = "helix";
0750 }
0751
0752 if (verbose) {
0753 DETRAY_ERROR_HOST("\nFailed on " << track_type << ": " << i_track << "/"
0754 << n_tracks << "\n"
0755 << test_track);
0756 }
0757
0758 detray::detail::svg_display(gctx, il, truth_trace, test_track,
0759 track_type + "_" + std::to_string(i_track),
0760 test_name, recorded_trace, overlap_idx, verbose);
0761 }
0762
0763
0764 template <typename truth_trace_t>
0765 inline std::string print_trace(const truth_trace_t &truth_trace,
0766 std::size_t n) {
0767 std::stringstream out_stream{};
0768 out_stream << "TRACE NO. " << n << std::endl;
0769
0770 for (const auto &[idx, record] : detray::views::enumerate(truth_trace)) {
0771 out_stream << "\nRecord " << idx << std::endl;
0772
0773 out_stream << " -> volume " << record.vol_idx << std::endl;
0774
0775 const auto pos = record.track_param.pos();
0776 const auto dir = record.track_param.dir();
0777 out_stream << " -> track pos: [" << pos[0] << ", " << pos[1] << ", "
0778 << pos[2] << std::endl;
0779 out_stream << " -> track dir: [" << dir[0] << ", " << dir[1] << ", "
0780 << dir[2] << std::endl;
0781
0782 out_stream << " -> intersection " << record.intersection << std::endl;
0783 }
0784
0785 return out_stream.str();
0786 }
0787
0788
0789 inline std::string print_adj(const dvector<dindex> &adjacency_matrix) {
0790 std::size_t dim = static_cast<dindex>(math::sqrt(adjacency_matrix.size()));
0791 std::stringstream out_stream{};
0792
0793 for (std::size_t i = 0u; i < dim - 1; ++i) {
0794 out_stream << "[>>] Node with index " << i << std::endl;
0795 out_stream << " -> edges: " << std::endl;
0796 for (std::size_t j = 0u; j < dim; ++j) {
0797 const auto degr = adjacency_matrix[dim * i + j];
0798 if (degr == 0) {
0799 continue;
0800 }
0801 std::string n_occur =
0802 degr > 1 ? "\t\t\t\t(" + std::to_string(degr) + "x)" : "";
0803
0804
0805 if (j == dim - 1 && degr != 0) {
0806 out_stream << " -> leaving world " + n_occur << std::endl;
0807 } else {
0808 out_stream << " -> " << std::to_string(j) + "\t" + n_occur
0809 << std::endl;
0810 }
0811 }
0812 }
0813
0814 return out_stream.str();
0815 }
0816
0817 }