Back to home page

EIC code displayed by LXR

 
 

    


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

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 core
0010 #include "detray/utils/grid/axis.hpp"
0011 
0012 #include "detray/definitions/indexing.hpp"
0013 #include "detray/definitions/units.hpp"
0014 #include "detray/geometry/coordinates/coordinates.hpp"
0015 #include "detray/utils/grid/concepts.hpp"
0016 #include "detray/utils/grid/detail/axis_binning.hpp"
0017 #include "detray/utils/grid/detail/axis_bounds.hpp"
0018 
0019 // Detray test include(s)
0020 #include "detray/test/framework/types.hpp"
0021 
0022 // GTest include(s)
0023 #include <gtest/gtest.h>
0024 
0025 // System include(s)
0026 #include <limits>
0027 
0028 using namespace detray;
0029 using namespace detray::axis;
0030 
0031 namespace {
0032 
0033 using test_algebra = test::algebra;
0034 using scalar = test::scalar;
0035 using point3 = test::point3;
0036 
0037 // Alias for testing
0038 template <bool ownership, typename containers>
0039 using cartesian_3D =
0040     multi_axis<ownership, cartesian3D<test_algebra>,
0041                single_axis<closed<label::e_x>, regular<scalar, containers>>,
0042                single_axis<closed<label::e_y>, regular<scalar, containers>>,
0043                single_axis<closed<label::e_z>, regular<scalar, containers>>>;
0044 
0045 // Floating point comparison
0046 constexpr scalar tol{1e-5f};
0047 
0048 }  // anonymous namespace
0049 
0050 GTEST_TEST(detray_grid, open_regular_axis) {
0051   // Lower bin edges: min and max bin edge for the regular axis
0052   vecmem::vector<scalar> bin_edges = {-10.f, -5.f, -3.f, 7.f, 7.f, 14.f, 20.f};
0053   // Regular axis: first entry is the offset in the bin edges vector (2), the
0054   // second entry is the number of bins (10): Lower and upper bin edges of
0055   // the max and min bin are -3 and 7 => stepsize is (7 - (-3)) / 10 = 1
0056   // ... -3, -2, -1,  0,  1,  2,  3,  4,  5,  6,  7 ...
0057   //   [0] [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11]
0058   const dsized_index_range edge_range = {2u, 10u};
0059 
0060   // An open regular x-axis
0061   using x_axis_t = single_axis<open<label::e_x>, regular<scalar>>;
0062   x_axis_t or_axis{edge_range, &bin_edges};
0063 
0064   static_assert(concepts::axis<x_axis_t>);
0065 
0066   // Test axis bounds
0067   EXPECT_EQ(or_axis.label(), axis::label::e_x);
0068   EXPECT_EQ(or_axis.bounds(), axis::bounds::e_open);
0069   EXPECT_EQ(or_axis.binning(), axis::binning::e_regular);
0070 
0071   // N bins
0072   EXPECT_EQ(or_axis.nbins(), 10u + 2u);
0073   EXPECT_EQ(or_axis.m_binning.nbins(), 10u);
0074   EXPECT_NEAR(or_axis.span()[0], -3.f, tol);
0075   EXPECT_NEAR(or_axis.span()[1], 7.f, tol);
0076   // Axis bin access
0077   // Axis is open: Every value smaller than -3 is mapped into bin 0:
0078   // which is (-inf, -3]
0079   EXPECT_EQ(or_axis.bin(-4.f), 0u);
0080   EXPECT_EQ(or_axis.bin(2.5f), 6u);
0081   EXPECT_EQ(or_axis.bin(1.5f), 5u);
0082   EXPECT_EQ(or_axis.bin(7.5f), 11u);
0083   EXPECT_EQ(or_axis.bin(-3.5f), 0u);
0084   // Axis is open: Every value greater than 7 is mapped into bin 11:
0085   // which is [7, inf)
0086   EXPECT_EQ(or_axis.bin(8.f), 11u);
0087 
0088   // Axis range access - binned (symmetric & asymmetric)
0089   const darray<dindex, 2> nhood00i = {0u, 0u};
0090   const darray<dindex, 2> nhood01i = {0u, 1u};
0091   const darray<dindex, 2> nhood11i = {1u, 1u};
0092   const darray<dindex, 2> nhood44i = {4u, 4u};
0093   const darray<dindex, 2> nhood55i = {5u, 5u};
0094 
0095   axis::bin_range expected_range = {6u, 7u};
0096   EXPECT_EQ(or_axis.range(2.5f, nhood00i), expected_range);
0097   expected_range = {6u, 8u};
0098   EXPECT_EQ(or_axis.range(2.5f, nhood01i), expected_range);
0099   expected_range = {5u, 8u};
0100   EXPECT_EQ(or_axis.range(2.5f, nhood11i), expected_range);
0101   expected_range = {2u, 11u};
0102   EXPECT_EQ(or_axis.range(2.5f, nhood44i), expected_range);
0103   expected_range = {1u, 12u};
0104   EXPECT_EQ(or_axis.range(2.5f, nhood55i), expected_range);
0105   expected_range = {1u, 10u};
0106   EXPECT_EQ(or_axis.range(1.5f, nhood44i), expected_range);
0107   expected_range = {4u, 12u};
0108   EXPECT_EQ(or_axis.range(5.5f, nhood55i), expected_range);
0109   expected_range = {11u, 12u};
0110   EXPECT_EQ(or_axis.range(7.5f, nhood00i), expected_range);
0111   expected_range = {0u, 1u};
0112   EXPECT_EQ(or_axis.range(-3.5f, nhood00i), expected_range);
0113 
0114   // Axis range access - scalar (symmteric & asymmetric)
0115   const darray<scalar, 2> nhood00s = {0.f, 0.f};
0116   const darray<scalar, 2> nhood_tol = {0.01f, 0.01f};
0117   const darray<scalar, 2> nhood11s = {1.f, 1.f};
0118   const darray<scalar, 2> nhoodAlls = {10.f, 10.f};
0119 
0120   expected_range = {6u, 7u};
0121   EXPECT_EQ(or_axis.range(2.5f, nhood00s), expected_range);
0122   EXPECT_EQ(or_axis.range(2.5f, nhood_tol), expected_range);
0123   expected_range = {5u, 8u};
0124   EXPECT_EQ(or_axis.range(2.5f, nhood11s), expected_range);
0125   expected_range = {0u, 12u};
0126   EXPECT_EQ(or_axis.range(2.5f, nhoodAlls), expected_range);
0127 }
0128 
0129 GTEST_TEST(detray_grid, closed_regular_axis) {
0130   // Lower bin edges: min and max bin edge for the regular axis
0131   vecmem::vector<scalar> bin_edges = {-10.f, -3.f, -3.f, 7.f, 7.f, 14.f};
0132   // Regular axis: first entry is the offset in the bin edges vector (2), the
0133   // second entry is the number of bins (10): Lower and upper bin edges of
0134   // the max and min bin are -3 and 7 => stepsize is (7 - (-3)) / 10 = 1
0135   // -3, -2, -1,  0,  1,  2,  3,  4,  5,  6,  7
0136   //   [0] [1] [2] [3] [4] [5] [6] [7] [8] [9]
0137   const dsized_index_range edge_range = {2u, 10u};
0138 
0139   // A closed regular r-axis
0140   using r_axis_t = single_axis<closed<label::e_r>, regular<scalar>>;
0141   r_axis_t cr_axis{edge_range, &bin_edges};
0142 
0143   static_assert(concepts::axis<r_axis_t>);
0144 
0145   // Test axis bounds
0146   EXPECT_EQ(cr_axis.label(), axis::label::e_r);
0147   EXPECT_EQ(cr_axis.bounds(), axis::bounds::e_closed);
0148   EXPECT_EQ(cr_axis.binning(), axis::binning::e_regular);
0149 
0150   // N bins
0151   EXPECT_EQ(cr_axis.nbins(), 10u);
0152   EXPECT_NEAR(cr_axis.span()[0], -3.f, tol);
0153   EXPECT_NEAR(cr_axis.span()[1], 7.f, tol);
0154   // Axis bin access
0155   // Axis is closed: Every value smaller than -3 is mapped into bin 0:
0156   // which is [-3, -2)
0157   EXPECT_EQ(cr_axis.bin(-4.f), 0u);
0158   EXPECT_EQ(cr_axis.bin(2.5f), 5u);
0159   // Axis is closed: Every value greater than 7 is mapped into bin 9:
0160   // which is (6, 7]
0161   EXPECT_EQ(cr_axis.bin(8.f), 9u);
0162 
0163   // Axis range access - binned (symmetric & asymmetric)
0164   const darray<dindex, 2> nhood00i = {0u, 0u};
0165   const darray<dindex, 2> nhood01i = {0u, 1u};
0166   const darray<dindex, 2> nhood11i = {1u, 1u};
0167   const darray<dindex, 2> nhood44i = {4u, 4u};
0168   const darray<dindex, 2> nhood55i = {5u, 5u};
0169 
0170   axis::bin_range expected_range = {5u, 6u};
0171   EXPECT_EQ(cr_axis.range(2.5f, nhood00i), expected_range);
0172   expected_range = {5u, 7u};
0173   EXPECT_EQ(cr_axis.range(2.5f, nhood01i), expected_range);
0174   expected_range = {4u, 7u};
0175   EXPECT_EQ(cr_axis.range(2.5f, nhood11i), expected_range);
0176   expected_range = {1u, 10u};
0177   EXPECT_EQ(cr_axis.range(2.5f, nhood44i), expected_range);
0178   expected_range = {0u, 10u};
0179   EXPECT_EQ(cr_axis.range(2.5f, nhood55i), expected_range);
0180   expected_range = {0u, 9u};
0181   EXPECT_EQ(cr_axis.range(1.5f, nhood44i), expected_range);
0182   expected_range = {3u, 10u};
0183   EXPECT_EQ(cr_axis.range(5.5f, nhood55i), expected_range);
0184   expected_range = {9u, 10u};
0185   EXPECT_EQ(cr_axis.range(7.5f, nhood00i), expected_range);
0186   expected_range = {0u, 1u};
0187   EXPECT_EQ(cr_axis.range(-3.5f, nhood00i), expected_range);
0188 
0189   // Axis range access - scalar (symmteric & asymmetric)
0190   const darray<scalar, 2> nhood00s = {0.f, 0.f};
0191   const darray<scalar, 2> nhood_tol = {0.01f, 0.01f};
0192   const darray<scalar, 2> nhood11s = {1.f, 1.f};
0193   const darray<scalar, 2> nhoodAlls = {10.f, 10.f};
0194 
0195   expected_range = {5u, 6u};
0196   EXPECT_EQ(cr_axis.range(2.5f, nhood00s), expected_range);
0197   EXPECT_EQ(cr_axis.range(2.5f, nhood_tol), expected_range);
0198   expected_range = {4u, 7u};
0199   EXPECT_EQ(cr_axis.range(2.5f, nhood11s), expected_range);
0200   expected_range = {0u, 10u};
0201   EXPECT_EQ(cr_axis.range(2.5f, nhoodAlls), expected_range);
0202 }
0203 
0204 GTEST_TEST(detray_grid, circular_regular_axis) {
0205   // Let's say 36 modules, but with 4 directly at 0, pi/2, pi, -pi2
0206   const scalar half_module{constant<scalar>::pi / 72.f};
0207   const scalar phi_min{-constant<scalar>::pi + half_module};
0208   const scalar phi_max{constant<scalar>::pi - half_module};
0209 
0210   // Lower bin edges: min and max bin edge for the regular axis
0211   vecmem::vector<scalar> bin_edges = {-10.f, phi_min, phi_max, 56.f};
0212   // Regular axis: first entry is the offset in the bin edges vector (1), the
0213   // second entry is the number of bins (36)
0214   const dsized_index_range edge_range = {1u, 36u};
0215 
0216   // A closed regular x-axis
0217   using axis_t = single_axis<circular<>, regular<scalar>>;
0218   axis_t cr_axis(edge_range, &bin_edges);
0219 
0220   static_assert(concepts::axis<axis_t>);
0221 
0222   // Test axis bounds
0223   EXPECT_EQ(cr_axis.label(), axis::label::e_phi);
0224   EXPECT_EQ(cr_axis.bounds(), axis::bounds::e_circular);
0225   EXPECT_EQ(cr_axis.binning(), axis::binning::e_regular);
0226 
0227   // N bins
0228   EXPECT_EQ(cr_axis.nbins(), 36u);
0229   // Axis bin access
0230   // overflow
0231   EXPECT_EQ(cr_axis.bin(phi_max + tol), 0u);
0232   // underflow
0233   EXPECT_EQ(cr_axis.bin(phi_min - tol), 35u);
0234   // middle of the axis
0235   EXPECT_EQ(cr_axis.bin(0), 18u);
0236 
0237   // Bin wrapping test
0238   typename single_axis<circular<>, regular<scalar>>::bounds_type circ_bounds{};
0239   EXPECT_EQ(circ_bounds.wrap(4, 36u), 4u);
0240   EXPECT_EQ(circ_bounds.wrap(0, 36u), 0u);
0241   EXPECT_EQ(circ_bounds.wrap(-1, 36u), 35u);
0242   EXPECT_EQ(circ_bounds.wrap(36, 36u), 0u);
0243   EXPECT_EQ(circ_bounds.wrap(40, 36u), 4u);
0244 
0245   // Axis range access - binned (symmetric & asymmetric)
0246   const darray<dindex, 2> nhood00i = {0u, 0u};
0247   const darray<dindex, 2> nhood01i = {0u, 1u};
0248   const darray<dindex, 2> nhood11i = {1u, 1u};
0249   const darray<dindex, 2> nhood22i = {2u, 2u};
0250 
0251   axis::bin_range expected_range = {0u, 1u};
0252   EXPECT_EQ(circ_bounds.wrap(
0253                 cr_axis.range(constant<scalar>::pi + tol, nhood00i), 36u),
0254             expected_range);
0255   expected_range = {0u, 2u};
0256   EXPECT_EQ(circ_bounds.wrap(
0257                 cr_axis.range(constant<scalar>::pi + tol, nhood01i), 36u),
0258             expected_range);
0259   expected_range = {35u, 2u};
0260   EXPECT_EQ(circ_bounds.wrap(
0261                 cr_axis.range(constant<scalar>::pi + tol, nhood11i), 36u),
0262             expected_range);
0263   expected_range = {34u, 3u};
0264   EXPECT_EQ(circ_bounds.wrap(
0265                 cr_axis.range(constant<scalar>::pi + tol, nhood22i), 36u),
0266             expected_range);
0267 
0268   // Axis range access - scalar (symmetric & asymmteric)
0269   const darray<scalar, 2> nhood00s = {0.f, 0.f};
0270   const darray<scalar, 2> nhood_tol = {0.5f * tol, 0.5f * tol};
0271   const scalar bin_step{cr_axis.bin_width()};
0272   const darray<scalar, 2> nhood22s = {2.f * bin_step, 2.f * bin_step};
0273 
0274   expected_range = {0u, 1u};
0275   EXPECT_EQ(circ_bounds.wrap(
0276                 cr_axis.range(constant<scalar>::pi + tol, nhood00s), 36u),
0277             expected_range);
0278   EXPECT_EQ(circ_bounds.wrap(
0279                 cr_axis.range(constant<scalar>::pi + tol, nhood_tol), 36u),
0280             expected_range);
0281   expected_range = {34u, 3u};
0282   EXPECT_EQ(circ_bounds.wrap(
0283                 cr_axis.range(constant<scalar>::pi + tol, nhood22s), 36u),
0284             expected_range);
0285 }
0286 
0287 GTEST_TEST(detray_grid, closed_irregular_axis) {
0288   // Lower bin edges: all lower bin edges for irregular binning, plus the
0289   // final upper bin edge
0290   vecmem::vector<scalar> bin_edges = {-100.f, -3.f, 1.f,  2.f, 4.f,
0291                                       8.f,    12.f, 15.f, 18.f};
0292   // Index offset and number of bins for the bin edges [-3, 15]
0293   dsized_index_range edge_range = {1u, 6u};
0294 
0295   // A closed irregular z-axis
0296   single_axis<closed<label::e_z>, irregular<scalar>> cir_axis(edge_range,
0297                                                               &bin_edges);
0298 
0299   // Test axis bounds
0300   EXPECT_EQ(cir_axis.label(), axis::label::e_z);
0301   EXPECT_EQ(cir_axis.bounds(), axis::bounds::e_closed);
0302   EXPECT_EQ(cir_axis.binning(), axis::binning::e_irregular);
0303 
0304   // Axis bin access
0305   //
0306   // N bins
0307   EXPECT_EQ(cir_axis.nbins(), 6u);
0308   // Bin tests
0309   EXPECT_EQ(cir_axis.bin(-2), 0u);
0310   EXPECT_EQ(cir_axis.bin(10), 4u);
0311   EXPECT_EQ(cir_axis.bin(5.8f), 3u);
0312   // Underflow test
0313   EXPECT_EQ(cir_axis.bin(-4), 0u);
0314   // Overflow test
0315   EXPECT_EQ(cir_axis.bin(17), 5u);
0316 
0317   // Axis range access - binned  (symmetric & asymmetric)
0318   const darray<dindex, 2> nhood00i = {0u, 0u};
0319   const darray<dindex, 2> nhood01i = {0u, 1u};
0320   const darray<dindex, 2> nhood11i = {1u, 1u};
0321   const darray<dindex, 2> nhood22i = {2u, 2u};
0322 
0323   axis::bin_range expected_range = {2u, 3u};
0324   EXPECT_EQ(cir_axis.range(3.f, nhood00i), expected_range);
0325   expected_range = {1u, 4u};
0326   EXPECT_EQ(cir_axis.range(3.f, nhood11i), expected_range);
0327   expected_range = {2u, 4u};
0328   EXPECT_EQ(cir_axis.range(3.f, nhood01i), expected_range);
0329   expected_range = {0u, 2u};
0330   EXPECT_EQ(cir_axis.range(0.f, nhood11i), expected_range);
0331   expected_range = {2u, 6u};
0332   EXPECT_EQ(cir_axis.range(10.f, nhood22i), expected_range);
0333 
0334   // Axis range access - scalar
0335   const darray<scalar, 2> nhood00s = {0.f, 0.f};
0336   const darray<scalar, 2> nhood10s = {1.5f, 0.2f};
0337   const darray<scalar, 2> nhood11s = {4.f, 5.5f};
0338 
0339   expected_range = {2u, 3u};
0340   EXPECT_EQ(cir_axis.range(3.f, nhood00s), expected_range);
0341   expected_range = {1u, 3u};
0342   EXPECT_EQ(cir_axis.range(3.f, nhood10s), expected_range);
0343   expected_range = {0u, 5u};
0344   EXPECT_EQ(cir_axis.range(3.f, nhood11s), expected_range);
0345 }
0346 
0347 template <typename axis_type>
0348 void test_axis(const axis_type& /*unused*/) {
0349   // Lower bin edges: min and max bin edge for the regular axis
0350   vecmem::vector<scalar> bin_edges = {-10.f, -5.f, -3.f,  7.f,   7.f,  14.f,
0351                                       20.f,  50.f, 100.f, 120.f, 150.f};
0352 
0353   vecmem::vector<scalar> different_bin_edges = {
0354       -10.f, -5.f, -4.f, 7.f, 7.f, 14.f, 20.f, 50.f, 100.f, 120.f, 150.f};
0355 
0356   // Regular axis: first entry is the offset in the bin edges vector (2),
0357   // the second entry is the number of bins (10): Lower and upper bin
0358   // edges of the max and min bin are -3 and 7 => stepsize is (7 - (-3)) /
0359   // 10 = 1
0360   // ... -3, -2, -1,  0,  1,  2,  3,  4,  5,  6,  7 ...
0361   //   [0] [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11]
0362   const dsized_index_range edge_range = {2u, 10u};
0363 
0364   const dsized_index_range different_edge_range = {2u, 8u};
0365 
0366   axis_type t_axis{edge_range, &bin_edges};
0367 
0368   axis_type another_t_axis{edge_range, &bin_edges};
0369 
0370   axis_type differnt_t_axis{edge_range, &different_bin_edges};
0371 
0372   axis_type differnt_edges_range_t_axis{different_edge_range, &bin_edges};
0373 
0374   // Test if the axes are equal
0375   EXPECT_EQ(t_axis, another_t_axis);
0376 
0377   // Confirm that the axes are not equal
0378   EXPECT_NE(t_axis, differnt_t_axis);
0379 
0380   // Confirm that the axes are not equal
0381   EXPECT_NE(t_axis, differnt_edges_range_t_axis);
0382 };
0383 
0384 GTEST_TEST(detray_grid, axis_comparison) {
0385   // Should be sufficient to test the comparison operators
0386   using x_axis_op_r_t = single_axis<open<label::e_x>, regular<scalar>>;
0387   using x_axis_cl_r_t = single_axis<closed<label::e_x>, regular<scalar>>;
0388   using x_axis_ci_r_t = single_axis<circular<label::e_x>, regular<scalar>>;
0389   using x_axis_op_ir_t = single_axis<open<label::e_x>, irregular<scalar>>;
0390   using x_axis_cl_ir_t = single_axis<closed<label::e_x>, irregular<scalar>>;
0391   using x_axis_ci_orr_t = single_axis<circular<label::e_x>, irregular<scalar>>;
0392 
0393   std::tuple<x_axis_op_r_t, x_axis_cl_r_t, x_axis_ci_r_t, x_axis_op_ir_t,
0394              x_axis_cl_ir_t, x_axis_ci_orr_t>
0395       axes = {};
0396 
0397   // Test them all
0398   std::apply([&](auto&... axis) { (test_axis(axis), ...); }, axes);
0399 }
0400 
0401 GTEST_TEST(detray_grid, multi_axis) {
0402   // readable axis ownership definition
0403   bool constexpr is_owning = true;
0404   bool constexpr is_not_owning = false;
0405 
0406   // Lower bin edges for all axes
0407   vecmem::vector<scalar> bin_edges = {-10.f, 10.f, -20.f, 20.f, 0.f, 100.f};
0408   // Offsets into edges container and #bins for all axes
0409   vecmem::vector<dsized_index_range> edge_ranges = {
0410       {0u, 20u}, {2u, 40u}, {4u, 50u}};
0411 
0412   // Non-owning multi axis test
0413   cartesian_3D<is_not_owning, host_container_types> axes(edge_ranges,
0414                                                          bin_edges);
0415 
0416   EXPECT_EQ(axes.dim, 3u);
0417 
0418   // Get single axis objects
0419   auto x_axis = axes.get_axis<label::e_x>();
0420   EXPECT_EQ(x_axis.nbins(), 20u);
0421   auto y_axis = axes.get_axis<label::e_y>();
0422   EXPECT_EQ(y_axis.nbins(), 40u);
0423   auto z_axis = axes.get_axis<label::e_z>();
0424   EXPECT_EQ(z_axis.nbins(), 50u);
0425 
0426   auto ax = axes.get_axis<decltype(y_axis)>();
0427   EXPECT_EQ(ax.nbins(), 40u);
0428 
0429   // Test bin search
0430   point3 p3{0.f, 0.f, 0.f};  // origin
0431   dmulti_index<dindex, 3> expected_bins{10u, 20u, 0u};
0432   EXPECT_EQ(axes.bins(p3), expected_bins);
0433   p3 = {-5.5f, -3.2f, 24.1f};
0434   expected_bins = {4u, 16u, 12u};
0435   EXPECT_EQ(axes.bins(p3), expected_bins);
0436   p3 = {-1.f, 21.f, 12.f};
0437   expected_bins = {9u, 39u, 6u};
0438   EXPECT_EQ(axes.bins(p3), expected_bins);
0439 
0440   // Test bin range search
0441   p3 = {1.f, 1.f, 10.f};
0442   // Axis range access - binned  (symmetric & asymmetric)
0443   const darray<dindex, 2> nhood00i = {0u, 0u};
0444   const darray<dindex, 2> nhood01i = {0u, 1u};
0445   const darray<dindex, 2> nhood22i = {2u, 2u};
0446 
0447   axis::multi_bin_range<3> expected_ranges{};
0448   expected_ranges[0] = {11u, 12u};
0449   expected_ranges[1] = {21u, 22u};
0450   expected_ranges[2] = {5u, 6u};
0451   EXPECT_EQ(axes.bin_ranges(p3, nhood00i), expected_ranges);
0452   expected_ranges[0] = {11u, 13u};
0453   expected_ranges[1] = {21u, 23u};
0454   expected_ranges[2] = {5u, 7u};
0455   EXPECT_EQ(axes.bin_ranges(p3, nhood01i), expected_ranges);
0456   expected_ranges[0] = {9u, 14u};
0457   expected_ranges[1] = {19u, 24u};
0458   expected_ranges[2] = {3u, 8u};
0459   EXPECT_EQ(axes.bin_ranges(p3, nhood22i), expected_ranges);
0460 
0461   // Axis range access - scalar
0462   const darray<scalar, 2> nhood00s = {0.f, 0.f};
0463   const darray<scalar, 2> nhood10s = {1.5f, 0.2f};
0464   const darray<scalar, 2> nhood11s = {4.f, 5.5f};
0465 
0466   expected_ranges[0] = {11u, 12u};
0467   expected_ranges[1] = {21u, 22u};
0468   expected_ranges[2] = {5u, 6u};
0469   EXPECT_EQ(axes.bin_ranges(p3, nhood00s), expected_ranges);
0470   expected_ranges[0] = {9u, 12u};
0471   expected_ranges[1] = {19u, 22u};
0472   expected_ranges[2] = {4u, 6u};
0473   EXPECT_EQ(axes.bin_ranges(p3, nhood10s), expected_ranges);
0474   expected_ranges[0] = {7u, 17u};
0475   expected_ranges[1] = {17u, 27u};
0476   expected_ranges[2] = {3u, 8u};
0477   EXPECT_EQ(axes.bin_ranges(p3, nhood11s), expected_ranges);
0478 
0479   // Owning multi axis test
0480   vecmem::vector<scalar> bin_edges_cp(bin_edges);
0481   // Offsets into edges container and #bins for all axes
0482   vecmem::vector<dsized_index_range> edge_ranges_cp(edge_ranges);
0483 
0484   cartesian_3D<is_owning, host_container_types> axes_own(
0485       std::move(edge_ranges_cp), std::move(bin_edges_cp));
0486 
0487   EXPECT_EQ(axes_own.bins(p3), axes.bins(p3));
0488   EXPECT_EQ(axes_own.bin_ranges(p3, nhood00i), axes.bin_ranges(p3, nhood00i));
0489   EXPECT_EQ(axes_own.bin_ranges(p3, nhood01i), axes.bin_ranges(p3, nhood01i));
0490   EXPECT_EQ(axes_own.bin_ranges(p3, nhood22i), axes.bin_ranges(p3, nhood22i));
0491   EXPECT_EQ(axes_own.bin_ranges(p3, nhood00s), axes.bin_ranges(p3, nhood00s));
0492   EXPECT_EQ(axes_own.bin_ranges(p3, nhood10s), axes.bin_ranges(p3, nhood10s));
0493   EXPECT_EQ(axes_own.bin_ranges(p3, nhood11s), axes.bin_ranges(p3, nhood11s));
0494 
0495   // Transfer to an owning multi-axis, which uses vecmem::device_vector
0496   cartesian_3D<is_owning, host_container_types>::view_type axes_view =
0497       get_data(axes_own);
0498   cartesian_3D<is_owning, device_container_types> axes_device(axes_view);
0499 
0500   // Get single axis objects
0501   auto x_axis_device = axes_device.get_axis<label::e_x>();
0502   EXPECT_EQ(x_axis_device.nbins(), 20u);
0503   auto y_axis_device = axes_device.get_axis<label::e_y>();
0504   EXPECT_EQ(y_axis_device.nbins(), 40u);
0505   auto z_axis_device = axes_device.get_axis<label::e_z>();
0506   EXPECT_EQ(z_axis_device.nbins(), 50u);
0507 }
0508 
0509 GTEST_TEST(detray_grid, multi_axis_comparison) {
0510   // Non-ownging test
0511   bool constexpr is_not_owning = false;
0512 
0513   // Lower bin edges for all axes
0514   vecmem::vector<scalar> bin_edges = {-10.f, 10.f, -20.f, 20.f, 0.f, 100.f};
0515 
0516   vecmem::vector<scalar> diff_bin_edges = {-10.f, 11.f, -20.f,
0517                                            20.f,  0.f,  100.f};
0518 
0519   // Offsets into edges container and #bins for all axes
0520   vecmem::vector<dsized_index_range> edge_ranges = {
0521       {0u, 20u}, {2u, 40u}, {4u, 50u}};
0522 
0523   // Offsets into edges container and #bins for all axes
0524   vecmem::vector<dsized_index_range> diff_edge_ranges = {
0525       {0u, 20u}, {3u, 40u}, {4u, 50u}};
0526 
0527   // Non-owning multi axis test : reference
0528   cartesian_3D<is_not_owning, host_container_types> ref_no_axes(edge_ranges,
0529                                                                 bin_edges);
0530 
0531   // Non-owning multi axis test : test
0532   cartesian_3D<is_not_owning, host_container_types> test_no_axes(edge_ranges,
0533                                                                  bin_edges);
0534 
0535   // Owning multi axis test : reference
0536   EXPECT_EQ(ref_no_axes, test_no_axes);
0537 
0538   // Non-owning multi axis test : different bins
0539   cartesian_3D<is_not_owning, host_container_types> diff_edges_no_axes(
0540       edge_ranges, diff_bin_edges);
0541   // Confirm that the axes are not equal
0542   EXPECT_NE(ref_no_axes, diff_edges_no_axes);
0543 
0544   // Non-owning multi axis test : different bin ranges
0545   cartesian_3D<is_not_owning, host_container_types> diff_edge_ranges_no_axes(
0546       diff_edge_ranges, bin_edges);
0547   // Confirm that the axes are not equal
0548   EXPECT_NE(ref_no_axes, diff_edge_ranges_no_axes);
0549 
0550   // Owning test
0551   bool constexpr is_owning = true;
0552 
0553   vecmem::vector<scalar> bin_ref_o_edges = {-10.f, 10.f, -20.f,
0554                                             20.f,  0.f,  100.f};
0555 
0556   vecmem::vector<scalar> bin_same_o_edges = {-10.f, 10.f, -20.f,
0557                                              20.f,  0.f,  100.f};
0558 
0559   vecmem::vector<dsized_index_range> edge_ref_o_ranges = {
0560       {0u, 20u}, {2u, 40u}, {4u, 50u}};
0561 
0562   vecmem::vector<dsized_index_range> edge_diff_o_ranges = {
0563       {0u, 20u}, {2u, 39u}, {4u, 50u}};
0564 
0565   vecmem::vector<dsized_index_range> edge_same_o_ranges = {
0566       {0u, 20u}, {2u, 40u}, {4u, 50u}};
0567 
0568   vecmem::vector<scalar> bin_test_o_edges = {-10.f, 10.f, -20.f,
0569                                              20.f,  0.f,  100.f};
0570 
0571   vecmem::vector<scalar> same_test_o_edges = {-10.f, 10.f, -20.f,
0572                                               20.f,  0.f,  100.f};
0573 
0574   vecmem::vector<scalar> diff_test_o_edges = {-10.f, 11.f, -20.f,
0575                                               20.f,  0.f,  100.f};
0576 
0577   vecmem::vector<dsized_index_range> edge_test_o_ranges = {
0578       {0u, 20u}, {2u, 40u}, {4u, 50u}};
0579 
0580   // Non-owning multi axis test : reference
0581   cartesian_3D<is_owning, host_container_types> ref_o_axes(
0582       std::move(edge_ref_o_ranges), std::move(bin_ref_o_edges));
0583 
0584   // Non-owning multi axis test : test
0585   cartesian_3D<is_owning, host_container_types> test_o_axes(
0586       std::move(edge_test_o_ranges), std::move(bin_test_o_edges));
0587 
0588   // Owning multi axis test : reference
0589   EXPECT_EQ(ref_o_axes, test_o_axes);
0590 
0591   // Non-owning multi axis test : diff endes
0592   cartesian_3D<is_owning, host_container_types> diff_edges_o_axes(
0593       std::move(edge_same_o_ranges), std::move(diff_test_o_edges));
0594 
0595   // Confirm that the axes are not equal
0596   EXPECT_NE(ref_o_axes, diff_edges_o_axes);
0597 
0598   // Non-owning multi axis test : diff ranges
0599   cartesian_3D<is_owning, host_container_types> diff_edge_ranges_o_axes(
0600       std::move(edge_diff_o_ranges), std::move(bin_same_o_edges));
0601 
0602   // Confirm that the axes are not equal
0603   EXPECT_NE(ref_o_axes, diff_edge_ranges_o_axes);
0604 }