Back to home page

EIC code displayed by LXR

 
 

    


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

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 // Project include(s)
0010 #include "detray/builders/volume_builder.hpp"
0011 
0012 #include "detray/builders/cuboid_portal_generator.hpp"
0013 #include "detray/builders/surface_factory.hpp"
0014 #include "detray/core/detector.hpp"
0015 #include "detray/definitions/indexing.hpp"
0016 #include "detray/geometry/mask.hpp"
0017 #include "detray/geometry/shapes.hpp"
0018 
0019 // Detray test include(s)
0020 #include "detray/test/framework/types.hpp"
0021 #include "detray/test/utils/prefill_detector.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 namespace {
0030 
0031 using scalar = detray::test::scalar;
0032 using point3 = detray::test::point3;
0033 
0034 /// Check volume links for a collection of masks in a given detector
0035 template <typename detector_t,
0036           typename detector_t::surface_type::mask_link::id_type mask_id>
0037 inline void check_mask(const detector_t& d,
0038                        const std::vector<detray::dindex>& vol_links) {
0039   for (const auto [idx, mask] :
0040        detray::views::enumerate(d.mask_store().template get<mask_id>())) {
0041     EXPECT_EQ(mask.volume_link(), vol_links.at(idx))
0042         << "mask no. " << idx << ": " << mask.to_string();
0043   }
0044 }
0045 
0046 }  // anonymous namespace
0047 
0048 /// Integration test to build a cylinder volume with contained surfaces
0049 GTEST_TEST(detray_builders, tracking_volume_construction) {
0050   using namespace detray;
0051 
0052   using metadata_t = test::default_metadata;
0053   using detector_t = detector<metadata_t>;
0054   using transform3 = typename detector_t::transform3_type;
0055   using geo_obj_id = typename detector_t::geo_obj_ids;
0056   using mask_id = typename detector_t::masks::id;
0057   using accel_id = typename detector_t::accel::id;
0058 
0059   // Surface factories
0060   using portal_cylinder_factory =
0061       surface_factory<detector_t, concentric_cylinder2D>;
0062   using annulus_factory = surface_factory<detector_t, annulus2D>;
0063   using cylinder_factory = surface_factory<detector_t, cylinder2D>;
0064   using rectangle_factory = surface_factory<detector_t, rectangle2D>;
0065   using disc_factory = surface_factory<detector_t, ring2D>;
0066   using trapezoid_factory = surface_factory<detector_t, trapezoid2D>;
0067 
0068   // detector
0069   vecmem::host_memory_resource host_mr;
0070   detector_t d(host_mr);
0071   auto geo_ctx = typename detector_t::geometry_context{};
0072   // ensure there is a data offset that needs to be handled correctly
0073   prefill_detector(d, geo_ctx);
0074   const dindex first_trf{d.transform_store().size()};
0075   const auto vol_idx{
0076       static_cast<typename detector_t::surface_type::navigation_link>(
0077           d.volumes().size())};
0078 
0079   // initial checks
0080   EXPECT_EQ(d.volumes().size(), 1u);
0081   EXPECT_EQ(d.portals().size(), 3u);
0082 
0083   // volume builder
0084   volume_builder<detector_t> vbuilder{volume_id::e_cylinder};
0085   typename detector_t::point3_type t{0.f, 0.f, 20.f};
0086   vbuilder.add_volume_placement(t);
0087 
0088   //
0089   // Fill the surface factories with data
0090   //
0091 
0092   // portal surfaces
0093   auto pt_cyl_factory = std::make_shared<portal_cylinder_factory>();
0094   typename portal_cylinder_factory::sf_data_collection cyl_sf_data;
0095   // Creates two cylinders at radius 0mm and 10mm with an extent in z
0096   // of -5mm to 5mm and linking to volumes 0 and 2, respectively.
0097   cyl_sf_data.emplace_back(surface_id::e_portal,
0098                            transform3(point3{0.f, 0.f, 0.f}), 0u,
0099                            std::vector<scalar>{10.f, -1500.f, 1500.f});
0100   cyl_sf_data.emplace_back(surface_id::e_portal,
0101                            transform3(point3{0.f, 0.f, 0.f}), 2u,
0102                            std::vector<scalar>{20.f, -1500.f, 1500.f});
0103   pt_cyl_factory->push_back(std::move(cyl_sf_data));
0104 
0105   auto pt_disc_factory = std::make_shared<disc_factory>();
0106   typename disc_factory::sf_data_collection disc_sf_data;
0107   // Creates two discs with a radius of 10mm, linking to volumes 3 and 4
0108   disc_sf_data.emplace_back(surface_id::e_portal,
0109                             transform3(point3{0.f, 0.f, -1500.f}), 3u,
0110                             std::vector<scalar>{0.f, 10.f});
0111   disc_sf_data.emplace_back(surface_id::e_portal,
0112                             transform3(point3{0.f, 0.f, 1500.f}), 4u,
0113                             std::vector<scalar>{0.f, 10.f});
0114   pt_disc_factory->push_back(std::move(disc_sf_data));
0115 
0116   // sensitive surfaces
0117   auto ann_factory = std::make_shared<annulus_factory>();
0118   typename annulus_factory::sf_data_collection ann_sf_data;
0119   ann_sf_data.emplace_back(
0120       surface_id::e_sensitive, transform3(point3{0.f, 0.f, 1000.f}), vol_idx,
0121       std::vector<scalar>{300.f, 350.f, -0.1f, 0.1f, 0.5f, 0.6f, 1.4f});
0122   ann_sf_data.emplace_back(
0123       surface_id::e_sensitive, transform3(point3{0.f, 0.f, 1000.f}), vol_idx,
0124       std::vector<scalar>{350.f, 400.f, -0.1f, 0.1f, 0.5f, 0.6f, 1.4f});
0125   ann_factory->push_back(std::move(ann_sf_data));
0126 
0127   auto rect_factory = std::make_shared<rectangle_factory>();
0128   typename rectangle_factory::sf_data_collection rect_sf_data;
0129   rect_sf_data.emplace_back(surface_id::e_sensitive,
0130                             transform3(point3{0.f, 0.f, -10.f}), vol_idx,
0131                             std::vector<scalar>{10.f, 8.f});
0132   rect_sf_data.emplace_back(surface_id::e_sensitive,
0133                             transform3(point3{0.f, 0.f, -20.f}), vol_idx,
0134                             std::vector<scalar>{10.f, 8.f});
0135   rect_sf_data.emplace_back(surface_id::e_sensitive,
0136                             transform3(point3{0.f, 0.f, -30.f}), vol_idx,
0137                             std::vector<scalar>{10.f, 8.f});
0138   rect_factory->push_back(std::move(rect_sf_data));
0139 
0140   auto trpz_factory = std::make_shared<trapezoid_factory>();
0141   typename trapezoid_factory::sf_data_collection trpz_sf_data;
0142   trpz_sf_data.emplace_back(surface_id::e_sensitive,
0143                             transform3(point3{0.f, 0.f, 1000.f}), vol_idx,
0144                             std::vector<scalar>{1.f, 3.f, 2.f, 0.25f});
0145   trpz_factory->push_back(std::move(trpz_sf_data));
0146 
0147   // passive surfaces
0148   auto cyl_factory = std::make_shared<cylinder_factory>();
0149   cyl_sf_data.clear();
0150   cyl_sf_data.emplace_back(surface_id::e_passive,
0151                            transform3(point3{0.f, 0.f, 0.f}), vol_idx,
0152                            std::vector<scalar>{5.f, -1300.f, 1300.f});
0153   cyl_factory->push_back(std::move(cyl_sf_data));
0154 
0155   auto sf_disc_factory = std::make_shared<disc_factory>();
0156   disc_sf_data.clear();
0157   disc_sf_data.emplace_back(surface_id::e_passive,
0158                             transform3(point3{0.f, 0.f, -1300.f}), vol_idx,
0159                             std::vector<scalar>{0.f, 5.f});
0160   disc_sf_data.emplace_back(surface_id::e_passive,
0161                             transform3(point3{0.f, 0.f, 1300.f}), vol_idx,
0162                             std::vector<scalar>{0.f, 5.f});
0163   sf_disc_factory->push_back(std::move(disc_sf_data));
0164 
0165   //
0166   // Fill everything into volume
0167   //
0168   vbuilder.add_surfaces(pt_cyl_factory, geo_ctx);
0169   vbuilder.add_surfaces(pt_disc_factory, geo_ctx);
0170 
0171   vbuilder.add_surfaces(ann_factory, geo_ctx);
0172   vbuilder.add_surfaces(rect_factory, geo_ctx);
0173   vbuilder.add_surfaces(trpz_factory, geo_ctx);
0174 
0175   vbuilder.add_surfaces(cyl_factory, geo_ctx);
0176   vbuilder.add_surfaces(sf_disc_factory, geo_ctx);
0177 
0178   // try adding something extra later...
0179   rect_factory->clear();
0180   rect_sf_data.clear();
0181   rect_sf_data.emplace_back(surface_id::e_sensitive,
0182                             transform3(point3{0.f, 0.f, 10.f}), vol_idx,
0183                             std::vector<scalar>{10.f, 8.f});
0184   rect_sf_data.emplace_back(surface_id::e_sensitive,
0185                             transform3(point3{0.f, 0.f, 20.f}), vol_idx,
0186                             std::vector<scalar>{10.f, 8.f});
0187   rect_factory->push_back(std::move(rect_sf_data));
0188   rect_sf_data.clear();
0189   rect_sf_data.emplace_back(surface_id::e_sensitive,
0190                             transform3(point3{0.f, 0.f, 30.f}), vol_idx,
0191                             std::vector<scalar>{10.f, 8.f});
0192   rect_factory->push_back(std::move(rect_sf_data));
0193 
0194   vbuilder.add_surfaces(rect_factory, geo_ctx);
0195 
0196   //
0197   // Adds all surfaces to the detector
0198   //
0199   vbuilder.build(d);
0200 
0201   //
0202   // check results
0203   //
0204   const auto& vol = d.volumes().back();
0205 
0206   EXPECT_EQ(d.volumes().size(), 2u);
0207   EXPECT_EQ(vol.index(), 1u);
0208   EXPECT_EQ(vol.id(), volume_id::e_cylinder);
0209 
0210   // Check the volume placement
0211   typename detector_t::transform3_type trf{t};
0212   EXPECT_TRUE(d.transform_store().at(first_trf) == trf);
0213 
0214   // Check the acceleration data structure link
0215   dtyped_index<accel_id, dindex> acc_link{accel_id::e_surface_default, 1u};
0216   ASSERT_TRUE(vol.accel_link().size() == geo_obj_id::e_size);
0217   EXPECT_EQ(vol.accel_link<geo_obj_id::e_portal>(), acc_link);
0218   EXPECT_EQ(vol.accel_link<geo_obj_id::e_passive>(), acc_link);
0219   // Not set by the vanilla volume builder
0220   EXPECT_TRUE(
0221       detail::is_invalid_value(vol.accel_link<geo_obj_id::e_sensitive>()));
0222 
0223   EXPECT_EQ(d.portals().size(), 19u);
0224   EXPECT_EQ(d.mask_store().template size<mask_id::e_concentric_cylinder2D>(),
0225             2u);
0226   EXPECT_EQ(d.mask_store().template size<mask_id::e_ring2D>(), 4u);
0227   EXPECT_EQ(d.mask_store().template size<mask_id::e_annulus2D>(), 3u);
0228   EXPECT_EQ(d.mask_store().template size<mask_id::e_cylinder2D>(), 1u);
0229   EXPECT_EQ(d.mask_store().template size<mask_id::e_rectangle2D>(), 7u);
0230   EXPECT_EQ(d.mask_store().template size<mask_id::e_ring2D>(), 4u);
0231   EXPECT_EQ(d.mask_store().template size<mask_id::e_trapezoid2D>(), 2u);
0232 
0233   // check surface type and volume link
0234   std::vector<surface_id> sf_ids{};
0235   sf_ids.reserve(d.surfaces().size());
0236   sf_ids.insert(sf_ids.end(), 3u, surface_id::e_sensitive);
0237   sf_ids.insert(sf_ids.end(), 4u, surface_id::e_portal);
0238   sf_ids.insert(sf_ids.end(), 6u, surface_id::e_sensitive);
0239   sf_ids.insert(sf_ids.end(), 3u, surface_id::e_passive);
0240   sf_ids.insert(sf_ids.end(), 3u, surface_id::e_sensitive);
0241 
0242   std::vector<dindex> volume_links{};
0243   volume_links.reserve(d.surfaces().size());
0244   volume_links.insert(volume_links.end(), 3u, 0u);
0245   volume_links.insert(volume_links.end(), 16u, 1u);
0246 
0247   // Check surface id and volume links
0248   for (const auto [idx, sf_id] : detray::views::enumerate(sf_ids)) {
0249     geometry::identifier geo_id{};
0250     geo_id.set_index(idx);
0251     const auto& sf = d.surface(geo_id);
0252     EXPECT_EQ(sf.id(), sf_id) << "error at index: " << idx;
0253     EXPECT_EQ(sf.volume(), volume_links.at(idx)) << "error at index: " << idx;
0254   }
0255 
0256   // check that the transform indices are continuous for the newly added
0257   // surfaces. The first new transform belongs to the volume itself
0258   for (std::size_t idx :
0259        detray::views::iota(dindex_range{3, d.surfaces().size()})) {
0260     geometry::identifier geo_id{};
0261     geo_id.set_index(idx);
0262     // Add a shift to the index for the volume placement transforms
0263     EXPECT_EQ(d.surface(geo_id).transform(), idx + 2)
0264         << "error at index: " << idx;
0265   }
0266 
0267   // check surface mask links
0268   std::vector<typename detector_t::surface_type::mask_link> mask_links{
0269       {mask_id::e_rectangle2D, {0u, 1u}},
0270       {mask_id::e_annulus2D, {0u, 1u}},
0271       {mask_id::e_trapezoid2D, {0u, 1u}},
0272       {mask_id::e_concentric_cylinder2D, {0u, 1u}},
0273       {mask_id::e_concentric_cylinder2D, {1u, 1u}},
0274       {mask_id::e_ring2D, {0u, 1u}},
0275       {mask_id::e_ring2D, {1u, 1u}},
0276       {mask_id::e_annulus2D, {1u, 1u}},
0277       {mask_id::e_annulus2D, {2u, 1u}},
0278       {mask_id::e_rectangle2D, {1u, 1u}},
0279       {mask_id::e_rectangle2D, {2u, 1u}},
0280       {mask_id::e_rectangle2D, {3u, 1u}},
0281       {mask_id::e_trapezoid2D, {1u, 1u}},
0282       {mask_id::e_cylinder2D, {0u, 1u}},
0283       {mask_id::e_ring2D, {2u, 1u}},
0284       {mask_id::e_ring2D, {3u, 1u}},
0285       {mask_id::e_rectangle2D, {4u, 1u}},
0286       {mask_id::e_rectangle2D, {5u, 1u}},
0287       {mask_id::e_rectangle2D, {6u, 1u}}};
0288   for (const auto [idx, m_link] : detray::views::enumerate(mask_links)) {
0289     geometry::identifier geo_id{};
0290     geo_id.set_index(idx);
0291     EXPECT_EQ(d.surface(geo_id).mask(), m_link) << "error at index: " << idx;
0292   }
0293 
0294   // check mask volume links
0295   volume_links.clear();
0296   volume_links = {0u, 2u};
0297   check_mask<detector_t, mask_id::e_concentric_cylinder2D>(d, volume_links);
0298 
0299   volume_links.clear();
0300   volume_links = {1u};
0301   check_mask<detector_t, mask_id::e_cylinder2D>(d, volume_links);
0302 
0303   volume_links.clear();
0304   volume_links = {3u, 4u, 1u, 1u};
0305   check_mask<detector_t, mask_id::e_ring2D>(d, volume_links);
0306   check_mask<detector_t, mask_id::e_ring2D>(d, volume_links);
0307 
0308   volume_links.clear();
0309   volume_links = {0u, 1u, 1u};
0310   check_mask<detector_t, mask_id::e_annulus2D>(d, volume_links);
0311 
0312   volume_links.clear();
0313   volume_links.reserve(7u);
0314   volume_links.push_back(0u);
0315   volume_links.insert(volume_links.end(), 6u, 1u);
0316   check_mask<detector_t, mask_id::e_rectangle2D>(d, volume_links);
0317 
0318   volume_links.clear();
0319   volume_links = {0u, 1u};
0320   check_mask<detector_t, mask_id::e_trapezoid2D>(d, volume_links);
0321 }