File indexing completed on 2026-05-27 07:24:16
0001
0002
0003
0004
0005
0006
0007
0008
0009
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
0020 #include "detray/test/framework/types.hpp"
0021 #include "detray/test/utils/prefill_detector.hpp"
0022
0023
0024 #include <vecmem/memory/host_memory_resource.hpp>
0025
0026
0027 #include <gtest/gtest.h>
0028
0029 namespace {
0030
0031 using scalar = detray::test::scalar;
0032 using point3 = detray::test::point3;
0033
0034
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 }
0047
0048
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
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
0069 vecmem::host_memory_resource host_mr;
0070 detector_t d(host_mr);
0071 auto geo_ctx = typename detector_t::geometry_context{};
0072
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
0080 EXPECT_EQ(d.volumes().size(), 1u);
0081 EXPECT_EQ(d.portals().size(), 3u);
0082
0083
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
0090
0091
0092
0093 auto pt_cyl_factory = std::make_shared<portal_cylinder_factory>();
0094 typename portal_cylinder_factory::sf_data_collection cyl_sf_data;
0095
0096
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
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
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
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
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
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
0198
0199 vbuilder.build(d);
0200
0201
0202
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
0211 typename detector_t::transform3_type trf{t};
0212 EXPECT_TRUE(d.transform_store().at(first_trf) == trf);
0213
0214
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
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
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
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
0257
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
0263 EXPECT_EQ(d.surface(geo_id).transform(), idx + 2)
0264 << "error at index: " << idx;
0265 }
0266
0267
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
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 }