Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:12:58

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 #include <boost/test/unit_test.hpp>
0010 
0011 #include "Acts/Definitions/Algebra.hpp"
0012 #include "Acts/Tests/CommonHelpers/FloatComparisons.hpp"
0013 #include "Acts/Utilities/Axis.hpp"
0014 #include "Acts/Utilities/AxisDefinitions.hpp"
0015 #include "Acts/Utilities/Grid.hpp"
0016 #include "Acts/Utilities/detail/grid_helper.hpp"
0017 
0018 #include <algorithm>
0019 #include <array>
0020 #include <concepts>
0021 #include <cstddef>
0022 #include <set>
0023 #include <string>
0024 #include <tuple>
0025 #include <utility>
0026 #include <vector>
0027 
0028 using namespace Acts::detail;
0029 
0030 namespace Acts::Test {
0031 
0032 BOOST_AUTO_TEST_SUITE(GridTests)
0033 
0034 BOOST_AUTO_TEST_CASE(grid_test_1d_equidistant) {
0035   using Point = std::array<double, 1>;
0036   using indices = std::array<std::size_t, 1>;
0037   Axis a(0.0, 4.0, 4u);
0038   Grid g(Type<double>, std::move(a));
0039 
0040   // test general properties
0041   BOOST_CHECK_EQUAL(g.size(), 6u);
0042   BOOST_CHECK_EQUAL(g.numLocalBins().at(0), 4u);
0043 
0044   // global bin index
0045   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{-0.3}})), 0u);
0046   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{-0.}})), 1u);
0047   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{0.}})), 1u);
0048   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{0.7}})), 1u);
0049   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{1}})), 2u);
0050   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{1.2}})), 2u);
0051   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{2.}})), 3u);
0052   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{2.7}})), 3u);
0053   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{3.}})), 4u);
0054   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{3.9999}})), 4u);
0055   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{4.}})), 5u);
0056   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{4.98}})), 5u);
0057 
0058   // global bin index -> local bin indices
0059   BOOST_CHECK(g.localBinsFromGlobalBin(0) == indices({{0}}));
0060   BOOST_CHECK(g.localBinsFromGlobalBin(1) == indices({{1}}));
0061   BOOST_CHECK(g.localBinsFromGlobalBin(2) == indices({{2}}));
0062   BOOST_CHECK(g.localBinsFromGlobalBin(3) == indices({{3}}));
0063   BOOST_CHECK(g.localBinsFromGlobalBin(4) == indices({{4}}));
0064   BOOST_CHECK(g.localBinsFromGlobalBin(5) == indices({{5}}));
0065 
0066   // local bin indices -> global bin index
0067   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{0}}), 0u);
0068   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{1}}), 1u);
0069   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{2}}), 2u);
0070   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{3}}), 3u);
0071   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{4}}), 4u);
0072   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{5}}), 5u);
0073 
0074   BOOST_CHECK(g.localBinsFromGlobalBin(
0075                   g.globalBinFromPosition(Point({{2.7}}))) == indices({{3}}));
0076 
0077   // inside checks
0078   BOOST_CHECK(!g.isInside(Point({{-2.}})));
0079   BOOST_CHECK(g.isInside(Point({{0.}})));
0080   BOOST_CHECK(g.isInside(Point({{2.5}})));
0081   BOOST_CHECK(!g.isInside(Point({{4.}})));
0082   BOOST_CHECK(!g.isInside(Point({{6.}})));
0083 
0084   // test some bin centers
0085   CHECK_CLOSE_ABS(g.binCenter({{1}}), Point({{0.5}}), 1e-6);
0086   CHECK_CLOSE_ABS(g.binCenter({{2}}), Point({{1.5}}), 1e-6);
0087   CHECK_CLOSE_ABS(g.binCenter({{3}}), Point({{2.5}}), 1e-6);
0088   CHECK_CLOSE_ABS(g.binCenter({{4}}), Point({{3.5}}), 1e-6);
0089 
0090   // test some lower-left bin edges
0091   CHECK_CLOSE_ABS(g.lowerLeftBinEdge({{1}}), Point({{0.}}), 1e-6);
0092   CHECK_CLOSE_ABS(g.lowerLeftBinEdge({{2}}), Point({{1.}}), 1e-6);
0093   CHECK_CLOSE_ABS(g.lowerLeftBinEdge({{3}}), Point({{2.}}), 1e-6);
0094   CHECK_CLOSE_ABS(g.lowerLeftBinEdge({{4}}), Point({{3.}}), 1e-6);
0095 
0096   // test some upper right-bin edges
0097   CHECK_CLOSE_ABS(g.upperRightBinEdge({{1}}), Point({{1.}}), 1e-6);
0098   CHECK_CLOSE_ABS(g.upperRightBinEdge({{2}}), Point({{2.}}), 1e-6);
0099   CHECK_CLOSE_ABS(g.upperRightBinEdge({{3}}), Point({{3.}}), 1e-6);
0100   CHECK_CLOSE_ABS(g.upperRightBinEdge({{4}}), Point({{4.}}), 1e-6);
0101 
0102   // initialize grid
0103   for (std::size_t bin = 0; bin < g.size(); ++bin) {
0104     g.at(bin) = bin;
0105   }
0106 
0107   // consistency of access
0108   const auto& point = Point({{0.7}});
0109   std::size_t globalBin = g.globalBinFromPosition(point);
0110   indices localBins = g.localBinsFromGlobalBin(globalBin);
0111 
0112   BOOST_CHECK_EQUAL(g.atPosition(point), g.at(globalBin));
0113   BOOST_CHECK_EQUAL(g.atPosition(point), g.atLocalBins(localBins));
0114 }
0115 
0116 BOOST_AUTO_TEST_CASE(grid_test_2d_equidistant) {
0117   using Point = std::array<double, 2>;
0118   using indices = std::array<std::size_t, 2>;
0119   Axis a(0.0, 4.0, 4u);
0120   Axis b(0.0, 3.0, 3u);
0121   Grid g(Type<double>, std::move(a), std::move(b));
0122 
0123   // test general properties
0124   BOOST_CHECK_EQUAL(g.size(), 30u);
0125   BOOST_CHECK_EQUAL(g.numLocalBins().at(0), 4u);
0126   BOOST_CHECK_EQUAL(g.numLocalBins().at(1), 3u);
0127 
0128   // global bin index
0129   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{-1, -1}})), 0u);
0130   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{-1, 0}})), 1u);
0131   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{-1, 1}})), 2u);
0132   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{-1, 2}})), 3u);
0133   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{-1, 3}})), 4u);
0134   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{0, -1}})), 5u);
0135   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{0, 0}})), 6u);
0136   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{0, 1}})), 7u);
0137   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{0, 2}})), 8u);
0138   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{0, 3}})), 9u);
0139   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{1, -1}})), 10u);
0140   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{1, 0}})), 11u);
0141   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{1, 1}})), 12u);
0142   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{1, 2}})), 13u);
0143   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{1, 3}})), 14u);
0144   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{2, -1}})), 15u);
0145   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{2, 0}})), 16u);
0146   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{2, 1}})), 17u);
0147   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{2, 2}})), 18u);
0148   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{2, 3}})), 19u);
0149   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{3, -1}})), 20u);
0150   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{3, 0}})), 21u);
0151   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{3, 1}})), 22u);
0152   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{3, 2}})), 23u);
0153   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{3, 3}})), 24u);
0154   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{4, -1}})), 25u);
0155   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{4, 0}})), 26u);
0156   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{4, 1}})), 27u);
0157   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{4, 2}})), 28u);
0158   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{4, 3}})), 29u);
0159 
0160   // test some arbitrary points
0161   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{1.2, 0.3}})), 11u);
0162   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{2.2, 3.3}})), 19u);
0163   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{0.9, 1.8}})), 7u);
0164   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{3.7, 3.1}})), 24u);
0165   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{1.4, 2.3}})), 13u);
0166   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{-3, 3}})), 4u);
0167   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{8, 1}})), 27u);
0168   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{1, -3}})), 10u);
0169   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{3, 11}})), 24u);
0170   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{-2, -3}})), 0u);
0171   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{-2, 7}})), 04u);
0172   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{12, -1}})), 25u);
0173   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{12, 11}})), 29u);
0174 
0175   // global bin index -> local bin indices
0176   BOOST_CHECK(g.localBinsFromGlobalBin(0) == indices({{0, 0}}));
0177   BOOST_CHECK(g.localBinsFromGlobalBin(1) == indices({{0, 1}}));
0178   BOOST_CHECK(g.localBinsFromGlobalBin(2) == indices({{0, 2}}));
0179   BOOST_CHECK(g.localBinsFromGlobalBin(3) == indices({{0, 3}}));
0180   BOOST_CHECK(g.localBinsFromGlobalBin(4) == indices({{0, 4}}));
0181   BOOST_CHECK(g.localBinsFromGlobalBin(5) == indices({{1, 0}}));
0182   BOOST_CHECK(g.localBinsFromGlobalBin(6) == indices({{1, 1}}));
0183   BOOST_CHECK(g.localBinsFromGlobalBin(7) == indices({{1, 2}}));
0184   BOOST_CHECK(g.localBinsFromGlobalBin(8) == indices({{1, 3}}));
0185   BOOST_CHECK(g.localBinsFromGlobalBin(9) == indices({{1, 4}}));
0186   BOOST_CHECK(g.localBinsFromGlobalBin(10) == indices({{2, 0}}));
0187   BOOST_CHECK(g.localBinsFromGlobalBin(11) == indices({{2, 1}}));
0188   BOOST_CHECK(g.localBinsFromGlobalBin(12) == indices({{2, 2}}));
0189   BOOST_CHECK(g.localBinsFromGlobalBin(13) == indices({{2, 3}}));
0190   BOOST_CHECK(g.localBinsFromGlobalBin(14) == indices({{2, 4}}));
0191   BOOST_CHECK(g.localBinsFromGlobalBin(15) == indices({{3, 0}}));
0192   BOOST_CHECK(g.localBinsFromGlobalBin(16) == indices({{3, 1}}));
0193   BOOST_CHECK(g.localBinsFromGlobalBin(17) == indices({{3, 2}}));
0194   BOOST_CHECK(g.localBinsFromGlobalBin(18) == indices({{3, 3}}));
0195   BOOST_CHECK(g.localBinsFromGlobalBin(19) == indices({{3, 4}}));
0196   BOOST_CHECK(g.localBinsFromGlobalBin(20) == indices({{4, 0}}));
0197   BOOST_CHECK(g.localBinsFromGlobalBin(21) == indices({{4, 1}}));
0198   BOOST_CHECK(g.localBinsFromGlobalBin(22) == indices({{4, 2}}));
0199   BOOST_CHECK(g.localBinsFromGlobalBin(23) == indices({{4, 3}}));
0200   BOOST_CHECK(g.localBinsFromGlobalBin(24) == indices({{4, 4}}));
0201   BOOST_CHECK(g.localBinsFromGlobalBin(25) == indices({{5, 0}}));
0202   BOOST_CHECK(g.localBinsFromGlobalBin(26) == indices({{5, 1}}));
0203   BOOST_CHECK(g.localBinsFromGlobalBin(27) == indices({{5, 2}}));
0204   BOOST_CHECK(g.localBinsFromGlobalBin(28) == indices({{5, 3}}));
0205   BOOST_CHECK(g.localBinsFromGlobalBin(29) == indices({{5, 4}}));
0206 
0207   // local bin indices -> global bin index
0208   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{0, 0}}), 0u);
0209   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{0, 1}}), 1u);
0210   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{0, 2}}), 2u);
0211   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{0, 3}}), 3u);
0212   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{0, 4}}), 4u);
0213   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{1, 0}}), 5u);
0214   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{1, 1}}), 6u);
0215   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{1, 2}}), 7u);
0216   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{1, 3}}), 8u);
0217   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{1, 4}}), 9u);
0218   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{2, 0}}), 10u);
0219   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{2, 1}}), 11u);
0220   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{2, 2}}), 12u);
0221   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{2, 3}}), 13u);
0222   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{2, 4}}), 14u);
0223   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{3, 0}}), 15u);
0224   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{3, 1}}), 16u);
0225   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{3, 2}}), 17u);
0226   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{3, 3}}), 18u);
0227   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{3, 4}}), 19u);
0228   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{4, 0}}), 20u);
0229   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{4, 1}}), 21u);
0230   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{4, 2}}), 22u);
0231   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{4, 3}}), 23u);
0232   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{4, 4}}), 24u);
0233   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{5, 0}}), 25u);
0234   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{5, 1}}), 26u);
0235   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{5, 2}}), 27u);
0236   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{5, 3}}), 28u);
0237   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{5, 4}}), 29u);
0238 
0239   BOOST_CHECK(g.localBinsFromGlobalBin(g.globalBinFromPosition(
0240                   Point({{1.2, 0.7}}))) == indices({{2, 1}}));
0241 
0242   // inside checks
0243   BOOST_CHECK(!g.isInside(Point({{-2., -1}})));
0244   BOOST_CHECK(!g.isInside(Point({{-2., 1.}})));
0245   BOOST_CHECK(!g.isInside(Point({{-2., 5.}})));
0246   BOOST_CHECK(!g.isInside(Point({{1., -1.}})));
0247   BOOST_CHECK(!g.isInside(Point({{6., -1.}})));
0248   BOOST_CHECK(g.isInside(Point({{0.5, 1.3}})));
0249   BOOST_CHECK(!g.isInside(Point({{4., -1.}})));
0250   BOOST_CHECK(!g.isInside(Point({{4., 0.3}})));
0251   BOOST_CHECK(!g.isInside(Point({{4., 3.}})));
0252   BOOST_CHECK(!g.isInside(Point({{-1., 3.}})));
0253   BOOST_CHECK(!g.isInside(Point({{2., 3.}})));
0254   BOOST_CHECK(!g.isInside(Point({{5., 3.}})));
0255 
0256   // test some bin centers
0257   CHECK_CLOSE_ABS(g.binCenter({{1, 1}}), Point({{0.5, 0.5}}), 1e-6);
0258   CHECK_CLOSE_ABS(g.binCenter({{2, 3}}), Point({{1.5, 2.5}}), 1e-6);
0259   CHECK_CLOSE_ABS(g.binCenter({{3, 1}}), Point({{2.5, 0.5}}), 1e-6);
0260   CHECK_CLOSE_ABS(g.binCenter({{4, 2}}), Point({{3.5, 1.5}}), 1e-6);
0261 
0262   // test some lower-left bin edges
0263   CHECK_CLOSE_ABS(g.lowerLeftBinEdge({{1, 1}}), Point({{0., 0.}}), 1e-6);
0264   CHECK_CLOSE_ABS(g.lowerLeftBinEdge({{2, 3}}), Point({{1., 2.}}), 1e-6);
0265   CHECK_CLOSE_ABS(g.lowerLeftBinEdge({{3, 1}}), Point({{2., 0.}}), 1e-6);
0266   CHECK_CLOSE_ABS(g.lowerLeftBinEdge({{4, 2}}), Point({{3., 1.}}), 1e-6);
0267 
0268   // test some upper right-bin edges
0269   CHECK_CLOSE_ABS(g.upperRightBinEdge({{1, 1}}), Point({{1., 1.}}), 1e-6);
0270   CHECK_CLOSE_ABS(g.upperRightBinEdge({{2, 3}}), Point({{2., 3.}}), 1e-6);
0271   CHECK_CLOSE_ABS(g.upperRightBinEdge({{3, 1}}), Point({{3., 1.}}), 1e-6);
0272   CHECK_CLOSE_ABS(g.upperRightBinEdge({{4, 2}}), Point({{4., 2.}}), 1e-6);
0273 
0274   // initialize grid
0275   for (std::size_t bin = 0; bin < g.size(); ++bin) {
0276     g.at(bin) = bin;
0277   }
0278 
0279   // consistency of access
0280   const auto& point = Point({{0.7, 1.3}});
0281   std::size_t globalBin = g.globalBinFromPosition(point);
0282   indices localBins = g.localBinsFromGlobalBin(globalBin);
0283 
0284   BOOST_CHECK_EQUAL(g.atPosition(point), g.at(globalBin));
0285   BOOST_CHECK_EQUAL(g.atPosition(point), g.atLocalBins(localBins));
0286 }
0287 
0288 BOOST_AUTO_TEST_CASE(grid_test_3d_equidistant) {
0289   using Point = std::array<double, 3>;
0290   using indices = std::array<std::size_t, 3>;
0291   Axis a(0.0, 2.0, 2u);
0292   Axis b(0.0, 3.0, 3u);
0293   Axis c(0.0, 2.0, 2u);
0294   Grid g(Type<double>, std::move(a), std::move(b), std::move(c));
0295 
0296   // test general properties
0297   BOOST_CHECK_EQUAL(g.size(), 80u);
0298   BOOST_CHECK_EQUAL(g.numLocalBins().at(0), 2u);
0299   BOOST_CHECK_EQUAL(g.numLocalBins().at(1), 3u);
0300   BOOST_CHECK_EQUAL(g.numLocalBins().at(2), 2u);
0301 
0302   // test grid points
0303   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{0, 0, 0}})), 25u);
0304   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{0, 0, 1}})), 26u);
0305   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{0, 0, 2}})), 27u);
0306   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{0, 1, 0}})), 29u);
0307   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{0, 1, 1}})), 30u);
0308   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{0, 1, 2}})), 31u);
0309   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{0, 2, 0}})), 33u);
0310   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{0, 2, 1}})), 34u);
0311   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{0, 2, 2}})), 35u);
0312   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{0, 3, 0}})), 37u);
0313   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{0, 3, 1}})), 38u);
0314   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{0, 3, 2}})), 39u);
0315   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{1, 0, 0}})), 45u);
0316   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{1, 0, 1}})), 46u);
0317   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{1, 0, 2}})), 47u);
0318   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{1, 1, 0}})), 49u);
0319   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{1, 1, 1}})), 50u);
0320   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{1, 1, 2}})), 51u);
0321   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{1, 2, 0}})), 53u);
0322   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{1, 2, 1}})), 54u);
0323   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{1, 2, 2}})), 55u);
0324   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{1, 3, 0}})), 57u);
0325   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{1, 3, 1}})), 58u);
0326   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{1, 3, 2}})), 59u);
0327   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{2, 0, 0}})), 65u);
0328   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{2, 0, 1}})), 66u);
0329   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{2, 0, 2}})), 67u);
0330   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{2, 1, 0}})), 69u);
0331   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{2, 1, 1}})), 70u);
0332   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{2, 1, 2}})), 71u);
0333   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{2, 2, 0}})), 73u);
0334   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{2, 2, 1}})), 74u);
0335   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{2, 2, 2}})), 75u);
0336   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{2, 3, 0}})), 77u);
0337   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{2, 3, 1}})), 78u);
0338   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{2, 3, 2}})), 79u);
0339 
0340   // global bin index -> local bin indices
0341   BOOST_CHECK(g.localBinsFromGlobalBin(0) == indices({{0, 0, 0}}));
0342   BOOST_CHECK(g.localBinsFromGlobalBin(1) == indices({{0, 0, 1}}));
0343   BOOST_CHECK(g.localBinsFromGlobalBin(2) == indices({{0, 0, 2}}));
0344   BOOST_CHECK(g.localBinsFromGlobalBin(3) == indices({{0, 0, 3}}));
0345   BOOST_CHECK(g.localBinsFromGlobalBin(4) == indices({{0, 1, 0}}));
0346   BOOST_CHECK(g.localBinsFromGlobalBin(5) == indices({{0, 1, 1}}));
0347   BOOST_CHECK(g.localBinsFromGlobalBin(6) == indices({{0, 1, 2}}));
0348   BOOST_CHECK(g.localBinsFromGlobalBin(7) == indices({{0, 1, 3}}));
0349   BOOST_CHECK(g.localBinsFromGlobalBin(24) == indices({{1, 1, 0}}));
0350   BOOST_CHECK(g.localBinsFromGlobalBin(25) == indices({{1, 1, 1}}));
0351   BOOST_CHECK(g.localBinsFromGlobalBin(26) == indices({{1, 1, 2}}));
0352   BOOST_CHECK(g.localBinsFromGlobalBin(27) == indices({{1, 1, 3}}));
0353   BOOST_CHECK(g.localBinsFromGlobalBin(52) == indices({{2, 3, 0}}));
0354   BOOST_CHECK(g.localBinsFromGlobalBin(53) == indices({{2, 3, 1}}));
0355   BOOST_CHECK(g.localBinsFromGlobalBin(54) == indices({{2, 3, 2}}));
0356   BOOST_CHECK(g.localBinsFromGlobalBin(55) == indices({{2, 3, 3}}));
0357   BOOST_CHECK(g.localBinsFromGlobalBin(60) == indices({{3, 0, 0}}));
0358   BOOST_CHECK(g.localBinsFromGlobalBin(61) == indices({{3, 0, 1}}));
0359   BOOST_CHECK(g.localBinsFromGlobalBin(62) == indices({{3, 0, 2}}));
0360   BOOST_CHECK(g.localBinsFromGlobalBin(63) == indices({{3, 0, 3}}));
0361   BOOST_CHECK(g.localBinsFromGlobalBin(76) == indices({{3, 4, 0}}));
0362   BOOST_CHECK(g.localBinsFromGlobalBin(77) == indices({{3, 4, 1}}));
0363   BOOST_CHECK(g.localBinsFromGlobalBin(78) == indices({{3, 4, 2}}));
0364   BOOST_CHECK(g.localBinsFromGlobalBin(79) == indices({{3, 4, 3}}));
0365 
0366   // local bin indices -> global bin index
0367   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{0, 0, 0}}), 0u);
0368   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{0, 0, 1}}), 1u);
0369   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{0, 0, 2}}), 2u);
0370   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{0, 0, 3}}), 3u);
0371   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{0, 1, 0}}), 4u);
0372   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{0, 1, 1}}), 5u);
0373   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{0, 1, 2}}), 6u);
0374   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{0, 1, 3}}), 7u);
0375   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{1, 1, 0}}), 24u);
0376   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{1, 1, 1}}), 25u);
0377   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{1, 1, 2}}), 26u);
0378   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{1, 1, 3}}), 27u);
0379   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{2, 3, 0}}), 52u);
0380   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{2, 3, 1}}), 53u);
0381   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{2, 3, 2}}), 54u);
0382   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{2, 3, 3}}), 55u);
0383   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{3, 0, 0}}), 60u);
0384   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{3, 0, 1}}), 61u);
0385   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{3, 0, 2}}), 62u);
0386   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{3, 0, 3}}), 63u);
0387   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{3, 4, 0}}), 76u);
0388   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{3, 4, 1}}), 77u);
0389   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{3, 4, 2}}), 78u);
0390   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{3, 4, 3}}), 79u);
0391 
0392   BOOST_CHECK(g.localBinsFromGlobalBin(g.globalBinFromPosition(
0393                   Point({{1.2, 0.7, 1.4}}))) == indices({{2, 1, 2}}));
0394 
0395   // inside checks
0396   BOOST_CHECK(!g.isInside(Point({{-2., -1, -2}})));
0397   BOOST_CHECK(!g.isInside(Point({{-2., 1., 0.}})));
0398   BOOST_CHECK(!g.isInside(Point({{-2., 5., -1}})));
0399   BOOST_CHECK(!g.isInside(Point({{1., -1., 1.}})));
0400   BOOST_CHECK(!g.isInside(Point({{6., -1., 4.}})));
0401   BOOST_CHECK(g.isInside(Point({{0.5, 1.3, 1.7}})));
0402   BOOST_CHECK(!g.isInside(Point({{2., -1., -0.4}})));
0403   BOOST_CHECK(!g.isInside(Point({{2., 0.3, 3.4}})));
0404   BOOST_CHECK(!g.isInside(Point({{2., 3., 0.8}})));
0405   BOOST_CHECK(!g.isInside(Point({{-1., 3., 5.}})));
0406   BOOST_CHECK(!g.isInside(Point({{2., 3., -1.}})));
0407   BOOST_CHECK(!g.isInside(Point({{5., 3., 0.5}})));
0408 
0409   // test some bin centers
0410   CHECK_CLOSE_ABS(g.binCenter({{1, 1, 1}}), Point({{0.5, 0.5, 0.5}}), 1e-6);
0411   CHECK_CLOSE_ABS(g.binCenter({{2, 3, 2}}), Point({{1.5, 2.5, 1.5}}), 1e-6);
0412   CHECK_CLOSE_ABS(g.binCenter({{1, 1, 2}}), Point({{0.5, 0.5, 1.5}}), 1e-6);
0413   CHECK_CLOSE_ABS(g.binCenter({{2, 2, 1}}), Point({{1.5, 1.5, 0.5}}), 1e-6);
0414 
0415   // test some lower-left bin edges
0416   CHECK_CLOSE_ABS(g.lowerLeftBinEdge({{1, 1, 1}}), Point({{0., 0., 0.}}), 1e-6);
0417   CHECK_CLOSE_ABS(g.lowerLeftBinEdge({{2, 3, 2}}), Point({{1., 2., 1.}}), 1e-6);
0418   CHECK_CLOSE_ABS(g.lowerLeftBinEdge({{1, 1, 2}}), Point({{0., 0., 1.}}), 1e-6);
0419   CHECK_CLOSE_ABS(g.lowerLeftBinEdge({{2, 2, 1}}), Point({{1., 1., 0.}}), 1e-6);
0420 
0421   // test some upper right-bin edges
0422   CHECK_CLOSE_ABS(g.upperRightBinEdge({{1, 1, 1}}), Point({{1., 1., 1.}}),
0423                   1e-6);
0424   CHECK_CLOSE_ABS(g.upperRightBinEdge({{2, 3, 2}}), Point({{2., 3., 2.}}),
0425                   1e-6);
0426   CHECK_CLOSE_ABS(g.upperRightBinEdge({{1, 1, 2}}), Point({{1., 1., 2.}}),
0427                   1e-6);
0428   CHECK_CLOSE_ABS(g.upperRightBinEdge({{2, 2, 1}}), Point({{2., 2., 1.}}),
0429                   1e-6);
0430 
0431   // initialize grid
0432   for (std::size_t bin = 0; bin < g.size(); ++bin) {
0433     g.at(bin) = bin;
0434   }
0435 
0436   // consistency of access
0437   const auto& point = Point({{0.7, 2.3, 1.3}});
0438   std::size_t globalBin = g.globalBinFromPosition(point);
0439   indices localBins = g.localBinsFromGlobalBin(globalBin);
0440 
0441   BOOST_CHECK_EQUAL(g.atPosition(point), g.at(globalBin));
0442   BOOST_CHECK_EQUAL(g.atPosition(point), g.atLocalBins(localBins));
0443 }
0444 
0445 BOOST_AUTO_TEST_CASE(grid_test_1d_variable) {
0446   using Point = std::array<double, 1>;
0447   using indices = std::array<std::size_t, 1>;
0448   Axis a({0.0, 1.0, 4.0});
0449   Grid g(Type<double>, std::move(a));
0450 
0451   // test general properties
0452   BOOST_CHECK_EQUAL(g.size(), 4u);
0453   BOOST_CHECK_EQUAL(g.numLocalBins().at(0), 2u);
0454 
0455   // global bin index
0456   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{-0.3}})), 0u);
0457   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{0.}})), 1u);
0458   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{0.7}})), 1u);
0459   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{1}})), 2u);
0460   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{1.2}})), 2u);
0461   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{2.7}})), 2u);
0462   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{4.}})), 3u);
0463   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{4.98}})), 3u);
0464 
0465   // global bin index -> local bin indices
0466   BOOST_CHECK(g.localBinsFromGlobalBin(0) == indices({{0}}));
0467   BOOST_CHECK(g.localBinsFromGlobalBin(1) == indices({{1}}));
0468   BOOST_CHECK(g.localBinsFromGlobalBin(2) == indices({{2}}));
0469   BOOST_CHECK(g.localBinsFromGlobalBin(3) == indices({{3}}));
0470 
0471   // local bin indices -> global bin index
0472   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{0}}), 0u);
0473   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{1}}), 1u);
0474   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{2}}), 2u);
0475   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{3}}), 3u);
0476 
0477   BOOST_CHECK(g.localBinsFromGlobalBin(
0478                   g.globalBinFromPosition(Point({{0.8}}))) == indices({{1}}));
0479 
0480   // inside checks
0481   BOOST_CHECK(!g.isInside(Point({{-2.}})));
0482   BOOST_CHECK(g.isInside(Point({{0.}})));
0483   BOOST_CHECK(g.isInside(Point({{2.5}})));
0484   BOOST_CHECK(!g.isInside(Point({{4.}})));
0485   BOOST_CHECK(!g.isInside(Point({{6.}})));
0486 
0487   // test some bin centers
0488   CHECK_CLOSE_ABS(g.binCenter({{1}}), Point({{0.5}}), 1e-6);
0489   CHECK_CLOSE_ABS(g.binCenter({{2}}), Point({{2.5}}), 1e-6);
0490 
0491   // test some lower-left bin edges
0492   CHECK_CLOSE_ABS(g.lowerLeftBinEdge({{1}}), Point({{0.}}), 1e-6);
0493   CHECK_CLOSE_ABS(g.lowerLeftBinEdge({{2}}), Point({{1.}}), 1e-6);
0494 
0495   // test some upper right-bin edges
0496   CHECK_CLOSE_ABS(g.upperRightBinEdge({{1}}), Point({{1.}}), 1e-6);
0497   CHECK_CLOSE_ABS(g.upperRightBinEdge({{2}}), Point({{4.}}), 1e-6);
0498 
0499   // initialize grid
0500   for (std::size_t bin = 0; bin < g.size(); ++bin) {
0501     g.at(bin) = bin;
0502   }
0503 
0504   // consistency of access
0505   const auto& point = Point({{0.7}});
0506   std::size_t globalBin = g.globalBinFromPosition(point);
0507   indices localBins = g.localBinsFromGlobalBin(globalBin);
0508 
0509   BOOST_CHECK_EQUAL(g.atPosition(point), g.at(globalBin));
0510   BOOST_CHECK_EQUAL(g.atPosition(point), g.atLocalBins(localBins));
0511 }
0512 
0513 BOOST_AUTO_TEST_CASE(grid_test_2d_variable) {
0514   using Point = std::array<double, 2>;
0515   using indices = std::array<std::size_t, 2>;
0516   Axis a({0.0, 0.5, 3.0});
0517   Axis b({0.0, 1.0, 4.0});
0518   Grid g(Type<double>, std::move(a), std::move(b));
0519 
0520   // test general properties
0521   BOOST_CHECK_EQUAL(g.size(), 16u);
0522   BOOST_CHECK_EQUAL(g.numLocalBins().at(0), 2u);
0523   BOOST_CHECK_EQUAL(g.numLocalBins().at(1), 2u);
0524 
0525   // test grid points
0526   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{0, 0}})), 5u);
0527   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{0, 1}})), 6u);
0528   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{0, 4}})), 7u);
0529   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{0.5, 0}})), 9u);
0530   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{0.5, 1}})), 10u);
0531   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{0.5, 4}})), 11u);
0532   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{3, 0}})), 13u);
0533   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{3, 1}})), 14u);
0534   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{3, 4}})), 15u);
0535 
0536   // test some arbitrary points
0537   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{0.3, 1.2}})), 6u);
0538   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{3.3, 2.2}})), 14u);
0539   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{1.8, 0.9}})), 9u);
0540   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{3.1, 0.7}})), 13u);
0541   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{2.3, 1.4}})), 10u);
0542   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{2, -3}})), 8u);
0543   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{1, 8}})), 11u);
0544   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{-3, 1}})), 2u);
0545   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{11, 3}})), 14u);
0546   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{-3, -2}})), 0u);
0547   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{7, -2}})), 12u);
0548   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{-1, 12}})), 3u);
0549   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{11, 12}})), 15u);
0550 
0551   // global bin index -> local bin indices
0552   BOOST_CHECK(g.localBinsFromGlobalBin(0) == indices({{0, 0}}));
0553   BOOST_CHECK(g.localBinsFromGlobalBin(1) == indices({{0, 1}}));
0554   BOOST_CHECK(g.localBinsFromGlobalBin(2) == indices({{0, 2}}));
0555   BOOST_CHECK(g.localBinsFromGlobalBin(3) == indices({{0, 3}}));
0556   BOOST_CHECK(g.localBinsFromGlobalBin(4) == indices({{1, 0}}));
0557   BOOST_CHECK(g.localBinsFromGlobalBin(5) == indices({{1, 1}}));
0558   BOOST_CHECK(g.localBinsFromGlobalBin(6) == indices({{1, 2}}));
0559   BOOST_CHECK(g.localBinsFromGlobalBin(7) == indices({{1, 3}}));
0560   BOOST_CHECK(g.localBinsFromGlobalBin(8) == indices({{2, 0}}));
0561   BOOST_CHECK(g.localBinsFromGlobalBin(9) == indices({{2, 1}}));
0562   BOOST_CHECK(g.localBinsFromGlobalBin(10) == indices({{2, 2}}));
0563   BOOST_CHECK(g.localBinsFromGlobalBin(11) == indices({{2, 3}}));
0564   BOOST_CHECK(g.localBinsFromGlobalBin(12) == indices({{3, 0}}));
0565   BOOST_CHECK(g.localBinsFromGlobalBin(13) == indices({{3, 1}}));
0566   BOOST_CHECK(g.localBinsFromGlobalBin(14) == indices({{3, 2}}));
0567   BOOST_CHECK(g.localBinsFromGlobalBin(15) == indices({{3, 3}}));
0568 
0569   // local bin indices -> global bin index
0570   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{0, 0}}), 0u);
0571   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{0, 1}}), 1u);
0572   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{0, 2}}), 2u);
0573   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{0, 3}}), 3u);
0574   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{1, 0}}), 4u);
0575   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{1, 1}}), 5u);
0576   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{1, 2}}), 6u);
0577   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{1, 3}}), 7u);
0578   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{2, 0}}), 8u);
0579   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{2, 1}}), 9u);
0580   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{2, 2}}), 10u);
0581   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{2, 3}}), 11u);
0582   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{3, 0}}), 12u);
0583   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{3, 1}}), 13u);
0584   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{3, 2}}), 14u);
0585   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{3, 3}}), 15u);
0586 
0587   BOOST_CHECK(g.localBinsFromGlobalBin(g.globalBinFromPosition(
0588                   Point({{3.2, 1.8}}))) == indices({{3, 2}}));
0589 
0590   // inside checks
0591   BOOST_CHECK(!g.isInside(Point({{-2., -1}})));
0592   BOOST_CHECK(!g.isInside(Point({{-2., 1.}})));
0593   BOOST_CHECK(!g.isInside(Point({{-2., 5.}})));
0594   BOOST_CHECK(!g.isInside(Point({{1., -1.}})));
0595   BOOST_CHECK(!g.isInside(Point({{6., -1.}})));
0596   BOOST_CHECK(g.isInside(Point({{0.5, 1.3}})));
0597   BOOST_CHECK(!g.isInside(Point({{3., -1.}})));
0598   BOOST_CHECK(!g.isInside(Point({{3., 0.3}})));
0599   BOOST_CHECK(!g.isInside(Point({{3., 4.}})));
0600   BOOST_CHECK(!g.isInside(Point({{-1., 4.}})));
0601   BOOST_CHECK(!g.isInside(Point({{2., 4.}})));
0602   BOOST_CHECK(!g.isInside(Point({{5., 4.}})));
0603 
0604   // test some bin centers
0605   CHECK_CLOSE_ABS(g.binCenter({{1, 1}}), Point({{0.25, 0.5}}), 1e-6);
0606   CHECK_CLOSE_ABS(g.binCenter({{2, 1}}), Point({{1.75, 0.5}}), 1e-6);
0607   CHECK_CLOSE_ABS(g.binCenter({{1, 2}}), Point({{0.25, 2.5}}), 1e-6);
0608   CHECK_CLOSE_ABS(g.binCenter({{2, 2}}), Point({{1.75, 2.5}}), 1e-6);
0609 
0610   // test some lower-left bin edges
0611   CHECK_CLOSE_ABS(g.lowerLeftBinEdge({{1, 1}}), Point({{0., 0.}}), 1e-6);
0612   CHECK_CLOSE_ABS(g.lowerLeftBinEdge({{2, 1}}), Point({{0.5, 0.}}), 1e-6);
0613   CHECK_CLOSE_ABS(g.lowerLeftBinEdge({{1, 2}}), Point({{0., 1.}}), 1e-6);
0614   CHECK_CLOSE_ABS(g.lowerLeftBinEdge({{2, 2}}), Point({{0.5, 1.}}), 1e-6);
0615 
0616   // test some upper right-bin edges
0617   CHECK_CLOSE_ABS(g.upperRightBinEdge({{1, 1}}), Point({{0.5, 1.}}), 1e-6);
0618   CHECK_CLOSE_ABS(g.upperRightBinEdge({{2, 1}}), Point({{3., 1.}}), 1e-6);
0619   CHECK_CLOSE_ABS(g.upperRightBinEdge({{1, 2}}), Point({{0.5, 4.}}), 1e-6);
0620   CHECK_CLOSE_ABS(g.upperRightBinEdge({{2, 2}}), Point({{3., 4.}}), 1e-6);
0621 
0622   // initialize grid
0623   for (std::size_t bin = 0; bin < g.size(); ++bin) {
0624     g.at(bin) = bin;
0625   }
0626 
0627   // consistency of access
0628   const auto& point = Point({{0.7, 1.3}});
0629   std::size_t globalBin = g.globalBinFromPosition(point);
0630   indices localBins = g.localBinsFromGlobalBin(globalBin);
0631 
0632   BOOST_CHECK_EQUAL(g.atPosition(point), g.at(globalBin));
0633   BOOST_CHECK_EQUAL(g.atPosition(point), g.atLocalBins(localBins));
0634 }
0635 
0636 BOOST_AUTO_TEST_CASE(grid_test_3d_variable) {
0637   using Point = std::array<double, 3>;
0638   using indices = std::array<std::size_t, 3>;
0639   Axis a({0.0, 1.0});
0640   Axis b({0.0, 0.5, 3.0});
0641   Axis c({0.0, 0.5, 3.0, 3.3});
0642   Grid g(Type<double>, std::move(a), std::move(b), std::move(c));
0643 
0644   // test general properties
0645   BOOST_CHECK_EQUAL(g.size(), 60u);
0646   BOOST_CHECK_EQUAL(g.numLocalBins().at(0), 1u);
0647   BOOST_CHECK_EQUAL(g.numLocalBins().at(1), 2u);
0648   BOOST_CHECK_EQUAL(g.numLocalBins().at(2), 3u);
0649 
0650   // test grid points
0651   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{0, 0, 0}})), 26u);
0652   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{1, 0, 0}})), 46u);
0653   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{0, 0.5, 0}})), 31u);
0654   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{1, 0.5, 0}})), 51u);
0655   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{0, 3, 0}})), 36u);
0656   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{1, 3, 0}})), 56u);
0657   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{0, 0, 0.5}})), 27u);
0658   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{1, 0, 0.5}})), 47u);
0659   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{0, 0.5, 0.5}})), 32u);
0660   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{1, 0.5, 0.5}})), 52u);
0661   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{0, 3, 0.5}})), 37u);
0662   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{1, 3, 0.5}})), 57u);
0663   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{0, 0, 3}})), 28u);
0664   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{1, 0, 3}})), 48u);
0665   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{0, 0.5, 3}})), 33u);
0666   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{1, 0.5, 3}})), 53u);
0667   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{0, 3, 3}})), 38u);
0668   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{1, 3, 3}})), 58u);
0669   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{0, 0, 3.3}})), 29u);
0670   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{1, 0, 3.3}})), 49u);
0671   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{0, 0.5, 3.3}})), 34u);
0672   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{1, 0.5, 3.3}})), 54u);
0673   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{0, 3, 3.3}})), 39u);
0674   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{1, 3, 3.3}})), 59u);
0675 
0676   // global bin index -> local bin indices
0677   BOOST_CHECK(g.localBinsFromGlobalBin(0) == indices({{0, 0, 0}}));
0678   BOOST_CHECK(g.localBinsFromGlobalBin(1) == indices({{0, 0, 1}}));
0679   BOOST_CHECK(g.localBinsFromGlobalBin(2) == indices({{0, 0, 2}}));
0680   BOOST_CHECK(g.localBinsFromGlobalBin(3) == indices({{0, 0, 3}}));
0681   BOOST_CHECK(g.localBinsFromGlobalBin(4) == indices({{0, 0, 4}}));
0682   BOOST_CHECK(g.localBinsFromGlobalBin(5) == indices({{0, 1, 0}}));
0683   BOOST_CHECK(g.localBinsFromGlobalBin(21) == indices({{1, 0, 1}}));
0684   BOOST_CHECK(g.localBinsFromGlobalBin(22) == indices({{1, 0, 2}}));
0685   BOOST_CHECK(g.localBinsFromGlobalBin(23) == indices({{1, 0, 3}}));
0686   BOOST_CHECK(g.localBinsFromGlobalBin(24) == indices({{1, 0, 4}}));
0687   BOOST_CHECK(g.localBinsFromGlobalBin(25) == indices({{1, 1, 0}}));
0688   BOOST_CHECK(g.localBinsFromGlobalBin(26) == indices({{1, 1, 1}}));
0689   BOOST_CHECK(g.localBinsFromGlobalBin(57) == indices({{2, 3, 2}}));
0690   BOOST_CHECK(g.localBinsFromGlobalBin(58) == indices({{2, 3, 3}}));
0691   BOOST_CHECK(g.localBinsFromGlobalBin(59) == indices({{2, 3, 4}}));
0692 
0693   // local bin indices -> global bin index
0694   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{0, 0, 0}}), 0u);
0695   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{1, 0, 0}}), 20u);
0696   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{2, 0, 0}}), 40u);
0697   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{0, 1, 0}}), 5u);
0698   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{1, 1, 0}}), 25u);
0699   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{2, 1, 0}}), 45u);
0700   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{0, 3, 1}}), 16u);
0701   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{1, 3, 1}}), 36u);
0702   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{2, 3, 1}}), 56u);
0703   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{0, 0, 2}}), 2u);
0704   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{1, 0, 2}}), 22u);
0705   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{2, 0, 2}}), 42u);
0706   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{0, 3, 4}}), 19u);
0707   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{1, 3, 4}}), 39u);
0708   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{2, 3, 4}}), 59u);
0709 
0710   BOOST_CHECK(g.localBinsFromGlobalBin(g.globalBinFromPosition(
0711                   Point({{1.8, 0.7, 3.2}}))) == indices({{2, 2, 3}}));
0712 
0713   // inside checks
0714   BOOST_CHECK(!g.isInside(Point({{-2., -1, -2}})));
0715   BOOST_CHECK(!g.isInside(Point({{-2., 1., 0.}})));
0716   BOOST_CHECK(!g.isInside(Point({{-2., 5., -1}})));
0717   BOOST_CHECK(!g.isInside(Point({{1., -1., 1.}})));
0718   BOOST_CHECK(!g.isInside(Point({{6., -1., 4.}})));
0719   BOOST_CHECK(g.isInside(Point({{0.5, 1.3, 1.7}})));
0720   BOOST_CHECK(!g.isInside(Point({{1., -1., -0.4}})));
0721   BOOST_CHECK(!g.isInside(Point({{1., 0.3, 3.4}})));
0722   BOOST_CHECK(!g.isInside(Point({{1., 3., 0.8}})));
0723   BOOST_CHECK(!g.isInside(Point({{-1., 3., 5.}})));
0724   BOOST_CHECK(!g.isInside(Point({{2., 3., -1.}})));
0725   BOOST_CHECK(!g.isInside(Point({{5., 3., 0.5}})));
0726 
0727   // test some bin centers
0728   CHECK_CLOSE_ABS(g.binCenter({{1, 1, 1}}), Point({{0.5, 0.25, 0.25}}), 1e-6);
0729   CHECK_CLOSE_ABS(g.binCenter({{1, 1, 2}}), Point({{0.5, 0.25, 1.75}}), 1e-6);
0730   CHECK_CLOSE_ABS(g.binCenter({{1, 1, 3}}), Point({{0.5, 0.25, 3.15}}), 1e-6);
0731   CHECK_CLOSE_ABS(g.binCenter({{1, 2, 1}}), Point({{0.5, 1.75, 0.25}}), 1e-6);
0732   CHECK_CLOSE_ABS(g.binCenter({{1, 2, 2}}), Point({{0.5, 1.75, 1.75}}), 1e-6);
0733   CHECK_CLOSE_ABS(g.binCenter({{1, 2, 3}}), Point({{0.5, 1.75, 3.15}}), 1e-6);
0734 
0735   // test some lower-left bin edges
0736   CHECK_CLOSE_ABS(g.lowerLeftBinEdge({{1, 1, 1}}), Point({{0., 0., 0.}}), 1e-6);
0737   CHECK_CLOSE_ABS(g.lowerLeftBinEdge({{1, 1, 2}}), Point({{0., 0., 0.5}}),
0738                   1e-6);
0739   CHECK_CLOSE_ABS(g.lowerLeftBinEdge({{1, 1, 3}}), Point({{0., 0., 3.}}), 1e-6);
0740   CHECK_CLOSE_ABS(g.lowerLeftBinEdge({{1, 2, 1}}), Point({{0., 0.5, 0.}}),
0741                   1e-6);
0742   CHECK_CLOSE_ABS(g.lowerLeftBinEdge({{1, 2, 2}}), Point({{0., 0.5, 0.5}}),
0743                   1e-6);
0744   CHECK_CLOSE_ABS(g.lowerLeftBinEdge({{1, 2, 3}}), Point({{0., 0.5, 3.}}),
0745                   1e-6);
0746 
0747   // test some upper right-bin edges
0748   CHECK_CLOSE_ABS(g.upperRightBinEdge({{1, 1, 1}}), Point({{1., 0.5, 0.5}}),
0749                   1e-6);
0750   CHECK_CLOSE_ABS(g.upperRightBinEdge({{1, 1, 2}}), Point({{1., 0.5, 3.}}),
0751                   1e-6);
0752   CHECK_CLOSE_ABS(g.upperRightBinEdge({{1, 1, 3}}), Point({{1., 0.5, 3.3}}),
0753                   1e-6);
0754   CHECK_CLOSE_ABS(g.upperRightBinEdge({{1, 2, 1}}), Point({{1., 3., 0.5}}),
0755                   1e-6);
0756   CHECK_CLOSE_ABS(g.upperRightBinEdge({{1, 2, 2}}), Point({{1., 3., 3.}}),
0757                   1e-6);
0758   CHECK_CLOSE_ABS(g.upperRightBinEdge({{1, 2, 3}}), Point({{1., 3., 3.3}}),
0759                   1e-6);
0760 
0761   // initialize grid
0762   for (std::size_t bin = 0; bin < g.size(); ++bin) {
0763     g.at(bin) = bin;
0764   }
0765 
0766   // consistency of access
0767   const auto& point = Point({{0.7, 1.3, 3.7}});
0768   std::size_t globalBin = g.globalBinFromPosition(point);
0769   indices localBins = g.localBinsFromGlobalBin(globalBin);
0770 
0771   BOOST_CHECK_EQUAL(g.atPosition(point), g.at(globalBin));
0772   BOOST_CHECK_EQUAL(g.atPosition(point), g.atLocalBins(localBins));
0773 }
0774 
0775 BOOST_AUTO_TEST_CASE(grid_test_2d_mixed) {
0776   using Point = std::array<double, 2>;
0777   using indices = std::array<std::size_t, 2>;
0778   Axis a(0.0, 1.0, 4u);
0779   Axis b({0.0, 0.5, 3.0});
0780   Grid g(Type<double>, std::move(a), std::move(b));
0781 
0782   // test general properties
0783   BOOST_CHECK_EQUAL(g.size(), 24u);
0784   BOOST_CHECK_EQUAL(g.numLocalBins().at(0), 4u);
0785   BOOST_CHECK_EQUAL(g.numLocalBins().at(1), 2u);
0786 
0787   // test grid points
0788   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{0, 0}})), 5u);
0789   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{0.25, 0}})), 9u);
0790   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{0.5, 0}})), 13u);
0791   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{0.75, 0}})), 17u);
0792   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{1, 0}})), 21u);
0793   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{0, 0.5}})), 6u);
0794   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{0.25, 0.5}})), 10u);
0795   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{0.5, 0.5}})), 14u);
0796   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{0.75, 0.5}})), 18u);
0797   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{1, 0.5}})), 22u);
0798   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{0, 3}})), 7u);
0799   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{0.25, 3}})), 11u);
0800   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{0.5, 3}})), 15u);
0801   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{0.75, 3}})), 19u);
0802   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{1, 3}})), 23u);
0803 
0804   // test some arbitrary points
0805   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{1.2, 0.3}})), 21u);
0806   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{0.2, 1.3}})), 6u);
0807   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{0.9, 1.8}})), 18u);
0808   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{0.7, 2.1}})), 14u);
0809   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{0.4, 0.3}})), 9u);
0810   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{-3, 2}})), 2u);
0811   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{8, 1}})), 22u);
0812   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{0.1, -3}})), 4u);
0813   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{0.8, 11}})), 19u);
0814   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{-2, -3}})), 0u);
0815   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{-2, 7}})), 3u);
0816   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{12, -1}})), 20u);
0817   BOOST_CHECK_EQUAL(g.globalBinFromPosition(Point({{12, 11}})), 23u);
0818 
0819   // global bin index -> local bin indices
0820   using indices = std::array<std::size_t, 2>;
0821   BOOST_CHECK(g.localBinsFromGlobalBin(0) == indices({{0, 0}}));
0822   BOOST_CHECK(g.localBinsFromGlobalBin(1) == indices({{0, 1}}));
0823   BOOST_CHECK(g.localBinsFromGlobalBin(2) == indices({{0, 2}}));
0824   BOOST_CHECK(g.localBinsFromGlobalBin(3) == indices({{0, 3}}));
0825   BOOST_CHECK(g.localBinsFromGlobalBin(4) == indices({{1, 0}}));
0826   BOOST_CHECK(g.localBinsFromGlobalBin(5) == indices({{1, 1}}));
0827   BOOST_CHECK(g.localBinsFromGlobalBin(6) == indices({{1, 2}}));
0828   BOOST_CHECK(g.localBinsFromGlobalBin(7) == indices({{1, 3}}));
0829   BOOST_CHECK(g.localBinsFromGlobalBin(8) == indices({{2, 0}}));
0830   BOOST_CHECK(g.localBinsFromGlobalBin(9) == indices({{2, 1}}));
0831   BOOST_CHECK(g.localBinsFromGlobalBin(10) == indices({{2, 2}}));
0832   BOOST_CHECK(g.localBinsFromGlobalBin(11) == indices({{2, 3}}));
0833   BOOST_CHECK(g.localBinsFromGlobalBin(12) == indices({{3, 0}}));
0834   BOOST_CHECK(g.localBinsFromGlobalBin(13) == indices({{3, 1}}));
0835   BOOST_CHECK(g.localBinsFromGlobalBin(14) == indices({{3, 2}}));
0836   BOOST_CHECK(g.localBinsFromGlobalBin(15) == indices({{3, 3}}));
0837   BOOST_CHECK(g.localBinsFromGlobalBin(16) == indices({{4, 0}}));
0838   BOOST_CHECK(g.localBinsFromGlobalBin(17) == indices({{4, 1}}));
0839   BOOST_CHECK(g.localBinsFromGlobalBin(18) == indices({{4, 2}}));
0840   BOOST_CHECK(g.localBinsFromGlobalBin(19) == indices({{4, 3}}));
0841   BOOST_CHECK(g.localBinsFromGlobalBin(20) == indices({{5, 0}}));
0842   BOOST_CHECK(g.localBinsFromGlobalBin(21) == indices({{5, 1}}));
0843   BOOST_CHECK(g.localBinsFromGlobalBin(22) == indices({{5, 2}}));
0844   BOOST_CHECK(g.localBinsFromGlobalBin(23) == indices({{5, 3}}));
0845 
0846   // local bin indices -> global bin index
0847   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{0, 0}}), 0u);
0848   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{0, 1}}), 1u);
0849   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{0, 2}}), 2u);
0850   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{0, 3}}), 3u);
0851   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{1, 0}}), 4u);
0852   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{1, 1}}), 5u);
0853   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{1, 2}}), 6u);
0854   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{1, 3}}), 7u);
0855   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{2, 0}}), 8u);
0856   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{2, 1}}), 9u);
0857   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{2, 2}}), 10u);
0858   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{2, 3}}), 11u);
0859   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{3, 0}}), 12u);
0860   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{3, 1}}), 13u);
0861   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{3, 2}}), 14u);
0862   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{3, 3}}), 15u);
0863   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{4, 0}}), 16u);
0864   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{4, 1}}), 17u);
0865   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{4, 2}}), 18u);
0866   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{4, 3}}), 19u);
0867   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{5, 0}}), 20u);
0868   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{5, 1}}), 21u);
0869   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{5, 2}}), 22u);
0870   BOOST_CHECK_EQUAL(g.globalBinFromLocalBins({{5, 3}}), 23u);
0871 
0872   BOOST_CHECK(g.localBinsFromGlobalBin(g.globalBinFromPosition(
0873                   Point({{1.1, 1.7}}))) == indices({{5, 2}}));
0874 
0875   // inside checks
0876   BOOST_CHECK(!g.isInside(Point({{-2., -1}})));
0877   BOOST_CHECK(!g.isInside(Point({{-2., 1.}})));
0878   BOOST_CHECK(!g.isInside(Point({{-2., 5.}})));
0879   BOOST_CHECK(!g.isInside(Point({{0.1, -1.}})));
0880   BOOST_CHECK(!g.isInside(Point({{6., -1.}})));
0881   BOOST_CHECK(g.isInside(Point({{0.5, 1.3}})));
0882   BOOST_CHECK(!g.isInside(Point({{1., -1.}})));
0883   BOOST_CHECK(!g.isInside(Point({{1., 0.3}})));
0884   BOOST_CHECK(!g.isInside(Point({{1., 3.}})));
0885   BOOST_CHECK(!g.isInside(Point({{-1., 3.}})));
0886   BOOST_CHECK(!g.isInside(Point({{0.2, 3.}})));
0887   BOOST_CHECK(!g.isInside(Point({{5., 3.}})));
0888 
0889   // test some bin centers
0890   CHECK_CLOSE_ABS(g.binCenter({{1, 1}}), Point({{0.125, 0.25}}), 1e-6);
0891   CHECK_CLOSE_ABS(g.binCenter({{1, 2}}), Point({{0.125, 1.75}}), 1e-6);
0892   CHECK_CLOSE_ABS(g.binCenter({{2, 1}}), Point({{0.375, 0.25}}), 1e-6);
0893   CHECK_CLOSE_ABS(g.binCenter({{2, 2}}), Point({{0.375, 1.75}}), 1e-6);
0894   CHECK_CLOSE_ABS(g.binCenter({{3, 1}}), Point({{0.625, 0.25}}), 1e-6);
0895   CHECK_CLOSE_ABS(g.binCenter({{3, 2}}), Point({{0.625, 1.75}}), 1e-6);
0896   CHECK_CLOSE_ABS(g.binCenter({{4, 1}}), Point({{0.875, 0.25}}), 1e-6);
0897   CHECK_CLOSE_ABS(g.binCenter({{4, 2}}), Point({{0.875, 1.75}}), 1e-6);
0898 
0899   // test some lower-left bin edges
0900   CHECK_CLOSE_ABS(g.lowerLeftBinEdge({{1, 1}}), Point({{0., 0.}}), 1e-6);
0901   CHECK_CLOSE_ABS(g.lowerLeftBinEdge({{1, 2}}), Point({{0., 0.5}}), 1e-6);
0902   CHECK_CLOSE_ABS(g.lowerLeftBinEdge({{2, 1}}), Point({{0.25, 0.}}), 1e-6);
0903   CHECK_CLOSE_ABS(g.lowerLeftBinEdge({{2, 2}}), Point({{0.25, 0.5}}), 1e-6);
0904   CHECK_CLOSE_ABS(g.lowerLeftBinEdge({{3, 1}}), Point({{0.5, 0.}}), 1e-6);
0905   CHECK_CLOSE_ABS(g.lowerLeftBinEdge({{3, 2}}), Point({{0.5, 0.5}}), 1e-6);
0906   CHECK_CLOSE_ABS(g.lowerLeftBinEdge({{4, 1}}), Point({{0.75, 0.}}), 1e-6);
0907   CHECK_CLOSE_ABS(g.lowerLeftBinEdge({{4, 2}}), Point({{0.75, 0.5}}), 1e-6);
0908 
0909   // test some upper-right bin edges
0910   CHECK_CLOSE_ABS(g.upperRightBinEdge({{1, 1}}), Point({{0.25, 0.5}}), 1e-6);
0911   CHECK_CLOSE_ABS(g.upperRightBinEdge({{1, 2}}), Point({{0.25, 3.}}), 1e-6);
0912   CHECK_CLOSE_ABS(g.upperRightBinEdge({{2, 1}}), Point({{0.5, 0.5}}), 1e-6);
0913   CHECK_CLOSE_ABS(g.upperRightBinEdge({{2, 2}}), Point({{0.5, 3.}}), 1e-6);
0914   CHECK_CLOSE_ABS(g.upperRightBinEdge({{3, 1}}), Point({{0.75, 0.5}}), 1e-6);
0915   CHECK_CLOSE_ABS(g.upperRightBinEdge({{3, 2}}), Point({{0.75, 3.}}), 1e-6);
0916   CHECK_CLOSE_ABS(g.upperRightBinEdge({{4, 1}}), Point({{1., 0.5}}), 1e-6);
0917   CHECK_CLOSE_ABS(g.upperRightBinEdge({{4, 2}}), Point({{1., 3.}}), 1e-6);
0918 
0919   // initialize grid
0920   for (std::size_t bin = 0; bin < g.size(); ++bin) {
0921     g.at(bin) = bin;
0922   }
0923 
0924   // consistency of access
0925   const auto& point = Point({{1.3, 3.7}});
0926   std::size_t globalBin = g.globalBinFromPosition(point);
0927   indices localBins = g.localBinsFromGlobalBin(globalBin);
0928 
0929   BOOST_CHECK_EQUAL(g.atPosition(point), g.at(globalBin));
0930   BOOST_CHECK_EQUAL(g.atPosition(point), g.atLocalBins(localBins));
0931 }
0932 
0933 BOOST_AUTO_TEST_CASE(grid_test_2d_mixed_at) {
0934   Axis a(0.0, 6.0, 4u);
0935   Axis b({0.0, 1.5, 3.0});
0936   Grid g(Type<double>, std::move(a), std::move(b));
0937 
0938   // initialize the grid
0939   using Point = std::array<double, 2>;
0940   g.atPosition(Point({{0, 0}})) = 0.;
0941   g.atPosition(Point({{1.5, 0}})) = 1.;
0942   g.atPosition(Point({{3, 0}})) = 2.;
0943   g.atPosition(Point({{4.5, 0}})) = 3.;
0944   g.atPosition(Point({{6, 0}})) = 4.;
0945   g.atPosition(Point({{0, 1.5}})) = 5.;
0946   g.atPosition(Point({{1.5, 1.5}})) = 6.;
0947   g.atPosition(Point({{3, 1.5}})) = 7.;
0948   g.atPosition(Point({{4.5, 1.5}})) = 8.;
0949   g.atPosition(Point({{6, 1.5}})) = 9.;
0950   g.atPosition(Point({{0, 3}})) = 10.;
0951   g.atPosition(Point({{1.5, 3}})) = 11.;
0952   g.atPosition(Point({{3, 3}})) = 12.;
0953   g.atPosition(Point({{4.5, 3}})) = 13.;
0954   g.atPosition(Point({{6, 3}})) = 14.;
0955 
0956   // test general properties
0957   BOOST_CHECK_EQUAL(g.size(), 24u);
0958 
0959   // test some arbitrary points
0960   BOOST_CHECK_EQUAL(g.atPosition(Point({{1.2, 0.3}})), 0.);
0961   BOOST_CHECK_EQUAL(g.atPosition(Point({{2.2, 1.3}})), 1.);
0962   BOOST_CHECK_EQUAL(g.atPosition(Point({{4.9, 1.8}})), 8.);
0963   BOOST_CHECK_EQUAL(g.atPosition(Point({{3.7, 2.1}})), 7.);
0964   BOOST_CHECK_EQUAL(g.atPosition(Point({{0.4, 2.3}})), 5.);
0965 }
0966 
0967 BOOST_AUTO_TEST_CASE(grid_interpolation) {
0968   using Point = std::array<double, 3>;
0969   Axis a(1.0, 3.0, 2u);
0970   Axis b(1.0, 5.0, 2u);
0971   Axis c(1.0, 7.0, 2u);
0972   Grid g(Type<double>, std::move(a), std::move(b), std::move(c));
0973 
0974   g.atPosition(Point({{1., 1., 1.}})) = 10.;
0975   g.atPosition(Point({{2., 1., 1.}})) = 20.;
0976   g.atPosition(Point({{1., 3., 1.}})) = 30.;
0977   g.atPosition(Point({{2., 3., 1.}})) = 40.;
0978   g.atPosition(Point({{1., 1., 4.}})) = 50.;
0979   g.atPosition(Point({{2., 1., 4.}})) = 60.;
0980   g.atPosition(Point({{1., 3., 4.}})) = 70.;
0981   g.atPosition(Point({{2., 3., 4.}})) = 80.;
0982 
0983   CHECK_CLOSE_REL(g.interpolate(Point({{1., 1., 1.}})), 10., 1e-6);
0984   CHECK_CLOSE_REL(g.interpolate(Point({{2., 1., 1.}})), 20., 1e-6);
0985   CHECK_CLOSE_REL(g.interpolate(Point({{1., 3., 1.}})), 30., 1e-6);
0986   CHECK_CLOSE_REL(g.interpolate(Point({{2., 3., 1.}})), 40., 1e-6);
0987   CHECK_CLOSE_REL(g.interpolate(Point({{1., 1., 4.}})), 50., 1e-6);
0988   CHECK_CLOSE_REL(g.interpolate(Point({{2., 1., 4.}})), 60., 1e-6);
0989   CHECK_CLOSE_REL(g.interpolate(Point({{1., 3., 4.}})), 70., 1e-6);
0990   CHECK_CLOSE_REL(g.interpolate(Point({{2., 3., 4.}})), 80., 1e-6);
0991   CHECK_CLOSE_REL(g.interpolate(Point({{1.5, 1., 1.}})), 15., 1e-6);
0992   CHECK_CLOSE_REL(g.interpolate(Point({{1.5, 3., 1.}})), 35., 1e-6);
0993   CHECK_CLOSE_REL(g.interpolate(Point({{1., 2., 1.}})), 20., 1e-6);
0994   CHECK_CLOSE_REL(g.interpolate(Point({{2., 2., 1.}})), 30., 1e-6);
0995   CHECK_CLOSE_REL(g.interpolate(Point({{1.5, 1., 4.}})), 55., 1e-6);
0996   CHECK_CLOSE_REL(g.interpolate(Point({{1.5, 3., 4.}})), 75., 1e-6);
0997   CHECK_CLOSE_REL(g.interpolate(Point({{1., 2., 4.}})), 60., 1e-6);
0998   CHECK_CLOSE_REL(g.interpolate(Point({{2., 2., 4.}})), 70., 1e-6);
0999   CHECK_CLOSE_REL(g.interpolate(Point({{1., 1., 2.5}})), 30., 1e-6);
1000   CHECK_CLOSE_REL(g.interpolate(Point({{1., 3., 2.5}})), 50., 1e-6);
1001   CHECK_CLOSE_REL(g.interpolate(Point({{2., 1., 2.5}})), 40., 1e-6);
1002   CHECK_CLOSE_REL(g.interpolate(Point({{2., 3., 2.5}})), 60., 1e-6);
1003   CHECK_CLOSE_REL(g.interpolate(Point({{1.5, 2., 2.5}})), 360. / 8, 1e-6);
1004   CHECK_CLOSE_REL(g.interpolate(Point({{1.3, 2.1, 1.6}})), 32., 1e-6);
1005   CHECK_CLOSE_REL(g.interpolate(Point({{2., 3., 4.}})), 80., 1e-6);
1006 }
1007 
1008 BOOST_AUTO_TEST_CASE(neighborhood) {
1009   using bins_t = std::vector<std::size_t>;
1010 
1011   Axis a(0.0, 1.0, 10u);
1012   Axis b(0.0, 1.0, 10u);
1013   Axis c(0.0, 1.0, 10u);
1014   Grid g1(Type<double>, Axis{a});
1015   Grid g2(Type<double>, Axis{a}, Axis{b});
1016   Grid g3(Type<double>, std::move(a), std::move(b), std::move(c));
1017 
1018   // clang-format off
1019     // 1D case
1020     BOOST_CHECK(g1.neighborHoodIndices({{0}}, 1).collectVector()
1021                 == bins_t({0, 1}));
1022     BOOST_CHECK(g1.neighborHoodIndices({{0}}, 2).collectVector()
1023                 == bins_t({0, 1, 2}));
1024     BOOST_CHECK(g1.neighborHoodIndices({{1}}, 1).collectVector()
1025                 == bins_t({0, 1, 2}));
1026     BOOST_CHECK(g1.neighborHoodIndices({{1}}, 3).collectVector()
1027                 == bins_t({0, 1, 2, 3, 4}));
1028     BOOST_CHECK(g1.neighborHoodIndices({{4}}, 2).collectVector()
1029                 == bins_t({2, 3, 4, 5, 6}));
1030     BOOST_CHECK(g1.neighborHoodIndices({{9}}, 2).collectVector()
1031                 == bins_t({7, 8, 9, 10, 11}));
1032     BOOST_CHECK(g1.neighborHoodIndices({{10}}, 2).collectVector()
1033                 == bins_t({8, 9, 10, 11}));
1034     BOOST_CHECK(g1.neighborHoodIndices({{11}}, 2).collectVector()
1035                 == bins_t({9, 10, 11}));
1036 
1037     // 2D case
1038     BOOST_CHECK(g2.neighborHoodIndices({{0, 0}}, 1).collectVector()
1039                 == bins_t({0, 1, 12, 13}));
1040     BOOST_CHECK(g2.neighborHoodIndices({{0, 1}}, 1).collectVector()
1041                 == bins_t({0, 1, 2, 12, 13, 14}));
1042     BOOST_CHECK(g2.neighborHoodIndices({{1, 0}}, 1).collectVector()
1043                 == bins_t({0, 1, 12, 13, 24, 25}));
1044     BOOST_CHECK(g2.neighborHoodIndices({{1, 1}}, 1).collectVector()
1045                 == bins_t({0, 1, 2, 12, 13, 14, 24, 25, 26}));
1046     BOOST_CHECK(g2.neighborHoodIndices({{5, 5}}, 1).collectVector()
1047                 == bins_t({52, 53, 54, 64, 65, 66, 76, 77, 78}));
1048     BOOST_CHECK(g2.neighborHoodIndices({{9, 10}}, 2).collectVector()
1049                 == bins_t({92, 93, 94, 95, 104, 105, 106, 107, 116, 117, 118,
1050                            119, 128, 129, 130, 131, 140, 141, 142, 143}));
1051 
1052     // 3D case
1053     BOOST_CHECK(g3.neighborHoodIndices({{0, 0, 0}}, 1).collectVector()
1054                 == bins_t({0, 1, 12, 13, 144, 145, 156, 157}));
1055     BOOST_CHECK(g3.neighborHoodIndices({{0, 0, 1}}, 1).collectVector()
1056                 == bins_t({0, 1, 2, 12, 13, 14, 144, 145, 146, 156, 157, 158}));
1057     BOOST_CHECK(g3.neighborHoodIndices({{0, 1, 0}}, 1).collectVector()
1058                 == bins_t({0, 1, 12, 13, 24, 25, 144, 145, 156, 157, 168, 169}));
1059     BOOST_CHECK(g3.neighborHoodIndices({{1, 0, 0}}, 1).collectVector()
1060                 == bins_t({0, 1, 12, 13, 144, 145, 156, 157, 288, 289, 300, 301}));
1061     BOOST_CHECK(g3.neighborHoodIndices({{0, 1, 1}}, 1).collectVector()
1062                 == bins_t({0, 1, 2, 12, 13, 14, 24, 25, 26, 144, 145, 146,
1063                            156, 157, 158, 168, 169, 170}));
1064     BOOST_CHECK(g3.neighborHoodIndices({{1, 1, 1}}, 1).collectVector()
1065                 == bins_t({0, 1, 2, 12, 13, 14, 24, 25, 26, 144, 145, 146,
1066                            156, 157, 158, 168, 169, 170, 288, 289, 290, 300,
1067                            301, 302, 312, 313, 314}));
1068     BOOST_CHECK(g3.neighborHoodIndices({{11, 10, 9}}, 1).collectVector()
1069                 == bins_t({1556, 1557, 1558, 1568, 1569, 1570, 1580, 1581,
1070                            1582, 1700, 1701, 1702, 1712, 1713, 1714, 1724,
1071                            1725, 1726}));
1072 
1073     // Neighbors array
1074     std::array<std::pair<int,int>,1> a1;
1075     a1.at(0) = std::make_pair<int,int>(-1,1);
1076     BOOST_CHECK(g1.neighborHoodIndices({{0}}, a1).collectVector()
1077                 == bins_t({0,1}));
1078     BOOST_CHECK(g1.neighborHoodIndices({{2}}, a1).collectVector()
1079                 == bins_t({1,2,3}));
1080 
1081     a1.at(0) = std::make_pair<int,int>(2,3);
1082     BOOST_CHECK(g1.neighborHoodIndices({{2}}, a1).collectVector()
1083                 == bins_t({4,5}));
1084 
1085     a1.at(0) = std::make_pair<int,int>(-2,-1);
1086     BOOST_CHECK(g1.neighborHoodIndices({{2}}, a1).collectVector()
1087                 == bins_t({0,1}));
1088 
1089     a1.at(0) = std::make_pair<int,int>(-3,-1);
1090     BOOST_CHECK(g1.neighborHoodIndices({{2}}, a1).collectVector()
1091                 == bins_t({0,1}));
1092   // clang-format on
1093 
1094   Axis d(AxisClosed, 0.0, 1.0, 10u);
1095 
1096   Grid g1Cl(Type<double>, std::move(d));
1097   BOOST_CHECK(g1Cl.neighborHoodIndices({{0}}, 1).collectVector() ==
1098               bins_t({}));  // underflow, makes no sense
1099   BOOST_CHECK(g1Cl.neighborHoodIndices({{11}}, 1).collectVector() ==
1100               bins_t({}));  // overflow, makes no sense
1101   BOOST_CHECK(g1Cl.neighborHoodIndices({{1}}, 1).collectVector() ==
1102               bins_t({10, 1, 2}));  // overflow, makes no sense
1103   BOOST_CHECK(g1Cl.neighborHoodIndices({{5}}, 1).collectVector() ==
1104               bins_t({4, 5, 6}));  // overflow, makes no sense
1105 
1106   Axis f(AxisClosed, 0.0, 1.0, 5u);
1107   Axis e(AxisClosed, 0.0, 1.0, 5u);
1108   Grid g2Cl(Type<double>, std::move(e), std::move(f));
1109   BOOST_CHECK(g2Cl.neighborHoodIndices({{3, 3}}, 1).collectVector() ==
1110               bins_t({16, 17, 18, 23, 24, 25, 30, 31, 32}));
1111   BOOST_CHECK(g2Cl.neighborHoodIndices({{1, 1}}, 1).collectVector() ==
1112               bins_t({40, 36, 37, 12, 8, 9, 19, 15, 16}));
1113   BOOST_CHECK(g2Cl.neighborHoodIndices({{1, 5}}, 1).collectVector() ==
1114               bins_t({39, 40, 36, 11, 12, 8, 18, 19, 15}));
1115   BOOST_CHECK(g2Cl.neighborHoodIndices({{5, 1}}, 1).collectVector() ==
1116               bins_t({33, 29, 30, 40, 36, 37, 12, 8, 9}));
1117   BOOST_CHECK(g2Cl.neighborHoodIndices({{5, 5}}, 1).collectVector() ==
1118               bins_t({32, 33, 29, 39, 40, 36, 11, 12, 8}));
1119 
1120   BOOST_CHECK(g2Cl.neighborHoodIndices({{3, 3}}, 2).collectVector() ==
1121               bins_t({8,  9,  10, 11, 12, 15, 16, 17, 18, 19, 22, 23, 24,
1122                       25, 26, 29, 30, 31, 32, 33, 36, 37, 38, 39, 40}));
1123   BOOST_CHECK(g2Cl.neighborHoodIndices({{1, 1}}, 2).collectVector() ==
1124               bins_t({32, 33, 29, 30, 31, 39, 40, 36, 37, 38, 11, 12, 8,
1125                       9,  10, 18, 19, 15, 16, 17, 25, 26, 22, 23, 24}));
1126   BOOST_CHECK(g2Cl.neighborHoodIndices({{1, 5}}, 2).collectVector() ==
1127               bins_t({31, 32, 33, 29, 30, 38, 39, 40, 36, 37, 10, 11, 12,
1128                       8,  9,  17, 18, 19, 15, 16, 24, 25, 26, 22, 23}));
1129   BOOST_CHECK(g2Cl.neighborHoodIndices({{5, 1}}, 2).collectVector() ==
1130               bins_t({25, 26, 22, 23, 24, 32, 33, 29, 30, 31, 39, 40, 36,
1131                       37, 38, 11, 12, 8,  9,  10, 18, 19, 15, 16, 17}));
1132   BOOST_CHECK(g2Cl.neighborHoodIndices({{5, 5}}, 2).collectVector() ==
1133               bins_t({24, 25, 26, 22, 23, 31, 32, 33, 29, 30, 38, 39, 40,
1134                       36, 37, 10, 11, 12, 8,  9,  17, 18, 19, 15, 16}));
1135 
1136   std::array<std::pair<int, int>, 2> a2;
1137   a2.at(0) =
1138       std::make_pair<int, int>(-2, -1);  // only 2 bins left of requested bin
1139                                          // (not including the requested bin)
1140   a2.at(1) = std::make_pair<int, int>(
1141       -1, 2);  // one bin left of requested bin, the requested bin itself and 2
1142                // bins right of requested bin
1143   std::set<std::size_t> returnedBins;
1144 
1145   auto returnedBinsVec = g2Cl.neighborHoodIndices({{3, 2}}, a2).collectVector();
1146   returnedBins.insert(returnedBinsVec.begin(), returnedBinsVec.end());
1147   std::set<std::size_t> expectedBins{{8, 9, 10, 11, 15, 16, 17, 18}};
1148   BOOST_CHECK(returnedBins == expectedBins);
1149 
1150   returnedBinsVec = g2Cl.neighborHoodIndices({{1, 5}}, a2).collectVector();
1151   returnedBins.clear();
1152   returnedBins.insert(returnedBinsVec.begin(), returnedBinsVec.end());
1153   expectedBins = {{29, 30, 32, 33, 36, 37, 39, 40}};
1154   BOOST_CHECK(returnedBins == expectedBins);
1155 
1156   a2.at(0) = {-6, 7};
1157   a2.at(1) = {0, 0};
1158   returnedBinsVec = g2Cl.neighborHoodIndices({{1, 5}}, a2).collectVector();
1159   returnedBins.clear();
1160   returnedBins.insert(returnedBinsVec.begin(), returnedBinsVec.end());
1161   expectedBins = {{12, 19, 26, 33, 40}};
1162   BOOST_CHECK(returnedBins == expectedBins);
1163 
1164   // @TODO 3D test would be nice, but should essentially not be a problem if
1165   // 2D works.
1166 
1167   // clang-format off
1168     /*
1169      *       1   2    3    4    5
1170      *   |------------------------|
1171      * 1 |  8 |  9 | 10 | 11 | 12 |
1172      *   |----|----|----|----|----|
1173      * 2 | 15 | 16 | 17 | 18 | 19 |
1174      *   |----|----|----|----|----|
1175      * 3 | 22 | 23 | 24 | 25 | 26 |
1176      *   |----|----|----|----|----|
1177      * 4 | 29 | 30 | 31 | 32 | 33 |
1178      *   |----|----|----|----|----|
1179      * 5 | 36 | 37 | 38 | 39 | 40 |
1180      *   |------------------------|
1181      */
1182   // clang-format on
1183 }
1184 
1185 BOOST_AUTO_TEST_CASE(closestPoints) {
1186   using Point = std::array<double, 3>;
1187   using bins_t = std::vector<std::size_t>;
1188 
1189   Axis a(0.0, 1.0, 10u);
1190   Axis b(0.0, 1.0, 5u);
1191   Axis c(0.0, 1.0, 3u);
1192   Grid g1(Type<double>, Axis{a});
1193   Grid g2(Type<double>, Axis{a}, Axis{b});
1194   Grid g3(Type<double>, std::move(a), std::move(b), std::move(c));
1195 
1196   // clang-format off
1197     // 1D case
1198     BOOST_CHECK(g1.closestPointsIndices(Point({{0.52}})).collectVector()
1199                 == bins_t({6, 7}));
1200     BOOST_CHECK(g1.closestPointsIndices(Point({{0.98}})).collectVector()
1201                 == bins_t({10, 11}));
1202 
1203     // 2D case
1204     BOOST_CHECK(g2.closestPointsIndices(Point({{0.52, 0.08}})).collectVector()
1205                 == bins_t({43, 44, 50, 51}));
1206     BOOST_CHECK(g2.closestPointsIndices(Point({{0.05, 0.08}})).collectVector()
1207                 == bins_t({8, 9, 15, 16}));
1208 
1209     // 3D case
1210     BOOST_CHECK(g3.closestPointsIndices(Point({{0.23, 0.13, 0.61}})).collectVector()
1211                 == bins_t({112, 113, 117, 118, 147, 148, 152, 153}));
1212     BOOST_CHECK(g3.closestPointsIndices(Point({{0.52, 0.35, 0.71}})).collectVector()
1213                 == bins_t({223, 224, 228, 229, 258, 259, 263, 264}));
1214 
1215     using EAxisClosed = Axis<AxisType::Equidistant, AxisBoundaryType::Closed>;
1216     using Grid1Cl_t = Grid<double, EAxisClosed>;
1217     using Grid2Cl_t = Grid<double, EAxisClosed, EAxisClosed>;
1218     //using Grid3Cl_t = Grid<double, EAxisClosed, EAxisClosed, EAxisClosed>;
1219     EAxisClosed   aCl(0.0, 1.0, 10u);
1220     EAxisClosed   bCl(0.0, 1.0, 5u);
1221     // EAxisClosed   cCl(0.0, 1.0, 3u);
1222     Grid1Cl_t g1Cl(std::make_tuple(aCl));
1223     Grid2Cl_t g2Cl(std::make_tuple(std::move(aCl), std::move(bCl)));
1224 
1225     // 1D case
1226     BOOST_CHECK(g1Cl.closestPointsIndices(Point({{0.52}})).collectVector()
1227                 == bins_t({6, 7}));
1228     BOOST_CHECK(g1Cl.closestPointsIndices(Point({{0.98}})).collectVector()
1229                 == bins_t({10, 1}));
1230 
1231     // 2D case
1232     BOOST_CHECK(g2Cl.closestPointsIndices(Point({{0.52, 0.08}})).collectVector()
1233                 == bins_t({43, 44, 50, 51}));
1234     BOOST_CHECK(g2Cl.closestPointsIndices(Point({{0.52, 0.68}})).collectVector()
1235                 == bins_t({46, 47, 53, 54}));
1236     BOOST_CHECK(g2Cl.closestPointsIndices(Point({{0.52, 0.88}})).collectVector()
1237                 == bins_t({47, 43, 54, 50}));
1238     BOOST_CHECK(g2Cl.closestPointsIndices(Point({{0.05, 0.08}})).collectVector()
1239                 == bins_t({8, 9, 15, 16}));
1240     BOOST_CHECK(g2Cl.closestPointsIndices(Point({{0.9, 0.95}})).collectVector()
1241                 == bins_t({75, 71, 12, 8}));
1242 
1243     // @TODO: 3D checks would also be nice
1244 
1245     Axis  aOp(AxisBound,0.0, 1.0, 10u);
1246     Axis  bOp(AxisBound,0.0, 1.0, 5u);
1247     Grid g1Op(Type<double>, Axis{aOp});
1248     Grid g2Op(Type<double>, std::move(aOp), std::move(bOp));
1249 
1250     // 1D case
1251     BOOST_CHECK(g1Op.closestPointsIndices(Point({{0.52}})).collectVector()
1252                 == bins_t({6, 7}));
1253     BOOST_CHECK(g1Op.closestPointsIndices(Point({{0.98}})).collectVector()
1254                 == bins_t({10}));
1255     BOOST_CHECK(g1Op.closestPointsIndices(Point({{0.88}})).collectVector()
1256                 == bins_t({9, 10}));
1257 
1258     // 2D case
1259     BOOST_CHECK(g2Op.closestPointsIndices(Point({{0.52, 0.08}})).collectVector()
1260                 == bins_t({43, 44, 50, 51}));
1261     BOOST_CHECK(g2Op.closestPointsIndices(Point({{0.52, 0.68}})).collectVector()
1262                 == bins_t({46, 47, 53, 54}));
1263     BOOST_CHECK(g2Op.closestPointsIndices(Point({{0.52, 0.88}})).collectVector()
1264                 == bins_t({47, 54}));
1265     BOOST_CHECK(g2Op.closestPointsIndices(Point({{0.05, 0.1}})).collectVector()
1266                 == bins_t({8, 9, 15, 16}));
1267     BOOST_CHECK(g2Op.closestPointsIndices(Point({{0.95, 0.95}})).collectVector()
1268                 == bins_t({75}));
1269 
1270     // @TODO: 3D checks would also be nice
1271 
1272     /*
1273      *       1    2    3    4    5
1274      *    |------------------------|
1275      *  1 |  8 |  9 | 10 | 11 | 12 |
1276      *    |----|----|----|----|----|
1277      *  2 | 15 | 16 | 17 | 18 | 19 |
1278      *    |----|----|----|----|----|
1279      *  3 | 22 | 23 | 24 | 25 | 26 |
1280      *    |----|----|----|----|----|
1281      *  4 | 29 | 30 | 31 | 32 | 33 |
1282      *    |----|----|----|----|----|
1283      *  5 | 36 | 37 | 38 | 39 | 40 |
1284      *    |------------------------|
1285      *  6 | 43 | 44 | 45 | 46 | 47 |
1286      *    |------------------------|
1287      *  7 | 50 | 51 | 52 | 53 | 54 |
1288      *    |------------------------|
1289      *  8 | 57 | 58 | 59 | 60 | 61 |
1290      *    |------------------------|
1291      *  9 | 64 | 65 | 66 | 67 | 68 |
1292      *    |------------------------|
1293      * 10 | 71 | 72 | 73 | 74 | 75 |
1294      *    |------------------------|
1295      * 77   78   79   80   81   82   83
1296      */
1297 
1298   // clang-format on
1299 }
1300 
1301 BOOST_AUTO_TEST_CASE(grid_type_conversion) {
1302   // Type conversion test
1303   using Grid2Int =
1304       Grid<int, Axis<AxisType::Equidistant>, Axis<AxisType::Variable>>;
1305 
1306   Axis a(0.0, 1.0, 10u);
1307   Axis b({0., 1.2, 2.3, 3.4, 4.5, 5.6});
1308   Grid g2(Type<double>, std::move(a), std::move(b));
1309   decltype(g2) g2Copy(g2.axesTuple());
1310 
1311   static_assert(std::same_as<decltype(g2), decltype(g2Copy)>);
1312 
1313   auto g2ConvertedInt = g2Copy.convertType<int>();
1314   static_assert(std::same_as<decltype(g2ConvertedInt), Grid2Int>);
1315 }
1316 
1317 BOOST_AUTO_TEST_CASE(grid_full_conversion) {
1318   // The converter class
1319   struct DoubleToInt {
1320     // Declare a value tupe
1321     using value_type = int;
1322     // the conversion operator
1323     int operator()(double d) { return static_cast<int>(d); }
1324   };
1325 
1326   // Grid conversion test
1327   Axis a(0.0, 1.0, 2u);
1328   Grid g1(Type<double>, std::move(a));
1329 
1330   using Point = std::array<double, 1>;
1331   g1.atPosition(Point({{0.3}})) = 1.1;
1332   g1.atPosition(Point({{0.6}})) = 2.4;
1333 
1334   DoubleToInt d2i;
1335 
1336   auto g1ConvertedInt = g1.convertGrid(d2i);
1337   BOOST_CHECK_EQUAL(g1ConvertedInt.atPosition(Point({{0.3}})), 1);
1338   BOOST_CHECK_EQUAL(g1ConvertedInt.atPosition(Point({{0.6}})), 2);
1339 }
1340 
1341 BOOST_AUTO_TEST_CASE(Output) {
1342   Axis a{AxisOpen, 0.0, 1.0, 10u};
1343   Axis b{AxisBound, {1, 2, 3}};
1344 
1345   Grid g(Type<double>, std::move(a), std::move(b));
1346 
1347   std::stringstream ss;
1348   ss << g;
1349   BOOST_CHECK_EQUAL(
1350       ss.str(),
1351       "Axis<Equidistant, Open>(0, 1, 10), Axis<Variable, Bound>(1, 2, 3)");
1352 
1353   const IGrid& ig = g;
1354 
1355   ss.str("");
1356 
1357   ss << ig;
1358 
1359   BOOST_CHECK_EQUAL(
1360       ss.str(),
1361       "Axis<Equidistant, Open>(0, 1, 10), Axis<Variable, Bound>(1, 2, 3)");
1362 }
1363 
1364 BOOST_AUTO_TEST_CASE(Equality) {
1365   Axis a{AxisOpen, 0.0, 1.0, 10u};
1366   Axis b{AxisBound, {1, 2, 3}};
1367   Axis c{AxisClosed, {1, 2, 5}};
1368 
1369   Grid ab{Type<double>, a, b};
1370   Grid ac{Type<double>, a, c};
1371 
1372   BOOST_CHECK_EQUAL(ab, ab);
1373   BOOST_CHECK_EQUAL(ac, ac);
1374   BOOST_CHECK_NE(ab, ac);
1375 
1376   const IGrid& iab = ab;
1377   const IGrid& iac = ac;
1378 
1379   BOOST_CHECK_EQUAL(iab, iab);
1380   BOOST_CHECK_EQUAL(iac, iac);
1381 }
1382 
1383 BOOST_AUTO_TEST_SUITE_END()
1384 
1385 }  // namespace Acts::Test