EIC Software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
GeometryHierarchyMap.hpp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file GeometryHierarchyMap.hpp
1 // This file is part of the Acts project.
2 //
3 // Copyright (C) 2020 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 
12 
13 #include <algorithm>
14 #include <cassert>
15 #include <initializer_list>
16 #include <iterator>
17 #include <stdexcept>
18 #include <utility>
19 #include <vector>
20 
21 namespace Acts {
22 
61 template <typename value_t>
63  public:
65  using InputElement = typename std::pair<GeometryIdentifier, value_t>;
66  using Iterator = typename std::vector<value_t>::const_iterator;
67  using Size = typename std::vector<value_t>::size_type;
68  using Value = value_t;
69 
73  GeometryHierarchyMap(std::vector<InputElement> elements);
77  GeometryHierarchyMap(std::initializer_list<InputElement> elements);
78 
79  // defaulted constructors and assignment operators
80  GeometryHierarchyMap() = default;
83  ~GeometryHierarchyMap() = default;
86 
88  Iterator begin() const { return m_values.begin(); }
90  Iterator end() const { return m_values.end(); }
92  bool empty() const { return m_values.empty(); }
94  Size size() const { return m_values.size(); }
95 
99  GeometryIdentifier idAt(Size index) const { return m_ids.at(index); }
103  const Value& valueAt(Size index) const { return m_values.at(index); }
104 
114  Iterator find(GeometryIdentifier id) const;
115 
116  private:
117  // NOTE this class assumes that it knows the ordering of the levels within
118  // the geometry id. if the geometry id changes, this code has to be
119  // adapted too. the asserts ensure that such a change is caught.
120  static_assert(GeometryIdentifier().setVolume(1).value() <
121  GeometryIdentifier().setVolume(1).setBoundary(1).value(),
122  "Incompatible GeometryIdentifier hierarchy");
123  static_assert(GeometryIdentifier().setBoundary(1).value() <
124  GeometryIdentifier().setBoundary(1).setLayer(1).value(),
125  "Incompatible GeometryIdentifier hierarchy");
126  static_assert(GeometryIdentifier().setLayer(1).value() <
127  GeometryIdentifier().setLayer(1).setApproach(1).value(),
128  "Incompatible GeometryIdentifier hierarchy");
129  static_assert(GeometryIdentifier().setApproach(1).value() <
130  GeometryIdentifier().setApproach(1).setSensitive(1).value(),
131  "Incompatible GeometryIdentifier hierarchy");
132 
134 
135  // encoded ids for all elements for faster lookup.
136  std::vector<Identifier> m_ids;
137  // validity bit masks for the ids: which parts to use for comparison
138  std::vector<Identifier> m_masks;
139  std::vector<Value> m_values;
140 
143  // construct id from encoded value with all bits set
145  // manually iterate over identifier levels starting from the lowest
146  if (id.sensitive() != 0u) {
147  // all levels are valid; keep all bits set.
148  return allSet.value();
149  }
150  if (id.approach() != 0u) {
151  return allSet.setSensitive(0u).value();
152  }
153  if (id.layer() != 0u) {
154  return allSet.setSensitive(0u).setApproach(0u).value();
155  }
156  if (id.boundary() != 0u) {
157  return allSet.setSensitive(0u).setApproach(0u).setLayer(0u).value();
158  }
159  if (id.volume() != 0u) {
160  return allSet.setSensitive(0u)
161  .setApproach(0u)
162  .setLayer(0u)
163  .setBoundary(0u)
164  .value();
165  }
166  // no valid levels; all bits are zero.
167  return Identifier(0u);
168  }
170  static constexpr Identifier makeHighestLevelMask() {
171  return makeLeadingLevelsMask(GeometryIdentifier(0u).setVolume(1u));
172  }
174  static constexpr bool equalWithinMask(Identifier lhs, Identifier rhs,
175  Identifier mask) {
176  return (lhs & mask) == (rhs & mask);
177  }
179  template <typename iterator_t>
181 
186  template <typename iterator_t>
187  void fill(iterator_t beg, iterator_t end);
188 };
189 
190 // implementations
191 
192 template <typename value_t>
194  std::vector<InputElement> elements) {
195  sortAndCheckDuplicates(elements.begin(), elements.end());
196  fill(elements.begin(), elements.end());
197 }
198 
199 template <typename value_t>
201  std::initializer_list<InputElement> elements)
203  std::vector<InputElement>(elements.begin(), elements.end())) {}
204 
205 template <typename value_t>
206 template <typename iterator_t>
208  iterator_t beg, iterator_t end) {
209  // ensure elements are sorted by identifier
210  std::sort(beg, end, [=](const auto& lhs, const auto& rhs) {
211  return lhs.first < rhs.first;
212  });
213  // check that all elements have unique identifier
214  auto dup = std::adjacent_find(beg, end, [](const auto& lhs, const auto& rhs) {
215  return lhs.first == rhs.first;
216  });
217  if (dup != end) {
218  throw std::invalid_argument("Input elements contain duplicates");
219  }
220 }
221 
222 template <typename value_t>
223 template <typename iterator_t>
225  iterator_t end) {
226  const auto n = std::distance(beg, end);
227  m_ids.clear();
228  m_ids.reserve(n);
229  m_masks.clear();
230  m_masks.reserve(n);
231  m_values.clear();
232  m_values.reserve(n);
233  for (; beg != end; ++beg) {
234  m_ids.push_back(beg->first.value());
235  m_masks.push_back(makeLeadingLevelsMask(beg->first.value()));
236  m_values.push_back(std::move(beg->second));
237  }
238 }
239 
240 template <typename value_t>
242  -> Iterator {
243  assert((m_ids.size() == m_values.size()) and
244  "Inconsistent container state: #ids != # values");
245  assert((m_masks.size() == m_values.size()) and
246  "Inconsistent container state: #masks != #values");
247 
248  // we can not search for the element directly since the relevant one
249  // might be stored at a higher level. ids for higher levels would always
250  // be sorted before the requested id. searching for the first element
251  // after the requested ensures that we include the full hierarchy.
252  const auto it = std::upper_bound(m_ids.begin(), m_ids.end(), id.value());
253  auto i = std::distance(m_ids.begin(), it);
254 
255  // now go up the hierarchy to find the first matching element.
256  // example: the container stores four identifiers
257  //
258  // 2|x|x (volume-only)
259  // 2|2|1 (volume, layer, and sensitive)
260  // 2|3|x (volume and layer)
261  // 2|3|4 (volume, layer, and sensitive)
262  //
263  // where | marks level boundaries. searching for either 2|3|4, 2|3|7, or
264  // 2|4|x would first point to 2|3|4 and thus needs to go up the hierarchy.
265  while (0 < i) {
266  // index always starts after item of interest due to upper bound search
267  --i;
268 
269  // if the input id does not even match at the highest hierarchy level
270  // with the current comparison id, then have reached the end of this
271  // hierarchy. having a special check for the highest level avoids an
272  // unbounded search window all the way to the beginning of the container for
273  // the global default entry.
274  if (not equalWithinMask(id.value(), m_ids[i], makeHighestLevelMask())) {
275  // check if a global default entry exists
276  if (m_ids.front() == Identifier(0u)) {
277  return begin();
278  } else {
279  return end();
280  }
281  }
282 
283  // since the search is going backwards in the sorted container, it
284  // progresses from more specific to less specific elements. the first
285  // match is automatically the appropriate one.
286  if (equalWithinMask(id.value(), m_ids[i], m_masks[i])) {
287  return std::next(begin(), i);
288  }
289  }
290 
291  // all options are exhausted and no matching element was found.
292  return end();
293 }
294 
295 } // namespace Acts