File indexing completed on 2026-05-27 07:24:23
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #include "detray/utils/grid/grid.hpp"
0011
0012 #include "detray/definitions/indexing.hpp"
0013 #include "detray/geometry/shapes/cuboid3D.hpp"
0014 #include "detray/utils/grid/concepts.hpp"
0015
0016
0017 #include "detray/test/framework/types.hpp"
0018
0019
0020 #include <vecmem/containers/device_vector.hpp>
0021 #include <vecmem/containers/vector.hpp>
0022 #include <vecmem/memory/host_memory_resource.hpp>
0023
0024
0025 #include <gtest/gtest.h>
0026
0027
0028 #include <algorithm>
0029 #include <limits>
0030 #include <random>
0031
0032 using namespace detray;
0033 using namespace detray::axis;
0034
0035 namespace {
0036
0037
0038 using test_algebra = test::algebra;
0039 using scalar = test::scalar;
0040 using point3 = test::point3;
0041
0042 constexpr scalar inf{std::numeric_limits<scalar>::max()};
0043 constexpr scalar tol{1e-7f};
0044
0045
0046 template <bool ownership = true, typename containers = host_container_types>
0047 using cartesian_3D =
0048 coordinate_axes<axes<cuboid3D>, test_algebra, ownership, containers>;
0049
0050
0051 bool constexpr is_owning = true;
0052 bool constexpr is_n_owning = false;
0053
0054
0055 dvector<scalar> bin_edges = {-10.f, 10.f, -20.f, 20.f, 0.f, 100.f};
0056
0057 dvector<dsized_index_range> edge_ranges = {{0u, 20u}, {2u, 40u}, {4u, 50u}};
0058
0059
0060 cartesian_3D<is_n_owning, host_container_types> ax_n_own(edge_ranges,
0061 bin_edges);
0062
0063
0064 template <typename populator_t, typename bin_t>
0065 struct bin_content_sequence {
0066 using entry_t = typename bin_t::entry_type;
0067 entry_t entry{0};
0068
0069 auto operator()() {
0070 entry += entry_t{1};
0071 bin_t bin{};
0072 populator_t{}(bin, entry);
0073 return bin;
0074 }
0075 };
0076
0077
0078 template <concepts::grid grid_t, typename content_t>
0079 void test_content(const grid_t& g, const point3& p, const content_t& expected) {
0080 dindex i = 0u;
0081 for (const auto& entry : g.bin(p)) {
0082 ASSERT_NEAR(entry, expected[i++], tol) << " at index " << i - 1u;
0083 }
0084 }
0085
0086 }
0087
0088
0089 GTEST_TEST(detray_grid, single_grid) {
0090
0091 using grid_owning_t =
0092 grid<test_algebra, axes<cuboid3D>, bins::single<scalar>>;
0093
0094 using grid_n_owning_t =
0095 grid<test_algebra, axes<cuboid3D>, bins::single<scalar>,
0096 simple_serializer, host_container_types, false>;
0097
0098 using grid_device_t = grid<test_algebra, axes<cuboid3D>, bins::single<scalar>,
0099 simple_serializer, device_container_types>;
0100
0101 static_assert(concepts::grid<grid_owning_t>);
0102 static_assert(concepts::grid<grid_n_owning_t>);
0103 static_assert(concepts::grid<grid_device_t>);
0104
0105
0106
0107 grid_owning_t::bin_container_type bin_data{};
0108 bin_data.resize(40'000u);
0109 std::ranges::generate_n(
0110 bin_data.begin(), 40'000u,
0111 bin_content_sequence<replace<>, bins::single<scalar>>());
0112
0113
0114 dvector<scalar> bin_edges_cp(bin_edges);
0115 dvector<dsized_index_range> edge_ranges_cp(edge_ranges);
0116 grid_owning_t::bin_container_type bin_data_cp(bin_data);
0117
0118
0119 cartesian_3D<is_owning, host_container_types> axes_own(
0120 std::move(edge_ranges_cp), std::move(bin_edges_cp));
0121 grid_owning_t grid_own(std::move(bin_data_cp), std::move(axes_own));
0122
0123
0124 dvector<scalar> bin_edges_cp2(bin_edges);
0125 dvector<dsized_index_range> edge_ranges_cp2(edge_ranges);
0126 grid_owning_t::bin_container_type bin_data_cp2(bin_data);
0127
0128
0129 cartesian_3D<is_owning, host_container_types> axes_own2(
0130 std::move(edge_ranges_cp2), std::move(bin_edges_cp2));
0131 grid_owning_t grid_own2(std::move(bin_data_cp2), std::move(axes_own2));
0132
0133
0134 EXPECT_TRUE(grid_own == grid_own2);
0135
0136
0137 EXPECT_EQ(grid_own.dim, 3u);
0138 EXPECT_EQ(grid_own.nbins(), 40'000u);
0139 auto y_axis = grid_own.get_axis<label::e_y>();
0140 EXPECT_EQ(y_axis.nbins(), 40u);
0141 auto z_axis =
0142 grid_own.get_axis<single_axis<closed<label::e_z>, regular<scalar>>>();
0143 EXPECT_EQ(z_axis.nbins(), 50u);
0144
0145 // Create non-owning grid
0146 grid_n_owning_t grid_n_own(&bin_data, ax_n_own);
0147
0148 // Test for consistency with owning grid
0149 EXPECT_EQ(grid_n_own.dim, grid_own.dim);
0150 y_axis = grid_n_own.get_axis<label::e_y>();
0151 EXPECT_EQ(y_axis.nbins(), grid_own.get_axis<label::e_y>().nbins());
0152 z_axis = grid_n_own.get_axis<label::e_z>();
0153 EXPECT_EQ(z_axis.nbins(), grid_own.get_axis<label::e_z>().nbins());
0154
0155 // Construct a grid from a view
0156 grid_owning_t::view_type grid_view = get_data(grid_own);
0157 grid_device_t device_grid(grid_view);
0158
0159 // Test for consistency with non-owning grid
0160 EXPECT_EQ(device_grid.dim, grid_n_own.dim);
0161 auto y_axis_dev = device_grid.get_axis<label::e_y>();
0162 EXPECT_EQ(y_axis_dev.nbins(), grid_n_own.get_axis<label::e_y>().nbins());
0163 auto z_axis_dev = device_grid.get_axis<label::e_z>();
0164 EXPECT_EQ(z_axis_dev.nbins(), grid_n_own.get_axis<label::e_z>().nbins());
0165
0166 // Test the global bin iteration: owning grid
0167 auto seq = detray::views::iota(1, 40'001);
0168 auto flat_bin_view = grid_own.all();
0169
0170 static_assert(detray::ranges::random_access_range<decltype(flat_bin_view)>);
0171
0172 EXPECT_EQ(seq.size(), 40'000u);
0173 EXPECT_EQ(flat_bin_view.size(), 40'000u);
0174 EXPECT_EQ(flat_bin_view[42], 43u);
0175 EXPECT_TRUE(
0176 std::equal(flat_bin_view.begin(), flat_bin_view.end(), seq.begin()));
0177
0178
0179 auto flat_bin_view2 = grid_n_own.all();
0180
0181 static_assert(detray::ranges::random_access_range<decltype(flat_bin_view2)>);
0182
0183 EXPECT_EQ(seq.size(), 40'000u);
0184 EXPECT_EQ(flat_bin_view2.size(), 40'000u);
0185 EXPECT_EQ(flat_bin_view2[42], 43u);
0186 EXPECT_TRUE(
0187 std::equal(flat_bin_view2.begin(), flat_bin_view2.end(), seq.begin()));
0188
0189
0190
0191
0192
0193
0194
0195
0196
0197
0198
0199
0200
0201
0202
0203
0204
0205
0206 }
0207
0208
0209 GTEST_TEST(detray_grid, dynamic_array) {
0210
0211 using grid_owning_t =
0212 grid<test_algebra, axes<cuboid3D>, bins::dynamic_array<scalar>>;
0213
0214 using grid_n_owning_t =
0215 grid<test_algebra, axes<cuboid3D>, bins::dynamic_array<scalar>,
0216 simple_serializer, host_container_types, false>;
0217
0218 using grid_device_t =
0219 grid<test_algebra, axes<cuboid3D>, bins::dynamic_array<scalar>,
0220 simple_serializer, device_container_types>;
0221
0222 static_assert(concepts::grid<grid_owning_t>);
0223 static_assert(concepts::grid<grid_n_owning_t>);
0224 static_assert(concepts::grid<grid_device_t>);
0225
0226
0227
0228 grid_owning_t::bin_container_type bin_data{};
0229
0230 bin_data.entries.resize(80'000u);
0231 // 20 000 bins
0232 bin_data.bins.resize(40'000u);
0233
0234 int i{0};
0235 dindex offset{0u};
0236 scalar entry{0.f};
0237 complete<> completer{};
0238
0239
0240 std::vector<scalar> seq;
0241 seq.reserve(80'000);
0242
0243 for (auto& data : bin_data.bins) {
0244 data.offset = offset;
0245 // Every second bin holds one element, otherwise three
0246 data.capacity = (i % 2) != 0u ? 1u : 3u;
0247
0248 detray::bins::dynamic_array bin{bin_data.entries.data(), data};
0249
0250 ASSERT_TRUE(bin.capacity() == (i % 2 ? 1u : 3u));
0251 ASSERT_TRUE(bin.size() == 0);
0252
0253 offset += bin.capacity();
0254
0255 // Populate the bin
0256 completer(bin, entry);
0257
0258 for (auto e : bin) {
0259 ASSERT_TRUE(e == entry);
0260 seq.push_back(e);
0261 }
0262 entry += 1.f;
0263 ++i;
0264 }
0265
0266 // Copy data that will be moved into the data owning types
0267 dvector<scalar> bin_edges_cp(bin_edges);
0268 dvector<dsized_index_range> edge_ranges_cp(edge_ranges);
0269 grid_owning_t::bin_container_type bin_data_cp(bin_data);
0270
0271 // Data-owning axes and grid
0272 cartesian_3D<is_owning, host_container_types> axes_own(
0273 std::move(edge_ranges_cp), std::move(bin_edges_cp));
0274 grid_owning_t grid_own(std::move(bin_data_cp), std::move(axes_own));
0275
0276 // Check a few basics
0277 EXPECT_EQ(grid_own.dim, 3u);
0278 EXPECT_EQ(grid_own.nbins(), 40'000u);
0279 auto y_axis = grid_own.get_axis<label::e_y>();
0280 EXPECT_EQ(y_axis.nbins(), 40u);
0281 auto z_axis =
0282 grid_own.get_axis<single_axis<closed<label::e_z>, regular<scalar>>>();
0283 EXPECT_EQ(z_axis.nbins(), 50u);
0284
0285
0286
0287 dvector<scalar> bin_edges_cp2(bin_edges);
0288 dvector<dsized_index_range> edge_ranges_cp2(edge_ranges);
0289 grid_owning_t::bin_container_type bin_data_cp2(bin_data);
0290
0291
0292 cartesian_3D<is_owning, host_container_types> axes_own2(
0293 std::move(edge_ranges_cp2), std::move(bin_edges_cp2));
0294
0295 grid_owning_t grid_own2(std::move(bin_data_cp2), std::move(axes_own2));
0296
0297
0298 EXPECT_TRUE(grid_own == grid_own2);
0299
0300
0301 grid_n_owning_t grid_n_own(&bin_data, ax_n_own);
0302
0303
0304 EXPECT_EQ(grid_n_own.dim, grid_own.dim);
0305 y_axis = grid_n_own.get_axis<label::e_y>();
0306 EXPECT_EQ(y_axis.nbins(), grid_own.get_axis<label::e_y>().nbins());
0307 z_axis = grid_n_own.get_axis<label::e_z>();
0308 EXPECT_EQ(z_axis.nbins(), grid_own.get_axis<label::e_z>().nbins());
0309
0310
0311 grid_owning_t::view_type grid_view = get_data(grid_own);
0312 grid_device_t device_grid(grid_view);
0313
0314
0315 EXPECT_EQ(device_grid.dim, grid_n_own.dim);
0316 auto y_axis_dev = device_grid.get_axis<label::e_y>();
0317 EXPECT_EQ(y_axis_dev.nbins(), grid_n_own.get_axis<label::e_y>().nbins());
0318 auto z_axis_dev = device_grid.get_axis<label::e_z>();
0319 EXPECT_EQ(z_axis_dev.nbins(), grid_n_own.get_axis<label::e_z>().nbins());
0320
0321
0322 auto flat_bin_view = grid_own.all();
0323
0324 static_assert(detray::ranges::bidirectional_range<decltype(flat_bin_view)>);
0325
0326
0327
0328
0329 EXPECT_EQ(seq.size(), 80'000u);
0330 EXPECT_EQ(flat_bin_view.size(), 80'000u);
0331 EXPECT_TRUE(
0332 std::equal(flat_bin_view.begin(), flat_bin_view.end(), seq.begin()));
0333
0334
0335 auto flat_bin_view2 = grid_n_own.all();
0336
0337 static_assert(detray::ranges::bidirectional_range<decltype(flat_bin_view2)>);
0338
0339 EXPECT_EQ(seq.size(), 80'000u);
0340 EXPECT_EQ(flat_bin_view2.size(), 80'000u);
0341 EXPECT_TRUE(
0342 std::equal(flat_bin_view2.begin(), flat_bin_view2.end(), seq.begin()));
0343 }
0344
0345
0346 GTEST_TEST(detray_grid, replace_population) {
0347
0348 using grid_t = grid<test_algebra, decltype(ax_n_own), bins::single<scalar>,
0349 simple_serializer, host_container_types, false>;
0350
0351 static_assert(concepts::grid<grid_t>);
0352
0353
0354 using bin_t = grid_t::bin_type;
0355 grid_t::bin_container_type bin_data{};
0356 bin_data.resize(40'000u, bin_t{});
0357
0358 // Create non-owning grid
0359 grid_t g3r(&bin_data, ax_n_own);
0360
0361 // Test the initialization
0362 point3 p = {-10.f, -20.f, 0.f};
0363 for (int ib0 = 0; ib0 < 20; ++ib0) {
0364 for (int ib1 = 0; ib1 < 40; ++ib1) {
0365 for (int ib2 = 0; ib2 < 100; ib2 += 2) {
0366 p = {static_cast<scalar>(-10 + ib0), static_cast<scalar>(-20 + ib1),
0367 static_cast<scalar>(0 + ib2)};
0368 EXPECT_NEAR(g3r.bin(p)[0], std::numeric_limits<scalar>::max(), tol);
0369 }
0370 }
0371 }
0372
0373 p = {-4.5f, -4.5f, 4.5f};
0374 // Fill and read
0375 g3r.template populate<replace<>>(p, 3.f);
0376 EXPECT_NEAR(g3r.bin(p)[0], static_cast<scalar>(3u), tol);
0377
0378 // Fill and read two times, fill first 0-99, then 100-199
0379 for (unsigned int il = 0u; il < 2u; ++il) {
0380 scalar counter{static_cast<scalar>(il * 100u)};
0381 for (int ib0 = 0; ib0 < 20; ++ib0) {
0382 for (int ib1 = 0; ib1 < 40; ++ib1) {
0383 for (int ib2 = 0; ib2 < 100; ib2 += 2) {
0384 p = {static_cast<scalar>(-10 + ib0), static_cast<scalar>(-20 + ib1),
0385 static_cast<scalar>(0 + ib2)};
0386 g3r.template populate<replace<>>(p, counter);
0387 EXPECT_NEAR(g3r.bin(p)[0], counter, tol);
0388 counter += 1.f;
0389 }
0390 }
0391 }
0392 }
0393 }
0394
0395 /// Test bin entry retrieval
0396 GTEST_TEST(detray_grid, complete_population) {
0397 // Non-owning, 3D cartesian, completing grid (4 dims and sort)
0398 using grid_t =
0399 grid<test_algebra, decltype(ax_n_own), bins::static_array<scalar, 4>,
0400 simple_serializer, host_container_types, false>;
0401 using bin_t = grid_t::bin_type;
0402 using bin_content_t = std::array<scalar, 4>;
0403
0404 static_assert(concepts::grid<grid_t>);
0405
0406 // init
0407 grid_t::bin_container_type bin_data{};
0408 bin_data.resize(40'000u, bin_t{});
0409
0410 grid_t g3c(&bin_data, ax_n_own);
0411
0412
0413 point3 p = {-10.f, -20.f, 0.f};
0414 bin_t invalid{};
0415 for (int ib0 = 0; ib0 < 20; ++ib0) {
0416 for (int ib1 = 0; ib1 < 40; ++ib1) {
0417 for (int ib2 = 0; ib2 < 100; ib2 += 2) {
0418 p = {static_cast<scalar>(-10 + ib0), static_cast<scalar>(-20 + ib1),
0419 static_cast<scalar>(0 + ib2)};
0420 test_content(g3c, p, invalid);
0421 }
0422 }
0423 }
0424
0425 p = {-4.5f, -4.5f, 4.5f};
0426 bin_content_t expected{4.f, 4.f, 4.f, 4.f};
0427
0428 g3c.template populate<complete<>>(p, 4.f);
0429 test_content(g3c, p, expected);
0430
0431
0432 for (scalar entry : g3c.bin(p)) {
0433 EXPECT_EQ(entry, 4.f);
0434 }
0435 }
0436
0437
0438 GTEST_TEST(detray_grid, regular_attach_population) {
0439
0440 using grid_t =
0441 grid<test_algebra, decltype(ax_n_own), bins::static_array<scalar, 4>,
0442 simple_serializer, host_container_types, false>;
0443 using bin_t = grid_t::bin_type;
0444 using bin_content_t = std::array<scalar, 4>;
0445
0446 static_assert(concepts::grid<grid_t>);
0447
0448
0449 grid_t::bin_container_type bin_data{};
0450 bin_data.resize(40'000u, bin_t{});
0451
0452 // Create non-owning grid
0453 grid_t g3ra(&bin_data, ax_n_own);
0454
0455 // Test the initialization
0456 point3 p = {-10.f, -20.f, 0.f};
0457 bin_t invalid{};
0458 for (int ib0 = 0; ib0 < 20; ++ib0) {
0459 for (int ib1 = 0; ib1 < 40; ++ib1) {
0460 for (int ib2 = 0; ib2 < 100; ib2 += 2) {
0461 p = {static_cast<scalar>(-10 + ib0), static_cast<scalar>(-20 + ib1),
0462 static_cast<scalar>(0 + ib2)};
0463 test_content(g3ra, p, invalid);
0464 }
0465 }
0466 }
0467
0468 p = {-4.5f, -4.5f, 4.5f};
0469 bin_content_t expected{5.f, inf, inf, inf};
0470 // Fill and read
0471 g3ra.template populate<attach<>>(p, 5.f);
0472 test_content(g3ra, p, expected);
0473
0474 // Test search without search window
0475 for (scalar entry : g3ra.bin(p)) {
0476 EXPECT_EQ(entry, 5.f);
0477 }
0478 }