Back to home page

EIC code displayed by LXR

 
 

    


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

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 // Detray include(s)
0010 #include "detray/builders/grid_builder.hpp"
0011 
0012 #include "detray/builders/surface_factory.hpp"
0013 #include "detray/builders/volume_builder.hpp"
0014 #include "detray/core/detector.hpp"
0015 #include "detray/definitions/indexing.hpp"
0016 #include "detray/geometry/mask.hpp"
0017 #include "detray/geometry/shapes/annulus2D.hpp"
0018 #include "detray/utils/type_list.hpp"
0019 
0020 // Detray test include(s)
0021 #include "detray/test/framework/types.hpp"
0022 
0023 // Vecmem include(s)
0024 #include <vecmem/memory/host_memory_resource.hpp>
0025 
0026 // Gtest include(s)
0027 #include <gtest/gtest.h>
0028 
0029 // System include(s)
0030 #include <limits>
0031 
0032 using namespace detray;
0033 using namespace detray::axis;
0034 
0035 namespace {
0036 
0037 test::transform3 Identity{};
0038 
0039 using detector_t = detector<test::toy_metadata>;
0040 using algebra_t = typename detector_t::algebra_type;
0041 using scalar = dscalar<algebra_t>;
0042 using point3 = dpoint3D<algebra_t>;
0043 using vector3 = dvector3D<algebra_t>;
0044 
0045 /// Mock volume builder for unit testing
0046 struct mock_volume_builder : public volume_builder_interface<detector_t> {
0047   auto vol_index() const -> dindex override { return 0; }
0048   void has_accel(bool /*toggle*/) override { /*Do nothing*/ }
0049   bool has_accel() const override { return false; }
0050   void set_name(std::string /*unused*/) override { /*Do nothing*/ }
0051   std::string_view name() override { return "test_volume_0"; }
0052   auto operator()() const -> const typename detector_t::volume_type& override {
0053     return m_vol;
0054   }
0055   auto operator()() -> typename detector_t::volume_type& override {
0056     return m_vol;
0057   }
0058   auto build(detector_t& /*unused*/,
0059              typename detector_t::geometry_context /*mask*/ = {}) ->
0060       typename detector_t::volume_type* override {
0061     return &m_vol;
0062   }
0063   void add_volume_placement(
0064       const typename detector_t::transform3_type& /*mask*/ = {})
0065       override { /*Do nothing*/ }
0066   void add_volume_placement(const typename detector_t::point3_type& /*unused*/)
0067       override { /*Do nothing*/ }
0068   void add_volume_placement(const typename detector_t::point3_type& /*unused*/,
0069                             const typename detector_t::vector3_type& /*unused*/,
0070                             const typename detector_t::vector3_type& /*unused*/)
0071       override { /*Do nothing*/ }
0072   void add_surfaces(std::shared_ptr<surface_factory_interface<detector_t>>,
0073                     typename detector_t::geometry_context /*gctx*/ = {})
0074       override { /*Do nothing*/ }
0075 
0076  protected:
0077   typename detector_t::surface_lookup_container& surfaces() override {
0078     return m_sf_lookup;
0079   }
0080   typename detector_t::transform_container& transforms() override {
0081     return m_transforms;
0082   }
0083   typename detector_t::mask_container& masks() override { return m_masks; }
0084 
0085  private:
0086   detector_t::volume_type m_vol{};
0087   detector_t::surface_lookup_container m_sf_lookup{};
0088   detector_t::transform_container m_transforms{};
0089   detector_t::mask_container m_masks{};
0090 };
0091 
0092 }  // anonymous namespace
0093 
0094 /// Unittest: Test the construction of a collection of grids
0095 GTEST_TEST(detray_builders, grid_factory_static) {
0096   // Data-owning grid collection
0097   vecmem::host_memory_resource host_mr;
0098   auto gr_factory =
0099       grid_factory<bins::static_array<dindex, 3>, simple_serializer, algebra_t>{
0100           host_mr};
0101 
0102   // Build from existing mask of a surface
0103   const scalar minR{0.f};
0104   const scalar maxR{10.f};
0105   const scalar minPhi{0.f};
0106   const scalar maxPhi{constant<scalar>::pi};
0107   mask<annulus2D, algebra_t> ann2{0u,     minR, maxR, minPhi,
0108                                   maxPhi, 0.f,  0.f,  0.f};
0109 
0110   // Grid with correctly initialized axes, but empty bin content
0111   auto ann_gr = gr_factory.new_grid(ann2, {5, 10});
0112 
0113   // Test axis
0114   const auto& ann_axis_r = ann_gr.template get_axis<label::e_r>();
0115   EXPECT_EQ(ann_axis_r.label(), label::e_r);
0116   EXPECT_EQ(ann_axis_r.bounds(), bounds::e_closed);
0117   EXPECT_EQ(ann_axis_r.binning(), binning::e_regular);
0118   EXPECT_EQ(ann_axis_r.nbins(), 5u);
0119   EXPECT_NEAR(ann_axis_r.span()[0], 0.f,
0120               std::numeric_limits<scalar>::epsilon());
0121   EXPECT_NEAR(ann_axis_r.span()[1], 10.f,
0122               std::numeric_limits<scalar>::epsilon());
0123 
0124   // Test fill a bin to see, if bin content was correctly initialized
0125   point3 p = {0.5f, 2.f, 0.f};
0126   vector3 d{};
0127   auto loc_p = ann_gr.project(Identity, p, d);
0128   ann_gr.template populate<attach<>>(loc_p, 3u);
0129   ann_gr.template populate<attach<>>(loc_p, 5u);
0130   auto bin2 = ann_gr.bin(loc_p);
0131 
0132   EXPECT_TRUE(bin2.size() == 2u);
0133   EXPECT_FALSE(bin2.empty());
0134   EXPECT_EQ(bin2[0], 3u);
0135   EXPECT_EQ(bin2[1], 5u);
0136 
0137   // Build from parameters
0138   const std::vector<scalar> bin_edges_z{-10.f, -8.f, -6.5f, -1.f,
0139                                         4.f,   5.f,  6.f,   9.f};
0140   const std::vector<scalar> bin_edges_phi{};
0141 
0142   auto cyl_gr = gr_factory.template new_grid<cylinder2D>(
0143       std::vector<scalar>{0.f, 2.f * constant<scalar>::pi, bin_edges_z.front(),
0144                           bin_edges_z.back()},
0145       {10u, bin_edges_z.size() - 1}, {}, {bin_edges_phi, bin_edges_z},
0146       types::list<circular<label::e_rphi>, closed<label::e_cyl_z>>{},
0147       types::list<regular<scalar>, irregular<scalar>>{});
0148 
0149   // Test axis
0150   const auto& cyl_axis_rphi = cyl_gr.template get_axis<label::e_rphi>();
0151   EXPECT_EQ(cyl_axis_rphi.label(), label::e_rphi);
0152   EXPECT_EQ(cyl_axis_rphi.bounds(), bounds::e_circular);
0153   EXPECT_EQ(cyl_axis_rphi.binning(), binning::e_regular);
0154   EXPECT_EQ(cyl_axis_rphi.nbins(), 10u);
0155   EXPECT_NEAR(cyl_axis_rphi.span()[0], 0.f,
0156               std::numeric_limits<scalar>::epsilon());
0157   EXPECT_NEAR(cyl_axis_rphi.span()[1], 2.f * constant<scalar>::pi,
0158               std::numeric_limits<scalar>::epsilon());
0159 
0160   const auto& cyl_axis_z = cyl_gr.template get_axis<label::e_cyl_z>();
0161   EXPECT_EQ(cyl_axis_z.label(), label::e_cyl_z);
0162   EXPECT_EQ(cyl_axis_z.bounds(), bounds::e_closed);
0163   EXPECT_EQ(cyl_axis_z.binning(), binning::e_irregular);
0164   EXPECT_EQ(cyl_axis_z.nbins(), 7u);
0165   EXPECT_NEAR(cyl_axis_z.span()[0], -10.f,
0166               std::numeric_limits<scalar>::epsilon());
0167   EXPECT_NEAR(cyl_axis_z.span()[1], 9.f,
0168               std::numeric_limits<scalar>::epsilon());
0169 
0170   // Test fill a bin to see, if bin content was correctly initialized
0171   loc_p = cyl_gr.project(Identity, p, d);
0172   cyl_gr.template populate<attach<>>(loc_p, 33u);
0173   cyl_gr.template populate<attach<>>(loc_p, 55u);
0174 
0175   EXPECT_EQ(cyl_gr.bin(loc_p)[0], 33u);
0176   EXPECT_EQ(cyl_gr.bin(loc_p)[1], 55u);
0177 
0178   // Build the same cylinder grid from a mask
0179   const scalar r{5.f};
0180   const scalar n_half_z{-10.f};
0181   const scalar p_half_z{9.f};
0182   mask<cylinder2D, algebra_t> cyl2{0u, r, n_half_z, p_half_z};
0183 
0184   auto cyl_gr2 =
0185       gr_factory
0186           .template new_grid<circular<label::e_rphi>, closed<label::e_cyl_z>,
0187                              regular<scalar>, irregular<scalar>>(
0188               cyl2, {bin_edges_z.size() - 1, 10u}, {},
0189               {bin_edges_phi, bin_edges_z});
0190 }
0191 
0192 /// Unittest: Test the construction of a collection of grids
0193 GTEST_TEST(detray_builders, grid_factory_dynamic) {
0194   // Data-owning grid collection
0195   vecmem::host_memory_resource host_mr;
0196   auto gr_factory =
0197       grid_factory<bins::dynamic_array<dindex>, simple_serializer, algebra_t>{
0198           host_mr};
0199 
0200   // Build from existing mask of a surface
0201   const scalar minR{0.f};
0202   const scalar maxR{10.f};
0203   const scalar minPhi{0.f};
0204   const scalar maxPhi{constant<scalar>::pi};
0205   mask<annulus2D, algebra_t> ann2{0u,     minR, maxR, minPhi,
0206                                   maxPhi, 0.f,  0.f,  0.f};
0207 
0208   // Grid with correctly initialized axes and bins, but empty bin content
0209   using ann_grid_t =
0210       typename decltype(gr_factory)::template grid_type<annulus2D>;
0211   std::vector<std::pair<typename ann_grid_t::loc_bin_index, dindex>> capacities;
0212 
0213   auto bin_indexer2D = detray::views::cartesian_product{
0214       detray::views::iota{0u, 2u}, detray::views::iota{0u, 3u}};
0215   dindex capacity{0u};
0216   for (const auto [bin_idx0, bin_idx1] : bin_indexer2D) {
0217     typename ann_grid_t::loc_bin_index mbin{bin_idx0, bin_idx1};
0218     capacities.emplace_back(mbin, ++capacity);
0219   }
0220 
0221   ann_grid_t ann_gr = gr_factory.new_grid(ann2, {2, 3}, capacities);
0222 
0223   // Test axis
0224   const auto& ann_axis_r = ann_gr.template get_axis<label::e_r>();
0225   EXPECT_EQ(ann_axis_r.label(), label::e_r);
0226   EXPECT_EQ(ann_axis_r.bounds(), bounds::e_closed);
0227   EXPECT_EQ(ann_axis_r.binning(), binning::e_regular);
0228   EXPECT_EQ(ann_axis_r.nbins(), 2u);
0229   EXPECT_NEAR(ann_axis_r.span()[0], 0.f,
0230               std::numeric_limits<scalar>::epsilon());
0231   EXPECT_NEAR(ann_axis_r.span()[1], 10.f,
0232               std::numeric_limits<scalar>::epsilon());
0233 
0234   // Test bin storage
0235   EXPECT_EQ(ann_gr.bins().size(), 6u);
0236   for (const auto& bin : ann_gr.bins()) {
0237     EXPECT_EQ(bin.size(), 0u);
0238     EXPECT_TRUE(bin.capacity() >= 1u);
0239   }
0240   EXPECT_EQ(ann_gr.size(), 0u);
0241 
0242   EXPECT_EQ(ann_gr.bins().entry_data().size(), 21u);
0243   EXPECT_EQ(ann_gr.bins().entry_data().capacity(), 21u);
0244 
0245   // Test fill a bin to see, if bin content was correctly initialized
0246   point3 p = {0.5f, 2.f, 0.f};
0247   vector3 d{};
0248   auto loc_p = ann_gr.project(Identity, p, d);
0249   auto bin2 = ann_gr.bin(loc_p);
0250   EXPECT_TRUE(bin2.capacity() == 2u);
0251   EXPECT_TRUE(bin2.empty());
0252   EXPECT_TRUE(bin2.size() == 0u);
0253   ann_gr.template populate<attach<>>(loc_p, 3u);
0254   ann_gr.template populate<attach<>>(loc_p, 5u);
0255 
0256   EXPECT_TRUE(bin2.capacity() == 2u);
0257   EXPECT_TRUE(bin2.size() == 2u);
0258   EXPECT_FALSE(bin2.empty());
0259   EXPECT_EQ(bin2[0], 3u);
0260   EXPECT_EQ(bin2[1], 5u);
0261 
0262   EXPECT_EQ(ann_gr.size(), 2u);
0263 
0264   // Build from parameters
0265   const std::vector<scalar> bin_edges_z{-10.f, -8.f, -6.5f, -1.f,
0266                                         4.f,   5.f,  6.f,   9.f};
0267   const std::vector<scalar> bin_edges_phi{};
0268 
0269   bin_indexer2D = detray::views::cartesian_product{detray::views::iota{0u, 10u},
0270                                                    detray::views::iota{0u, 7u}};
0271   capacity = 0u;
0272   capacities.clear();
0273   for (const auto [bin_idx0, bin_idx1] : bin_indexer2D) {
0274     typename ann_grid_t::loc_bin_index mbin{bin_idx0, bin_idx1};
0275     capacities.emplace_back(mbin, ++capacity);
0276   }
0277 
0278   auto cyl_gr = gr_factory.template new_grid<concentric_cylinder2D>(
0279       std::vector<scalar>{0.f, 2.f * constant<scalar>::pi, bin_edges_z.front(),
0280                           bin_edges_z.back()},
0281       {10u, bin_edges_z.size() - 1}, capacities, {bin_edges_phi, bin_edges_z},
0282       types::list<circular<label::e_rphi>, closed<label::e_cyl_z>>{},
0283       types::list<regular<scalar>, irregular<scalar>>{});
0284 
0285   // Test axis
0286   const auto& cyl_axis_rphi = cyl_gr.template get_axis<label::e_rphi>();
0287   EXPECT_EQ(cyl_axis_rphi.label(), label::e_rphi);
0288   EXPECT_EQ(cyl_axis_rphi.bounds(), bounds::e_circular);
0289   EXPECT_EQ(cyl_axis_rphi.binning(), binning::e_regular);
0290   EXPECT_EQ(cyl_axis_rphi.nbins(), 10u);
0291   EXPECT_NEAR(cyl_axis_rphi.span()[0], 0.f,
0292               std::numeric_limits<scalar>::epsilon());
0293   EXPECT_NEAR(cyl_axis_rphi.span()[1], 2.f * constant<scalar>::pi,
0294               std::numeric_limits<scalar>::epsilon());
0295 
0296   const auto& cyl_axis_z = cyl_gr.template get_axis<label::e_cyl_z>();
0297   EXPECT_EQ(cyl_axis_z.label(), label::e_cyl_z);
0298   EXPECT_EQ(cyl_axis_z.bounds(), bounds::e_closed);
0299   EXPECT_EQ(cyl_axis_z.binning(), binning::e_irregular);
0300   EXPECT_EQ(cyl_axis_z.nbins(), 7u);
0301   EXPECT_NEAR(cyl_axis_z.span()[0], -10.f,
0302               std::numeric_limits<scalar>::epsilon());
0303   EXPECT_NEAR(cyl_axis_z.span()[1], 9.f,
0304               std::numeric_limits<scalar>::epsilon());
0305 
0306   // Test bin storage
0307   EXPECT_EQ(cyl_gr.bins().size(), 70u);
0308   for (const auto& bin : cyl_gr.bins()) {
0309     EXPECT_EQ(bin.size(), 0u);
0310     EXPECT_TRUE(bin.capacity() >= 1u);
0311   }
0312   EXPECT_EQ(cyl_gr.size(), 0u);
0313 
0314   EXPECT_EQ(cyl_gr.bins().entry_data().size(), 2485u);
0315   EXPECT_EQ(cyl_gr.bins().entry_data().capacity(), 2485u);
0316 
0317   // Test fill a bin to see, if bin content was correctly initialized
0318   loc_p = cyl_gr.project(Identity, p, d);
0319   auto bin3 = cyl_gr.bin(loc_p);
0320 
0321   EXPECT_TRUE(bin3.capacity() == 18u);
0322   EXPECT_TRUE(bin3.empty());
0323   EXPECT_TRUE(bin3.size() == 0u);
0324 
0325   cyl_gr.template populate<attach<>>(loc_p, 33u);
0326   cyl_gr.template populate<attach<>>(loc_p, 55u);
0327   cyl_gr.template populate<attach<>>(loc_p, 77u);
0328 
0329   EXPECT_TRUE(bin3.capacity() == 18u);
0330   EXPECT_FALSE(bin3.empty());
0331   EXPECT_TRUE(bin3.size() == 3u);
0332   EXPECT_EQ(bin3[0], 33u);
0333   EXPECT_EQ(bin3[1], 55u);
0334   EXPECT_EQ(bin3[2], 77u);
0335 
0336   EXPECT_EQ(cyl_gr.size(), 3u);
0337 
0338   // Build the same cylinder grid from a mask
0339   const scalar r{5.f};
0340   const scalar n_half_z{-10.f};
0341   const scalar p_half_z{9.f};
0342   mask<cylinder2D, algebra_t> cyl2{0u, r, n_half_z, p_half_z};
0343 
0344   auto cyl_gr2 =
0345       gr_factory
0346           .template new_grid<circular<label::e_rphi>, closed<label::e_cyl_z>,
0347                              regular<scalar>, irregular<scalar>>(
0348               cyl2, {10u, bin_edges_z.size() - 1}, capacities,
0349               {bin_edges_phi, bin_edges_z});
0350 }
0351 
0352 /// Unittest: Test the grid builder
0353 GTEST_TEST(detray_builders, grid_builder) {
0354   // cylinder grid type of the toy detector
0355   using cyl_grid_t = grid<algebra_t, axes<concentric_cylinder2D>,
0356                           bins::static_array<detector_t::surface_type, 1>,
0357                           simple_serializer, host_container_types, false>;
0358 
0359   auto gbuilder = grid_builder<detector_t, cyl_grid_t, detray::bin_associator>{
0360       std::make_unique<mock_volume_builder>()};
0361 
0362   // The cylinder portals are at the end of the surface range by construction
0363   const auto cyl_mask =
0364       mask<concentric_cylinder2D, algebra_t>{0u, 10.f, -500.f, 500.f};
0365   std::size_t n_phi_bins{5u};
0366   std::size_t n_z_bins{4u};
0367 
0368   // Build empty grid
0369   gbuilder.init_grid(cyl_mask, {n_phi_bins, n_z_bins});
0370   auto cyl_grid = gbuilder.get();
0371 
0372   const auto& cyl_axis_z = cyl_grid.template get_axis<label::e_cyl_z>();
0373   EXPECT_EQ(cyl_axis_z.label(), label::e_cyl_z);
0374   EXPECT_EQ(cyl_axis_z.bounds(), bounds::e_closed);
0375   EXPECT_EQ(cyl_axis_z.binning(), binning::e_regular);
0376   EXPECT_EQ(cyl_axis_z.nbins(), 4u);
0377   EXPECT_NEAR(cyl_axis_z.span()[0], -500.f,
0378               std::numeric_limits<scalar>::epsilon());
0379   EXPECT_NEAR(cyl_axis_z.span()[1], 500.f,
0380               std::numeric_limits<scalar>::epsilon());
0381 }