Back to home page

sPhenix code displayed by LXR

 
 

    


File indexing completed on 2025-08-06 08:11:34

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