Back to home page

EIC code displayed by LXR

 
 

    


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

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/navigation/volume_graph.hpp"
0013 #include "detray/tracks/ray.hpp"
0014 #include "detray/utils/logging.hpp"
0015 
0016 // Detray IO include(s)
0017 #include "detray/io/utils/create_path.hpp"
0018 
0019 // Detray test include(s)
0020 #include "detray/test/common/track_generators.hpp"
0021 #include "detray/test/framework/fixture_base.hpp"
0022 #include "detray/test/framework/types.hpp"
0023 #include "detray/test/framework/whiteboard.hpp"
0024 #include "detray/test/validation/detector_scan_config.hpp"
0025 #include "detray/test/validation/detector_scan_utils.hpp"
0026 #include "detray/test/validation/detector_scanner.hpp"
0027 
0028 // System include(s)
0029 #include <iostream>
0030 #include <memory>
0031 #include <string>
0032 
0033 namespace detray::test {
0034 
0035 /// @brief Test class that runs the ray/helix scan on a given detector.
0036 ///
0037 /// @note The lifetime of the detector needs to be guaranteed.
0038 template <typename detector_t, template <typename> class scan_type>
0039 class detector_scan : public test::fixture_base<> {
0040   using algebra_t = typename detector_t::algebra_type;
0041   using scalar_t = dscalar<algebra_t>;
0042   using free_track_parameters_t = free_track_parameters<algebra_t>;
0043   using intersection_trace_t = typename scan_type<
0044       algebra_t>::template intersection_trace_type<detector_t>;
0045   using trajectory_type = typename scan_type<algebra_t>::trajectory_type;
0046   using uniform_gen_t =
0047       detail::random_numbers<scalar_t,
0048                              std::uniform_real_distribution<scalar_t>>;
0049   using track_generator_t =
0050       random_track_generator<free_track_parameters_t, uniform_gen_t>;
0051 
0052   /// Switch between rays and helices
0053   static constexpr auto k_use_rays{
0054       std::is_same_v<detail::ray<algebra_t>, trajectory_type>};
0055 
0056  public:
0057   using fixture_type = test::fixture_base<>;
0058   using config = detector_scan_config<track_generator_t, algebra_t>;
0059 
0060   explicit detector_scan(const detector_t &det,
0061                          const typename detector_t::name_map &names,
0062                          const config &cfg = {},
0063                          std::shared_ptr<test::whiteboard> wb = nullptr,
0064                          const typename detector_t::geometry_context gctx = {})
0065       : m_cfg{cfg},
0066         m_gctx{gctx},
0067         m_det{det},
0068         m_names{names},
0069         m_whiteboard{std::move(wb)} {
0070     if (!m_whiteboard) {
0071       throw std::invalid_argument("No white board was passed to " +
0072                                   m_cfg.name());
0073     }
0074   }
0075 
0076   /// Run the detector scan
0077   void TestBody() override {
0078     // Get the volume adjaceny matrix from the detector
0079     volume_graph graph(m_det);
0080     const auto &adj_mat = graph.adjacency_matrix();
0081 
0082     // Fill adjacency matrix from ray scan and compare
0083     dvector<dindex> adj_mat_scan(adj_mat.size(), 0);
0084     // Keep track of the objects that have already been seen per volume
0085     std::unordered_set<dindex> obj_hashes = {};
0086 
0087     // Index of the volume that the test trajectory origin lies in
0088     dindex start_index{0u};
0089 
0090     DETRAY_INFO_HOST("Running scan on: " << m_det.name(m_names) << "\n");
0091 
0092     // Fill detector scan data to white board
0093     const std::size_t n_helices = fill_scan_data();
0094 
0095     auto &detector_scan_traces =
0096         m_whiteboard->template get<std::vector<intersection_trace_t>>(
0097             m_cfg.name());
0098 
0099     const std::string det_name{m_det.name(m_names)};
0100     const std::string prefix{k_use_rays ? det_name + "_ray"
0101                                         : det_name + "_helix"};
0102 
0103     const auto data_path{
0104         std::filesystem::path{m_cfg.track_param_file()}.parent_path()};
0105 
0106     // Make sure the output directories exit
0107     io::create_path(data_path);
0108 
0109     std::ios_base::openmode io_mode = std::ios::trunc | std::ios::out;
0110     detray::io::file_handle debug_file{
0111         data_path / (prefix + "_detector_scan.txt"), io_mode};
0112 
0113     DETRAY_INFO_HOST("Checking trace data...\n");
0114 
0115     // Iterate through the scan data and perform checks
0116     std::size_t n_tracks{0u};
0117     for (int i = static_cast<int>(detector_scan_traces.size()) - 1; i >= 0;
0118          --i) {
0119       const auto j{static_cast<std::size_t>(i)};
0120 
0121       auto &intersection_trace = detector_scan_traces[j];
0122       assert((intersection_trace.size() > 0) && "Invalid intersection trace");
0123 
0124       // Retrieve the test trajectory
0125       const auto &trck_param = intersection_trace.front().track_param;
0126       trajectory_type test_traj = get_parametrized_trajectory(trck_param);
0127 
0128       // Run overlaps check on the trace and remove certain
0129       // allowed duplication (oversized portals from ACTS)
0130       if (m_cfg.overlaps_removal()) {
0131         const dindex_range overlap_idx = detector_scanner::overlaps_removal(
0132             intersection_trace, m_cfg.overlaps_tol());
0133 
0134         // Drop an svg of the trajectory where the overlap was found
0135         if (overlap_idx[1] - overlap_idx[0] != 0u) {
0136           constexpr bool verbose{false};
0137           detector_scanner::display_error(
0138               m_gctx, m_det, m_names, "OVERLAP_" + m_cfg.name(), test_traj,
0139               intersection_trace, m_cfg.svg_style(), n_tracks, n_helices,
0140               intersection_trace_t{}, overlap_idx, verbose);
0141         }
0142       }
0143 
0144       // Run consistency checks on the trace
0145       bool success = detector_scanner::check_trace<detector_t>(
0146           intersection_trace, start_index, adj_mat_scan, obj_hashes);
0147 
0148       // Display the detector, track and intersections for debugging
0149       if (!success) {
0150         detector_scanner::display_error(
0151             m_gctx, m_det, m_names, m_cfg.name(), test_traj, intersection_trace,
0152             m_cfg.svg_style(), n_tracks, n_helices, intersection_trace_t{});
0153       }
0154 
0155       EXPECT_TRUE(success);
0156 
0157       // Remove faulty trace for the following steps
0158       if (!success) {
0159         *debug_file << detector_scanner::print_trace(intersection_trace, j);
0160 
0161         DETRAY_ERROR_HOST("Skipped faulty trace no. " << j);
0162         detector_scan_traces.erase(detector_scan_traces.begin() + i);
0163       } else {
0164         ++n_tracks;
0165       }
0166     }
0167     std::clog << "------------------------------------\n"
0168               << "Tested " << n_tracks << " tracks: OK\n"
0169               << "------------------------------------\n"
0170               << std::endl;
0171 
0172     // Check that the links that were discovered by the scan match the
0173     // volume graph
0174     // ASSERT_TRUE(adj_mat == adj_mat_scan) <<
0175     // detector_scanner::print_adj(adj_mat_scan);
0176 
0177     // Compare the adjacency that was discovered in the ray scan to the
0178     // hashed one for the toy detector.
0179     // The hash tree is still Work in Progress !
0180     /*auto geo_checker = hash_tree(adj_mat);
0181     const bool check_links = (geo_checker.root() == root_hash);
0182 
0183     std::clog << "All links reachable: " << (check_links ? "OK" : "FAILURE")
0184             << std::endl;*/
0185   }
0186 
0187  private:
0188   /// Get the truth data, either from file or by generating it
0189   /// @returns the number of helices
0190   std::size_t fill_scan_data() {
0191     /// Record the scan for later use
0192     std::vector<intersection_trace_t> intersection_traces;
0193 
0194     auto trk_state_generator = track_generator_t(m_cfg.track_generator());
0195     const std::size_t n_helices{trk_state_generator.size()};
0196     intersection_traces.reserve(n_helices);
0197 
0198     std::string momentum_str{""};
0199     if constexpr (!k_use_rays) {
0200       const auto pT_range = m_cfg.track_generator().mom_range();
0201       // Remove floating point imprecisions
0202       momentum_str =
0203           "_" +
0204           std::to_string(std::floor(10. * static_cast<double>(pT_range[0])) /
0205                          10.) +
0206           "_" +
0207           std::to_string(std::ceil(10. * static_cast<double>(pT_range[1])) /
0208                          10.) +
0209           "_GeV";
0210     }
0211 
0212     std::string track_param_file_name{m_cfg.track_param_file() + momentum_str +
0213                                       ".csv"};
0214 
0215     std::string intersection_file_name{m_cfg.intersection_file() +
0216                                        momentum_str + ".csv"};
0217 
0218     const bool data_files_exist{io::file_exists(intersection_file_name) &&
0219                                 io::file_exists(track_param_file_name)};
0220 
0221     if (data_files_exist) {
0222       DETRAY_INFO_HOST("Reading data from file...");
0223 
0224       // Fill the intersection traces from file
0225       detector_scanner::read(intersection_file_name, track_param_file_name,
0226                              intersection_traces);
0227     } else {
0228       DETRAY_INFO_HOST("Generating trace data...");
0229 
0230       for (auto trk : trk_state_generator) {
0231         // Get ground truth from track
0232         trajectory_type test_traj = get_parametrized_trajectory(trk);
0233 
0234         // The track generator can randomize the sign of the charge
0235         const scalar_t qabs{math::fabs(m_cfg.m_trk_gen_cfg.charge())};
0236         const scalar_t q{math::copysign(qabs, trk.qop())};
0237 
0238         // Shoot trajectory through the detector and record all
0239         // surfaces it encounters
0240         // @note: For rays, set the momentum to 1 GeV to keep the
0241         //        direction vector normalized
0242         const scalar p{q == 0.f ? 1.f * unit<scalar>::GeV : trk.p(q)};
0243         auto trace = detector_scanner::run<scan_type>(
0244             m_gctx, m_det, test_traj, m_cfg.mask_tolerance(), p);
0245 
0246         intersection_traces.push_back(std::move(trace));
0247       }
0248     }
0249 
0250     // Save the results
0251 
0252     // Csv output
0253     if (!data_files_exist && m_cfg.write_intersections()) {
0254       detector_scanner::write_tracks(track_param_file_name,
0255                                      intersection_traces);
0256       detector_scanner::write_intersections(intersection_file_name,
0257                                             intersection_traces);
0258 
0259       DETRAY_INFO_HOST("  ->Wrote  " << intersection_traces.size()
0260                                      << " intersection traces to file");
0261     }
0262 
0263     DETRAY_INFO_HOST("  ->Adding " << intersection_traces.size()
0264                                    << " intersection traces to whiteboard");
0265 
0266     // Move the data to the whiteboard
0267     m_whiteboard->add(m_cfg.name(), std::move(intersection_traces));
0268 
0269     return n_helices;
0270   }
0271 
0272   /// @returns either the helix or ray corresponding to the input track
0273   /// parameters @param track
0274   trajectory_type get_parametrized_trajectory(
0275       const free_track_parameters_t &track) {
0276     std::unique_ptr<trajectory_type> test_traj{nullptr};
0277     if constexpr (k_use_rays) {
0278       test_traj = std::make_unique<trajectory_type>(track);
0279     } else {
0280       test_traj = std::make_unique<trajectory_type>(track, m_cfg.B_vector());
0281     }
0282     return *(test_traj.release());
0283   }
0284 
0285   /// The configuration of this test
0286   config m_cfg;
0287   /// The geometry context to scan
0288   typename detector_t::geometry_context m_gctx{};
0289   /// The detector to be checked
0290   const detector_t &m_det;
0291   /// Volume names
0292   const typename detector_t::name_map &m_names;
0293   /// Whiteboard to pin data
0294   std::shared_ptr<test::whiteboard> m_whiteboard{nullptr};
0295 };
0296 
0297 template <typename detector_t>
0298 using ray_scan = detector_scan<detector_t, detray::ray_scan>;
0299 
0300 template <typename detector_t>
0301 using helix_scan = detector_scan<detector_t, detray::helix_scan>;
0302 
0303 }  // namespace detray::test