ECCE @ EIC Software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
grid_helper.hpp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file grid_helper.hpp
1 // This file is part of the Acts project.
2 //
3 // Copyright (C) 2017-2018 CERN for the benefit of the Acts project
4 //
5 // This Source Code Form is subject to the terms of the Mozilla Public
6 // License, v. 2.0. If a copy of the MPL was not distributed with this
7 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 
9 #pragma once
10 
11 #include <array>
12 #include <tuple>
13 #include <utility>
14 #include "Acts/Utilities/IAxis.hpp"
16 
17 namespace Acts {
18 
19 namespace detail {
20 
21 // This object can be iterated to produce the (ordered) set of global indices
22 // associated with a neighborhood around a certain point on a grid.
23 //
24 // The goal is to emulate the effect of enumerating the global indices into
25 // an std::set (or into an std::vector that gets subsequently sorted), without
26 // paying the price of dynamic memory allocation in hot magnetic field
27 // interpolation code.
28 //
29 template <size_t DIM>
31  public:
32  // You can get the local neighbor indices from
33  // grid_helper_impl<DIM>::neighborHoodIndices and the number of bins in
34  // each direction from grid_helper_impl<DIM>::getNBins.
36  std::array<NeighborHoodIndices, DIM>& neighborIndices,
37  const std::array<size_t, DIM>& nBinsArray)
38  : m_localIndices(neighborIndices) {
39  if (DIM == 1)
40  return;
41  size_t globalStride = 1;
42  for (long i = DIM - 2; i >= 0; --i) {
43  globalStride *= (nBinsArray[i + 1] + 2);
44  m_globalStrides[i] = globalStride;
45  }
46  }
47 
48  class iterator {
49  public:
50  iterator() = default;
51 
53  std::array<NeighborHoodIndices::iterator, DIM>&& localIndicesIter)
54  : m_localIndicesIter(std::move(localIndicesIter)), m_parent(&parent) {}
55 
56  size_t operator*() const {
57  size_t globalIndex = *m_localIndicesIter[DIM - 1];
58  if (DIM == 1)
59  return globalIndex;
60  for (size_t i = 0; i < DIM - 1; ++i) {
61  globalIndex += m_parent->m_globalStrides[i] * (*m_localIndicesIter[i]);
62  }
63  return globalIndex;
64  }
65 
67  const auto& localIndices = m_parent->m_localIndices;
68 
69  // Go to the next global index via a lexicographic increment:
70  // - Start by incrementing the last local index
71  // - If it reaches the end, reset it and increment the previous one...
72  for (long i = DIM - 1; i > 0; --i) {
73  ++m_localIndicesIter[i];
74  if (m_localIndicesIter[i] != localIndices[i].end())
75  return *this;
76  m_localIndicesIter[i] = localIndices[i].begin();
77  }
78 
79  // The first index should stay at the end value when it reaches it, so
80  // that we know when we've reached the end of iteration.
81  ++m_localIndicesIter[0];
82  return *this;
83  }
84 
85  bool operator==(const iterator& it) {
86  // We know when we've reached the end, so we don't need an end-iterator.
87  // Sadly, in C++, there has to be one. Therefore, we special-case it
88  // heavily so that it's super-efficient to create and compare to.
89  if (it.m_parent == nullptr) {
90  return m_localIndicesIter[0] == m_parent->m_localIndices[0].end();
91  } else {
93  }
94  }
95 
96  bool operator!=(const iterator& it) { return !(*this == it); }
97 
98  private:
99  std::array<NeighborHoodIndices::iterator, DIM> m_localIndicesIter;
101  };
102 
103  iterator begin() const {
104  std::array<NeighborHoodIndices::iterator, DIM> localIndicesIter;
105  for (size_t i = 0; i < DIM; ++i) {
106  localIndicesIter[i] = m_localIndices[i].begin();
107  }
108  return iterator(*this, std::move(localIndicesIter));
109  }
110 
111  iterator end() const { return iterator(); }
112 
113  // Number of indices that will be produced if this sequence is iterated
114  size_t size() const {
115  size_t result = m_localIndices[0].size();
116  for (size_t i = 1; i < DIM; ++i) {
117  result *= m_localIndices[i].size();
118  }
119  return result;
120  }
121 
122  // Collect the sequence of indices into an std::vector
123  std::vector<size_t> collect() const {
124  std::vector<size_t> result;
125  result.reserve(this->size());
126  for (size_t idx : *this) {
127  result.push_back(idx);
128  }
129  return result;
130  }
131 
132  private:
133  std::array<NeighborHoodIndices, DIM> m_localIndices;
134  std::array<size_t, DIM - 1> m_globalStrides;
135 };
136 
141 template <size_t N>
143 
144 template <size_t N>
146  template <class... Axes>
147  static void getBinCenter(
148  std::array<double, sizeof...(Axes)>& center,
149  const std::array<size_t, sizeof...(Axes)>& localIndices,
150  const std::tuple<Axes...>& axes) {
151  center.at(N) = std::get<N>(axes).getBinCenter(localIndices.at(N));
152  grid_helper_impl<N - 1>::getBinCenter(center, localIndices, axes);
153  }
154 
155  template <class... Axes>
156  static void getGlobalBin(const std::array<size_t, sizeof...(Axes)>& localBins,
157  const std::tuple<Axes...>& axes, size_t& bin,
158  size_t& area) {
159  const auto& thisAxis = std::get<N>(axes);
160  bin += area * localBins.at(N);
161  // make sure to account for under-/overflow bins
162  area *= (thisAxis.getNBins() + 2);
163  grid_helper_impl<N - 1>::getGlobalBin(localBins, axes, bin, area);
164  }
165 
166  template <class Point, class... Axes>
167  static void getLocalBinIndices(const Point& point,
168  const std::tuple<Axes...>& axes,
169  std::array<size_t, sizeof...(Axes)>& indices) {
170  const auto& thisAxis = std::get<N>(axes);
171  indices.at(N) = thisAxis.getBin(point[N]);
172  grid_helper_impl<N - 1>::getLocalBinIndices(point, axes, indices);
173  }
174 
175  template <class... Axes>
176  static void getLocalBinIndices(size_t& bin, const std::tuple<Axes...>& axes,
177  size_t& area,
178  std::array<size_t, sizeof...(Axes)>& indices) {
179  const auto& thisAxis = std::get<N>(axes);
180  // make sure to account for under-/overflow bins
181  size_t new_area = area * (thisAxis.getNBins() + 2);
182  grid_helper_impl<N - 1>::getLocalBinIndices(bin, axes, new_area, indices);
183  indices.at(N) = bin / area;
184  bin %= area;
185  }
186 
187  template <class... Axes>
188  static void getLowerLeftBinEdge(
189  std::array<double, sizeof...(Axes)>& llEdge,
190  const std::array<size_t, sizeof...(Axes)>& localIndices,
191  const std::tuple<Axes...>& axes) {
192  llEdge.at(N) = std::get<N>(axes).getBinLowerBound(localIndices.at(N));
193  grid_helper_impl<N - 1>::getLowerLeftBinEdge(llEdge, localIndices, axes);
194  }
195 
196  template <class... Axes>
198  std::array<size_t, sizeof...(Axes)>& localIndices,
199  const std::tuple<Axes...>& axes) {
200  localIndices.at(N) = std::get<N>(axes).wrapBin(localIndices.at(N) - 1);
202  }
203 
204  template <class... Axes>
205  static void getNBins(const std::tuple<Axes...>& axes,
206  std::array<size_t, sizeof...(Axes)>& nBinsArray) {
207  // by convention getNBins does not include under-/overflow bins
208  nBinsArray[N] = std::get<N>(axes).getNBins();
209  grid_helper_impl<N - 1>::getNBins(axes, nBinsArray);
210  }
211 
212  template <class... Axes>
213  static void getAxes(const std::tuple<Axes...>& axes,
214  std::array<const IAxis*, sizeof...(Axes)>& axesArr) {
215  axesArr[N] = static_cast<const IAxis*>(&std::get<N>(axes));
216  grid_helper_impl<N - 1>::getAxes(axes, axesArr);
217  }
218 
219  template <class... Axes>
220  static void getUpperRightBinEdge(
221  std::array<double, sizeof...(Axes)>& urEdge,
222  const std::array<size_t, sizeof...(Axes)>& localIndices,
223  const std::tuple<Axes...>& axes) {
224  urEdge.at(N) = std::get<N>(axes).getBinUpperBound(localIndices.at(N));
225  grid_helper_impl<N - 1>::getUpperRightBinEdge(urEdge, localIndices, axes);
226  }
227 
228  template <class... Axes>
230  std::array<size_t, sizeof...(Axes)>& localIndices,
231  const std::tuple<Axes...>& axes) {
232  localIndices.at(N) = std::get<N>(axes).wrapBin(localIndices.at(N) + 1);
234  }
235 
236  template <class... Axes>
237  static void getMin(const std::tuple<Axes...>& axes,
238  std::array<double, sizeof...(Axes)>& minArray) {
239  minArray[N] = std::get<N>(axes).getMin();
240  grid_helper_impl<N - 1>::getMin(axes, minArray);
241  }
242 
243  template <class... Axes>
244  static void getMax(const std::tuple<Axes...>& axes,
245  std::array<double, sizeof...(Axes)>& maxArray) {
246  maxArray[N] = std::get<N>(axes).getMax();
247  grid_helper_impl<N - 1>::getMax(axes, maxArray);
248  }
249 
250  template <class... Axes>
251  static void getWidth(const std::tuple<Axes...>& axes,
252  std::array<double, sizeof...(Axes)>& widthArray) {
253  widthArray[N] = std::get<N>(axes).getBinWidth();
254  grid_helper_impl<N - 1>::getWidth(axes, widthArray);
255  }
256 
257  template <class Point, class... Axes>
258  static bool isInside(const Point& position, const std::tuple<Axes...>& axes) {
259  bool insideThisAxis = std::get<N>(axes).isInside(position[N]);
260  return insideThisAxis && grid_helper_impl<N - 1>::isInside(position, axes);
261  }
262 
263  template <class... Axes>
264  static void neighborHoodIndices(
265  const std::array<size_t, sizeof...(Axes)>& localIndices,
266  std::pair<size_t, size_t> sizes, const std::tuple<Axes...>& axes,
267  std::array<NeighborHoodIndices, sizeof...(Axes)>& neighborIndices) {
268  // ask n-th axis
269  size_t locIdx = localIndices.at(N);
270  NeighborHoodIndices locNeighbors =
271  std::get<N>(axes).neighborHoodIndices(locIdx, sizes);
272  neighborIndices.at(N) = locNeighbors;
273 
274  grid_helper_impl<N - 1>::neighborHoodIndices(localIndices, sizes, axes,
275  neighborIndices);
276  }
277 
278  template <class... Axes>
279  static void exteriorBinIndices(std::array<size_t, sizeof...(Axes)>& idx,
280  std::array<bool, sizeof...(Axes)> isExterior,
281  std::set<size_t>& combinations,
282  const std::tuple<Axes...>& axes) {
283  // iterate over this axis' bins, remembering which bins are exterior
284  for (size_t i = 0; i < std::get<N>(axes).getNBins() + 2; ++i) {
285  idx.at(N) = i;
286  isExterior.at(N) = (i == 0) || (i == std::get<N>(axes).getNBins() + 1);
287  // vary other axes recursively
288  grid_helper_impl<N - 1>::exteriorBinIndices(idx, isExterior, combinations,
289  axes);
290  }
291  }
292 };
293 
294 template <>
295 struct grid_helper_impl<0u> {
296  template <class... Axes>
297  static void getBinCenter(
298  std::array<double, sizeof...(Axes)>& center,
299  const std::array<size_t, sizeof...(Axes)>& localIndices,
300  const std::tuple<Axes...>& axes) {
301  center.at(0u) = std::get<0u>(axes).getBinCenter(localIndices.at(0u));
302  }
303 
304  template <class... Axes>
305  static void getGlobalBin(const std::array<size_t, sizeof...(Axes)>& localBins,
306  const std::tuple<Axes...>& /*axes*/, size_t& bin,
307  size_t& area) {
308  bin += area * localBins.at(0u);
309  }
310 
311  template <class Point, class... Axes>
312  static void getLocalBinIndices(const Point& point,
313  const std::tuple<Axes...>& axes,
314  std::array<size_t, sizeof...(Axes)>& indices) {
315  const auto& thisAxis = std::get<0u>(axes);
316  indices.at(0u) = thisAxis.getBin(point[0u]);
317  }
318 
319  template <class... Axes>
320  static void getLocalBinIndices(size_t& bin,
321  const std::tuple<Axes...>& /*axes*/,
322  size_t& area,
323  std::array<size_t, sizeof...(Axes)>& indices) {
324  // make sure to account for under-/overflow bins
325  indices.at(0u) = bin / area;
326  bin %= area;
327  }
328 
329  template <class... Axes>
330  static void getLowerLeftBinEdge(
331  std::array<double, sizeof...(Axes)>& llEdge,
332  const std::array<size_t, sizeof...(Axes)>& localIndices,
333  const std::tuple<Axes...>& axes) {
334  llEdge.at(0u) = std::get<0u>(axes).getBinLowerBound(localIndices.at(0u));
335  }
336 
337  template <class... Axes>
339  std::array<size_t, sizeof...(Axes)>& localIndices,
340  const std::tuple<Axes...>& axes) {
341  localIndices.at(0u) = std::get<0u>(axes).wrapBin(localIndices.at(0u) - 1);
342  }
343 
344  template <class... Axes>
345  static void getNBins(const std::tuple<Axes...>& axes,
346  std::array<size_t, sizeof...(Axes)>& nBinsArray) {
347  // by convention getNBins does not include under-/overflow bins
348  nBinsArray[0u] = std::get<0u>(axes).getNBins();
349  }
350 
351  template <class... Axes>
352  static void getAxes(const std::tuple<Axes...>& axes,
353  std::array<const IAxis*, sizeof...(Axes)>& axesArr) {
354  axesArr[0u] = static_cast<const IAxis*>(&std::get<0u>(axes));
355  }
356 
357  template <class... Axes>
358  static void getUpperRightBinEdge(
359  std::array<double, sizeof...(Axes)>& urEdge,
360  const std::array<size_t, sizeof...(Axes)>& localIndices,
361  const std::tuple<Axes...>& axes) {
362  urEdge.at(0u) = std::get<0u>(axes).getBinUpperBound(localIndices.at(0u));
363  }
364 
365  template <class... Axes>
367  std::array<size_t, sizeof...(Axes)>& localIndices,
368  const std::tuple<Axes...>& axes) {
369  localIndices.at(0u) = std::get<0u>(axes).wrapBin(localIndices.at(0u) + 1);
370  }
371 
372  template <class... Axes>
373  static void getMin(const std::tuple<Axes...>& axes,
374  std::array<double, sizeof...(Axes)>& minArray) {
375  minArray[0u] = std::get<0u>(axes).getMin();
376  }
377 
378  template <class... Axes>
379  static void getMax(const std::tuple<Axes...>& axes,
380  std::array<double, sizeof...(Axes)>& maxArray) {
381  maxArray[0u] = std::get<0u>(axes).getMax();
382  }
383 
384  template <class... Axes>
385  static void getWidth(const std::tuple<Axes...>& axes,
386  std::array<double, sizeof...(Axes)>& widthArray) {
387  widthArray[0u] = std::get<0u>(axes).getBinWidth();
388  }
389 
390  template <class Point, class... Axes>
391  static bool isInside(const Point& position, const std::tuple<Axes...>& axes) {
392  return std::get<0u>(axes).isInside(position[0u]);
393  }
394 
395  template <class... Axes>
396  static void neighborHoodIndices(
397  const std::array<size_t, sizeof...(Axes)>& localIndices,
398  std::pair<size_t, size_t> sizes, const std::tuple<Axes...>& axes,
399  std::array<NeighborHoodIndices, sizeof...(Axes)>& neighborIndices) {
400  // ask 0-th axis
401  size_t locIdx = localIndices.at(0u);
402  NeighborHoodIndices locNeighbors =
403  std::get<0u>(axes).neighborHoodIndices(locIdx, sizes);
404  neighborIndices.at(0u) = locNeighbors;
405  }
406 
407  template <class... Axes>
408  static void exteriorBinIndices(std::array<size_t, sizeof...(Axes)>& idx,
409  std::array<bool, sizeof...(Axes)> isExterior,
410  std::set<size_t>& combinations,
411  const std::tuple<Axes...>& axes) {
412  // For each exterior bin on this axis, we will do this
413  auto recordExteriorBin = [&](size_t i) {
414  idx.at(0u) = i;
415  // at this point, combinations are complete: save the global bin
416  size_t bin = 0, area = 1;
417  grid_helper_impl<sizeof...(Axes) - 1>::getGlobalBin(idx, axes, bin, area);
418  combinations.insert(bin);
419  };
420 
421  // The first and last bins on this axis are exterior by definition
422  for (size_t i :
423  {static_cast<size_t>(0), std::get<0u>(axes).getNBins() + 1}) {
424  recordExteriorBin(i);
425  }
426 
427  // If no other axis is on an exterior index, stop here
428  bool otherAxisExterior = false;
429  for (size_t N = 1; N < sizeof...(Axes); ++N) {
430  otherAxisExterior = otherAxisExterior | isExterior[N];
431  }
432  if (!otherAxisExterior) {
433  return;
434  }
435 
436  // Otherwise, we're on a grid border: iterate over all the other indices
437  for (size_t i = 1; i <= std::get<0u>(axes).getNBins(); ++i) {
438  recordExteriorBin(i);
439  }
440  }
441 };
443 
445 struct grid_helper {
457  template <class... Axes>
459  const std::array<size_t, sizeof...(Axes)>& localIndices,
460  const std::tuple<Axes...>& axes) {
461  // get neighboring bins, but only increment.
462  return neighborHoodIndices(localIndices, std::make_pair(0, 1), axes);
463  }
464 
474  template <class... Axes>
475  static std::array<double, sizeof...(Axes)> getBinCenter(
476  const std::array<size_t, sizeof...(Axes)>& localIndices,
477  const std::tuple<Axes...>& axes) {
478  std::array<double, sizeof...(Axes)> center;
479  constexpr size_t MAX = sizeof...(Axes) - 1;
480  grid_helper_impl<MAX>::getBinCenter(center, localIndices, axes);
481 
482  return center;
483  }
484 
495  template <class... Axes>
496  static size_t getGlobalBin(
497  const std::array<size_t, sizeof...(Axes)>& localBins,
498  const std::tuple<Axes...>& axes) {
499  constexpr size_t MAX = sizeof...(Axes) - 1;
500  size_t area = 1;
501  size_t bin = 0;
502 
503  grid_helper_impl<MAX>::getGlobalBin(localBins, axes, bin, area);
504 
505  return bin;
506  }
507 
522  template <class Point, class... Axes>
523  static std::array<size_t, sizeof...(Axes)> getLocalBinIndices(
524  const Point& point, const std::tuple<Axes...>& axes) {
525  constexpr size_t MAX = sizeof...(Axes) - 1;
526  std::array<size_t, sizeof...(Axes)> indices;
527 
528  grid_helper_impl<MAX>::getLocalBinIndices(point, axes, indices);
529 
530  return indices;
531  }
532 
543  template <class... Axes>
544  static std::array<size_t, sizeof...(Axes)> getLocalBinIndices(
545  size_t bin, const std::tuple<Axes...>& axes) {
546  constexpr size_t MAX = sizeof...(Axes) - 1;
547  size_t area = 1;
548  std::array<size_t, sizeof...(Axes)> indices;
549 
550  grid_helper_impl<MAX>::getLocalBinIndices(bin, axes, area, indices);
551 
552  return indices;
553  }
554 
564  template <class... Axes>
565  static std::array<double, sizeof...(Axes)> getLowerLeftBinEdge(
566  const std::array<size_t, sizeof...(Axes)>& localIndices,
567  const std::tuple<Axes...>& axes) {
568  std::array<double, sizeof...(Axes)> llEdge;
569  constexpr size_t MAX = sizeof...(Axes) - 1;
570  grid_helper_impl<MAX>::getLowerLeftBinEdge(llEdge, localIndices, axes);
571 
572  return llEdge;
573  }
574 
588  template <class... Axes>
589  static std::array<size_t, sizeof...(Axes)> getLowerLeftBinIndices(
590  const std::array<size_t, sizeof...(Axes)>& localIndices,
591  const std::tuple<Axes...>& axes) {
592  constexpr size_t MAX = sizeof...(Axes) - 1;
593  auto llIndices = localIndices;
595 
596  return llIndices;
597  }
598 
607  template <class... Axes>
608  static std::array<size_t, sizeof...(Axes)> getNBins(
609  const std::tuple<Axes...>& axes) {
610  std::array<size_t, sizeof...(Axes)> nBinsArray;
611  grid_helper_impl<sizeof...(Axes) - 1>::getNBins(axes, nBinsArray);
612  return nBinsArray;
613  }
614 
621  template <class... Axes>
622  static std::array<const IAxis*, sizeof...(Axes)> getAxes(
623  const std::tuple<Axes...>& axes) {
624  std::array<const IAxis*, sizeof...(Axes)> arr;
625  grid_helper_impl<sizeof...(Axes) - 1>::getAxes(axes, arr);
626  return arr;
627  }
628 
638  template <class... Axes>
639  static std::array<double, sizeof...(Axes)> getUpperRightBinEdge(
640  const std::array<size_t, sizeof...(Axes)>& localIndices,
641  const std::tuple<Axes...>& axes) {
642  std::array<double, sizeof...(Axes)> urEdge;
643  constexpr size_t MAX = sizeof...(Axes) - 1;
644  grid_helper_impl<MAX>::getUpperRightBinEdge(urEdge, localIndices, axes);
645 
646  return urEdge;
647  }
648 
662  template <class... Axes>
663  static std::array<size_t, sizeof...(Axes)> getUpperRightBinIndices(
664  const std::array<size_t, sizeof...(Axes)>& localIndices,
665  const std::tuple<Axes...>& axes) {
666  constexpr size_t MAX = sizeof...(Axes) - 1;
667  auto urIndices = localIndices;
669 
670  return urIndices;
671  }
672 
678  template <class... Axes>
679  static std::array<double, sizeof...(Axes)> getMin(
680  const std::tuple<Axes...>& axes) {
681  std::array<double, sizeof...(Axes)> minArray;
682  grid_helper_impl<sizeof...(Axes) - 1>::getMin(axes, minArray);
683  return minArray;
684  }
685 
691  template <class... Axes>
692  static std::array<double, sizeof...(Axes)> getMax(
693  const std::tuple<Axes...>& axes) {
694  std::array<double, sizeof...(Axes)> maxArray;
695  grid_helper_impl<sizeof...(Axes) - 1>::getMax(axes, maxArray);
696  return maxArray;
697  }
698 
704  template <class... Axes>
705  static std::array<double, sizeof...(Axes)> getWidth(
706  const std::tuple<Axes...>& axes) {
707  std::array<double, sizeof...(Axes)> widthArray;
708  grid_helper_impl<sizeof...(Axes) - 1>::getWidth(axes, widthArray);
709  return widthArray;
710  }
711 
732  template <class... Axes>
734  const std::array<size_t, sizeof...(Axes)>& localIndices,
735  std::pair<size_t, size_t> sizes, const std::tuple<Axes...>& axes) {
736  constexpr size_t MAX = sizeof...(Axes) - 1;
737 
738  // length N array which contains local neighbors based on size par
739  std::array<NeighborHoodIndices, sizeof...(Axes)> neighborIndices;
740  // get local bin indices for neighboring bins
741  grid_helper_impl<MAX>::neighborHoodIndices(localIndices, sizes, axes,
742  neighborIndices);
743 
744  // Query the number of bins
745  std::array<size_t, sizeof...(Axes)> nBinsArray = getNBins(axes);
746 
747  // Produce iterator of global indices
748  return GlobalNeighborHoodIndices(neighborIndices, nBinsArray);
749  }
750 
751  template <class... Axes>
753  const std::array<size_t, sizeof...(Axes)>& localIndices, size_t size,
754  const std::tuple<Axes...>& axes) {
755  return neighborHoodIndices(localIndices, std::make_pair(size, size), axes);
756  }
757 
763  template <class... Axes>
764  static std::set<size_t> exteriorBinIndices(const std::tuple<Axes...>& axes) {
765  constexpr size_t MAX = sizeof...(Axes) - 1;
766 
767  std::array<size_t, sizeof...(Axes)> idx;
768  std::array<bool, sizeof...(Axes)> isExterior;
769  std::set<size_t> combinations;
770  grid_helper_impl<MAX>::exteriorBinIndices(idx, isExterior, combinations,
771  axes);
772 
773  return combinations;
774  }
775 
789  template <class Point, class... Axes>
790  static bool isInside(const Point& position, const std::tuple<Axes...>& axes) {
791  constexpr size_t MAX = sizeof...(Axes) - 1;
792  return grid_helper_impl<MAX>::isInside(position, axes);
793  }
794 };
795 
796 } // namespace detail
797 
798 } // namespace Acts