File indexing completed on 2026-05-27 07:24:22
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #include "detray/navigation/detail/intersection_kernel.hpp"
0011
0012 #include "detray/core/detail/multi_store.hpp"
0013 #include "detray/core/detail/single_store.hpp"
0014 #include "detray/geometry/mask.hpp"
0015 #include "detray/geometry/shapes.hpp"
0016 #include "detray/geometry/surface_descriptor.hpp"
0017 #include "detray/navigation/intersection/helix_intersector.hpp"
0018 #include "detray/navigation/intersection/ray_intersector.hpp"
0019 #include "detray/tracks/tracks.hpp"
0020 #include "detray/tracks/trajectories.hpp"
0021 #include "detray/utils/ranges.hpp"
0022
0023
0024 #include "detray/test/framework/types.hpp"
0025
0026
0027 #include <vecmem/memory/host_memory_resource.hpp>
0028
0029
0030 #include <gtest/gtest.h>
0031
0032 using namespace detray;
0033
0034 using test_algebra = test::algebra;
0035 using scalar = test::scalar;
0036 using vector3 = test::vector3;
0037 using point3 = test::point3;
0038 using transform_link_t = dindex;
0039
0040 namespace {
0041
0042 constexpr const scalar is_close{1e-5f};
0043 constexpr const scalar external_tol{1.f * unit<scalar>::mm};
0044
0045 const intersection::config intr_cfg{
0046 .min_mask_tolerance = 1e-3f * unit<float>::mm,
0047 .max_mask_tolerance = 1e-3f * unit<float>::mm,
0048 .mask_tolerance_scalor = 0.f,
0049 .overstep_tolerance = 0.f};
0050
0051 enum class mask_id : unsigned int {
0052 e_rectangle2D = 0u,
0053 e_trapezoid2D = 1u,
0054 e_annulus2D = 2u,
0055 e_cylinder2D = 3u,
0056 e_concentric_cylinder2D = 4u,
0057 };
0058
0059 enum class material_id : unsigned int {
0060 e_material_slab = 0u,
0061 };
0062
0063 }
0064
0065
0066 using volume_link_t = std::uint_least16_t;
0067
0068
0069 using rectangle_t = mask<rectangle2D, test_algebra, volume_link_t>;
0070 using trapezoid_t = mask<trapezoid2D, test_algebra, volume_link_t>;
0071 using annulus_t = mask<annulus2D, test_algebra, volume_link_t>;
0072 using cylinder_t = mask<cylinder2D, test_algebra, volume_link_t>;
0073 using cylinder_portal_t =
0074 mask<concentric_cylinder2D, test_algebra, volume_link_t>;
0075
0076 using mask_container_t =
0077 regular_multi_store<mask_id, empty_context, dtuple, dvector, rectangle_t,
0078 trapezoid_t, annulus_t, cylinder_t, cylinder_portal_t>;
0079 using mask_link_t = typename mask_container_t::single_link;
0080 using material_link_t = dtyped_index<material_id, dindex>;
0081
0082 using transform_container_t =
0083 single_store<test::transform3, dvector, geometry_context>;
0084
0085
0086 using surface_t =
0087 surface_descriptor<mask_link_t, material_link_t, transform_link_t>;
0088 using surface_container_t = dvector<surface_t>;
0089
0090
0091
0092
0093
0094 GTEST_TEST(detray_intersection, intersection_kernel_ray) {
0095 vecmem::host_memory_resource host_mr;
0096
0097
0098 typename transform_container_t::context_type static_context{};
0099 transform_container_t transform_store;
0100
0101 transform_store.emplace_back(static_context, vector3{0.f, 0.f, 10.f});
0102 transform_store.emplace_back(static_context, vector3{0.f, 0.f, 20.f});
0103 transform_store.emplace_back(static_context, vector3{0.f, -20.f, 30.f});
0104
0105 transform_store.emplace_back(static_context, vector3{0.f, 0.f, 50.f},
0106 vector3{1.f, 0.f, 0.f}, vector3{0.f, 0.f, -1.f});
0107
0108 transform_store.emplace_back(static_context, vector3{0.f, 0.f, 0.f});
0109
0110 transform_store.emplace_back(static_context, vector3{12.f, 0.f, 1000.f});
0111
0112
0113 mask_container_t mask_store(host_mr);
0114
0115 const rectangle_t rect{0u, 10.f, 10.f};
0116 const trapezoid_t trap{0u, 10.f, 20.f, 30.f, 1.f / 60.f};
0117 const annulus_t annl{0u, 15.f, 55.f, 0.75f, 1.95f, 0.f, 2.f, -2.f};
0118 const cylinder_t cyl{0u, 5.f, -10.f, 10.f};
0119 const cylinder_portal_t cyl_portal{0u, 1.f, 0.f, 1000.f};
0120
0121 mask_store.template push_back<mask_id::e_rectangle2D>(rect, empty_context{});
0122 mask_store.template push_back<mask_id::e_rectangle2D>(rect, empty_context{});
0123 mask_store.template push_back<mask_id::e_trapezoid2D>(trap, empty_context{});
0124 mask_store.template push_back<mask_id::e_annulus2D>(annl, empty_context{});
0125 mask_store.template push_back<mask_id::e_cylinder2D>(cyl, empty_context{});
0126 mask_store.template push_back<mask_id::e_concentric_cylinder2D>(
0127 cyl_portal, empty_context{});
0128
0129
0130 surface_t rectangle_surface(0u, {mask_id::e_rectangle2D, 0u},
0131 {material_id::e_material_slab, 0u}, 0u,
0132 surface_id::e_sensitive);
0133 surface_t trapezoid_surface(1u, {mask_id::e_trapezoid2D, 0u},
0134 {material_id::e_material_slab, 1u}, 0u,
0135 surface_id::e_sensitive);
0136 surface_t annulus_surface(2u, {mask_id::e_annulus2D, 0u},
0137 {material_id::e_material_slab, 2u}, 0u,
0138 surface_id::e_sensitive);
0139 surface_t cyl_surface(3u, {mask_id::e_cylinder2D, 0u},
0140 {material_id::e_material_slab, 2u}, 0u,
0141 surface_id::e_passive);
0142 surface_t cyl_portal_surface(4u, {mask_id::e_concentric_cylinder2D, 0u},
0143 {material_id::e_material_slab, 2u}, 0u,
0144 surface_id::e_portal);
0145 surface_t rectangle_shifted_surface(5u, {mask_id::e_rectangle2D, 1u},
0146 {material_id::e_material_slab, 0u}, 0u,
0147 surface_id::e_sensitive);
0148
0149 rectangle_surface.set_index(0u);
0150 trapezoid_surface.set_index(1u);
0151 annulus_surface.set_index(2u);
0152 cyl_surface.set_index(3u);
0153 cyl_portal_surface.set_index(4u);
0154 rectangle_shifted_surface.set_index(5u);
0155
0156 surface_container_t surfaces = {
0157 rectangle_surface, trapezoid_surface, annulus_surface,
0158 cyl_surface, cyl_portal_surface, rectangle_shifted_surface};
0159
0160 const point3 pos{0.f, 0.f, 0.f};
0161 const vector3 mom{0.01f, 0.01f, 10.f};
0162 const free_track_parameters<test_algebra> track(pos, 0.f, mom, -1.f);
0163
0164
0165 const point3 expected_rectangle{0.01f, 0.01f, 10.f};
0166 const point3 expected_trapezoid{0.02f, 0.02f, 20.f};
0167 const point3 expected_annulus{0.03f, 0.03f, 30.f};
0168 const point3 expected_cylinder1{0.045f, 0.045f, 45.0f};
0169 const point3 expected_cylinder2{0.055f, 0.055f, 55.0f};
0170 const point3 expected_cylinder_pt{0.7071068f, 0.7071068f, 707.1068f};
0171 const point3 expected_rectangle_shifted{1.f, 1.f, 1000.f};
0172
0173 const std::vector<point3> expected_points = {
0174 expected_rectangle, expected_trapezoid, expected_annulus,
0175 expected_cylinder1, expected_cylinder2, expected_cylinder_pt,
0176 expected_rectangle_shifted};
0177
0178
0179 std::vector<
0180 intersection2D<surface_t, test_algebra, intersection::contains_pos>>
0181 sfi_init;
0182 sfi_init.reserve(expected_points.size());
0183
0184 for (const auto &surface : surfaces) {
0185 mask_store.visit<detail::intersection_initialize<ray_intersector>>(
0186 surface.mask(), sfi_init, detail::ray(track), surface, transform_store,
0187 static_context, intr_cfg, external_tol);
0188 }
0189
0190 EXPECT_EQ(expected_points.size(), sfi_init.size());
0191
0192
0193 for (std::size_t i = 0u;
0194 i < math::min(expected_points.size(), sfi_init.size()); ++i) {
0195 EXPECT_TRUE(sfi_init[i].is_along());
0196 EXPECT_EQ(sfi_init[i].volume_link(), 0u);
0197
0198 vector3 global{0.f, 0.f, 0.f};
0199
0200 const surface_t sf_desc = sfi_init[i].surface();
0201 if (sf_desc.mask().id() == mask_id::e_rectangle2D) {
0202 global = rect.to_global_frame(transform_store.at(sf_desc.transform()),
0203 sfi_init[i].local());
0204 } else if (sf_desc.mask().id() == mask_id::e_trapezoid2D) {
0205 global = trap.to_global_frame(transform_store.at(sf_desc.transform()),
0206 sfi_init[i].local());
0207 } else if (sf_desc.mask().id() == mask_id::e_annulus2D) {
0208 global = annl.to_global_frame(transform_store.at(sf_desc.transform()),
0209 sfi_init[i].local());
0210 } else if (sf_desc.mask().id() == mask_id::e_cylinder2D) {
0211 global = cyl.to_global_frame(transform_store.at(sf_desc.transform()),
0212 sfi_init[i].local());
0213 } else if (sf_desc.mask().id() == mask_id::e_concentric_cylinder2D) {
0214 global = cyl_portal.to_global_frame(
0215 transform_store.at(sf_desc.transform()), sfi_init[i].local());
0216 }
0217
0218 EXPECT_NEAR(global[0], expected_points[i][0], 1e-3f)
0219 << " at point " << i << ": surface " << sf_desc.identifier();
0220 EXPECT_NEAR(global[1], expected_points[i][1], 1e-3f)
0221 << " at point " << i << ": surface " << sf_desc.identifier();
0222 EXPECT_NEAR(global[2], expected_points[i][2], 1e-3f)
0223 << " at point " << i << ": surface " << sf_desc.identifier();
0224 }
0225
0226
0227
0228
0229
0230
0231
0232
0233
0234
0235
0236
0237
0238
0239
0240
0241
0242
0243
0244
0245
0246
0247
0248
0249
0250
0251
0252
0253
0254
0255
0256
0257
0258
0259
0260
0261 }
0262
0263
0264 GTEST_TEST(detray_intersection, intersection_kernel_helix) {
0265 vecmem::host_memory_resource host_mr;
0266
0267
0268 typename transform_container_t::context_type static_context{};
0269 transform_container_t transform_store;
0270
0271 transform_store.emplace_back(static_context, vector3{0.f, 0.f, 10.f});
0272 transform_store.emplace_back(static_context, vector3{0.f, 0.f, 20.f});
0273 transform_store.emplace_back(static_context, vector3{0.f, -20.f, 30.f});
0274
0275 mask_container_t mask_store(host_mr);
0276
0277 const rectangle_t rect{0u, 10.f, 10.f};
0278 const trapezoid_t trap{0u, 10.f, 20.f, 30.f, 1.f / 60.f};
0279 const annulus_t annl{0u, 15.f, 55.f, 0.75f, 1.95f, 0.f, 2.f, -2.f};
0280 mask_store.template push_back<mask_id::e_rectangle2D>(rect, empty_context{});
0281 mask_store.template push_back<mask_id::e_trapezoid2D>(trap, empty_context{});
0282 mask_store.template push_back<mask_id::e_annulus2D>(annl, empty_context{});
0283
0284
0285 const surface_t rectangle_surface(0u, {mask_id::e_rectangle2D, 0u},
0286 {material_id::e_material_slab, 0u}, 0u,
0287 surface_id::e_sensitive);
0288 const surface_t trapezoid_surface(1u, {mask_id::e_trapezoid2D, 0u},
0289 {material_id::e_material_slab, 1u}, 0u,
0290 surface_id::e_sensitive);
0291 const surface_t annulus_surface(2u, {mask_id::e_annulus2D, 0u},
0292 {material_id::e_material_slab, 2u}, 0u,
0293 surface_id::e_sensitive);
0294 surface_container_t surfaces = {rectangle_surface, trapezoid_surface,
0295 annulus_surface};
0296 const point3 pos{0.f, 0.f, 0.f};
0297 const vector3 mom{0.01f, 0.01f, 10.f};
0298 const vector3 B{0.f * unit<scalar>::T, 0.f * unit<scalar>::T,
0299 is_close * unit<scalar>::T};
0300 const detail::helix<test_algebra> h({pos, 0.f, mom, -1.f}, B);
0301
0302
0303 const point3 expected_rectangle{0.01f, 0.01f, 10.f};
0304 const point3 expected_trapezoid{0.02f, 0.02f, 20.f};
0305 const point3 expected_annulus{0.03f, 0.03f, 30.f};
0306 const std::vector<point3> expected_points = {
0307 expected_rectangle, expected_trapezoid, expected_annulus};
0308 std::vector<
0309 intersection2D<surface_t, test_algebra, intersection::contains_pos>>
0310 sfi_helix{};
0311 sfi_helix.reserve(expected_points.size());
0312
0313
0314 for (const auto [sf_idx, surface] : detray::views::enumerate(surfaces)) {
0315 mask_store.visit<detail::intersection_initialize<helix_intersector>>(
0316 surface.mask(), sfi_helix, h, surface, transform_store, static_context,
0317 intr_cfg, scalar{0.f});
0318
0319 vector3 global{0.f, 0.f, 0.f};
0320
0321 if (surface.mask().id() == mask_id::e_rectangle2D) {
0322 global =
0323 rect.to_global_frame(transform_store.at(0), sfi_helix[0].local());
0324 } else if (surface.mask().id() == mask_id::e_trapezoid2D) {
0325 global =
0326 trap.to_global_frame(transform_store.at(1), sfi_helix[0].local());
0327 } else if (surface.mask().id() == mask_id::e_annulus2D) {
0328 global =
0329 annl.to_global_frame(transform_store.at(2), sfi_helix[0].local());
0330 }
0331
0332 ASSERT_NEAR(global[0], expected_points[sf_idx][0], is_close);
0333 ASSERT_NEAR(global[1], expected_points[sf_idx][1], is_close);
0334 ASSERT_NEAR(global[2], expected_points[sf_idx][2], is_close);
0335
0336 sfi_helix.clear();
0337 }
0338 }