ECCE @ EIC Software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
HierarchicalGeometryContainer.hpp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file HierarchicalGeometryContainer.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 
11 #include <algorithm>
12 #include <cassert>
13 #include <iterator>
14 #include <stdexcept>
15 #include <vector>
16 
19 
20 namespace Acts {
21 
51 template <typename value_t>
53  public:
54  using Iterator = typename std::vector<value_t>::const_iterator;
55  using Size = typename std::vector<value_t>::size_type;
56  using Value = value_t;
57 
64  template <typename id_getter_t = detail::DefaultGeometryIdGetter>
65  HierarchicalGeometryContainer(std::vector<Value>&& elements,
66  id_getter_t getId = id_getter_t());
67 
68  // defaulted constructors and assignment operators
74  const HierarchicalGeometryContainer&) = default;
76  default;
77 
79  Iterator begin() const { return m_elements.begin(); }
81  Iterator end() const { return m_elements.end(); }
83  bool empty() const { return m_elements.empty(); }
85  Size size() const { return m_elements.size(); }
86 
96  Iterator find(GeometryID id) const;
97 
98  private:
100 
101  std::vector<Value> m_elements;
102  // encoded ids for all elements for faster lookup and to avoid having
103  // to store a method to retrieve the ids from the stored elements.
104  std::vector<Identifier> m_ids;
105  // validity bit masks for the ids: which parts to use for comparison
106  std::vector<Identifier> m_masks;
107 
110  // construct id from encoded value with all bits set
111  auto mask = GeometryID(~GeometryID::Value(0u));
112  // NOTE this code assumes some knowledge about the ordering of the levels
113  // within the geometry id. if the geometry id changes, this code has
114  // to be adapted too.
115  if (id.sensitive() != 0u) {
116  // all levels are valid; keep all at one.
117  return mask.value();
118  }
119  if (id.approach() != 0u) {
120  return mask.setSensitive(0u).value();
121  }
122  if (id.layer() != 0u) {
123  return mask.setSensitive(0u).setApproach(0u).value();
124  }
125  if (id.boundary() != 0u) {
126  return mask.setSensitive(0u).setApproach(0u).setLayer(0u).value();
127  }
128  // identifier must always have non-zero volume
129  return mask.setSensitive(0u)
130  .setApproach(0u)
131  .setLayer(0u)
132  .setBoundary(0u)
133  .value();
134  }
136  static constexpr Identifier makeHighestLevelMask() {
137  return makeLeadingLevelsMask(GeometryID(0u).setVolume(1u));
138  }
140  static constexpr bool equalWithinMask(Identifier lhs, Identifier rhs,
141  Identifier mask) {
142  return (lhs & mask) == (rhs & mask);
143  }
144 
148  template <typename id_getter_t>
149  void fillLookup(id_getter_t getId) {
150  m_ids.clear();
151  m_ids.reserve(m_elements.size());
152  m_masks.clear();
153  m_masks.reserve(m_elements.size());
154  for (const auto& element : m_elements) {
155  m_ids.push_back(getId(element).value());
156  m_masks.push_back(makeLeadingLevelsMask(getId(element).value()));
157  }
158  }
159 };
160 
161 // implementations
162 
163 template <typename value_t>
164 template <typename id_getter_t>
166  std::vector<Value>&& elements, id_getter_t getId)
167  : m_elements(std::move(elements)) {
168  // ensure values are sorted by geometry id
169  std::sort(m_elements.begin(), m_elements.end(),
170  [&](const auto& lhs, const auto& rhs) {
171  return getId(lhs) < getId(rhs);
172  });
173  // check that all elements are unique
174  auto duplicate = std::adjacent_find(m_elements.begin(), m_elements.end(),
175  [&](const auto& lhs, const auto& rhs) {
176  return getId(lhs) == getId(rhs);
177  });
178  if (duplicate != m_elements.end()) {
179  throw std::invalid_argument("Input elements contain duplicates");
180  }
181  fillLookup(getId);
182 }
183 
184 template <typename value_t>
187  assert((m_elements.size() == m_ids.size()) and
188  "Inconsistent container state: #elements != #ids");
189  assert((m_elements.size() == m_masks.size()) and
190  "Inconsistent container state: #elements != #masks");
191 
192  // we can not search for the element directly since the relevant one
193  // might be stored at a higher level. ids for higher levels would always
194  // be sorted before the requested id. searching for the first element
195  // after the requested ensures that we include the full hierarchy.
196  const auto it = std::upper_bound(m_ids.begin(), m_ids.end(), id.value());
197  auto i = std::distance(m_ids.begin(), it);
198 
199  // now go up the hierarchy to find the first matching element.
200  // example: the container stores three identifiers
201  //
202  // 2|x|x (volume-only)
203  // 2|3|x (volume and layer)
204  // 2|3|4 (volume, layer, and sensitive)
205  //
206  // where | marks level boundaries. searching for either 2|3|4, 2|3|7, or
207  // 2|4|x would first point to 2|3|4 and thus needs to go up the hierarchy.
208  while (0 < i) {
209  // always starts below item of interest due to upper bound search
210  --i;
211 
212  // if the input id does not even match at the highest hierarchy level
213  // with the current comparison id, then have reached the end of this
214  // hierarchy without finding a matching entry.
215  // NOTE this prevents adding a catch-all entry (all components equal to
216  // zero), but avoids having an unbounded search window all the way to
217  // the beginning of the ids container.
218  if (not equalWithinMask(id.value(), m_ids[i], makeHighestLevelMask())) {
219  return end();
220  }
221 
222  // since the search is going backwards in the sorted container, it
223  // progresses from more specific to less specific elements. the first
224  // match is automatically the appropriate one.
225  if (equalWithinMask(id.value(), m_ids[i], m_masks[i])) {
226  return std::next(begin(), i);
227  }
228  }
229 
230  // all options are exhausted and not matching element was found.
231  return end();
232 }
233 
234 } // namespace Acts