Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
KDTreeTrackingGeometryBuilder.cpp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file KDTreeTrackingGeometryBuilder.cpp
1 // This file is part of the Acts project.
2 //
3 // Copyright (C) 2022 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 
10 
14 #include "Acts/Geometry/Extent.hpp"
16 #include "Acts/Geometry/Layer.hpp"
31 
32 #include <cstddef>
33 #include <optional>
34 #include <ostream>
35 #include <stdexcept>
36 #include <utility>
37 
40  std::unique_ptr<const Logger> logger)
41  : m_cfg(cfg), m_logger(std::move(logger)) {
43 }
44 
45 std::unique_ptr<const Acts::TrackingGeometry>
47  const GeometryContext& gctx) const {
48  using MeasuredSurface =
49  std::pair<std::array<ActsScalar, 2u>, std::shared_ptr<Surface>>;
50  // Prepare all the surfaces
51  std::vector<MeasuredSurface> surfacesMeasured;
52  surfacesMeasured.reserve(m_cfg.surfaces.size());
53  for (auto& s : m_cfg.surfaces) {
54  auto ext = s->polyhedronRepresentation(gctx, 1u).extent();
55  surfacesMeasured.push_back(MeasuredSurface{
56  std::array<ActsScalar, 2u>{ext.medium(binZ), ext.medium(binR)}, s});
57  }
58 
59  // Create the KDTree
60  ACTS_INFO("Full KDTree has " << surfacesMeasured.size() << " surfaces.");
61  SurfaceKDT surfaceKDT(std::move(surfacesMeasured));
62 
63  // Walk through the proto builder
64  auto protoWorld = m_cfg.protoDetector.worldVolume;
65 
67 
68  auto worldTrackingVolume =
69  translateVolume(cCache, gctx, surfaceKDT, protoWorld);
70 
71  ACTS_INFO("Retrieved " << cCache.surfaceCounter
72  << " surfaces from the KDTree.");
73 
74  // return the geometry to the service
75  return std::make_unique<const Acts::TrackingGeometry>(worldTrackingVolume);
76 }
77 
78 std::shared_ptr<Acts::TrackingVolume>
80  Cache& cCache, const GeometryContext& gctx, const SurfaceKDT& kdt,
81  const ProtoVolume& ptVolume, const std::string& indent) const {
82  ACTS_DEBUG(indent << "Processing ProtoVolume: " << ptVolume.name);
83  std::vector<std::shared_ptr<const TrackingVolume>> translatedVolumes = {};
84 
85  // Volume extent
86  auto rangeR = ptVolume.extent.range(Acts::binR);
87  auto rangeZ = ptVolume.extent.range(Acts::binZ);
88 
89  // Simple gap volume
90  if (not ptVolume.container.has_value()) {
91  ACTS_VERBOSE(indent << "> empty volume to be built");
93  auto tVolume = m_cfg.trackingVolumeHelper->createGapTrackingVolume(
94  gctx, mtv, nullptr, rangeR.min(), rangeR.max(), rangeZ.min(),
95  rangeZ.max(), 0, false, ptVolume.name);
96  ACTS_DEBUG(indent << "> translated into gap volume bounds: "
97  << tVolume->volumeBounds());
98  return tVolume;
99  } else {
100  // Get the container information
101  auto& cts = ptVolume.container.value();
102 
103  // Container information must be present
104  if (cts.constituentVolumes.empty()) {
105  throw std::invalid_argument(
106  "KDTreeTrackingGeometryBuilder: no constituents given.");
107  }
108 
109  // This volume is a volume container
110  if (not cts.layerContainer) {
111  ACTS_VERBOSE(indent << "> volume container with "
112  << cts.constituentVolumes.size() << " constituents.");
113  for (auto& cVolume : cts.constituentVolumes) {
114  auto dtVolume = translateVolume(cCache, gctx, kdt, cVolume,
115  indent + m_cfg.hierarchyIndent);
116  translatedVolumes.push_back(dtVolume);
117  }
118  auto tVolume = m_cfg.trackingVolumeHelper->createContainerTrackingVolume(
119  gctx, translatedVolumes);
120  ACTS_DEBUG(indent << "> translated into container volume bounds: "
121  << tVolume->volumeBounds());
122  return tVolume;
123  } else {
124  // This volume is a layer container
125  std::vector<std::shared_ptr<const Layer>> layers = {};
126  ACTS_VERBOSE(indent << "> layer container with "
127  << cts.constituentVolumes.size() << " layers.");
128  for (auto& plVolume : cts.constituentVolumes) {
129  if (plVolume.internal.has_value()) {
130  layers.push_back(translateLayer(cCache, gctx, kdt, plVolume,
131  indent + m_cfg.hierarchyIndent));
132  } else {
133  ACTS_WARNING(indent << "> layer type volume has no internal "
134  "description, layer not built.");
135  }
136  }
137  // Create a new tracking volume with those layers
138  auto tVolume = m_cfg.trackingVolumeHelper->createTrackingVolume(
139  gctx, layers, {}, nullptr, rangeR.min(), rangeR.max(), rangeZ.min(),
140  rangeZ.max(), ptVolume.name);
141  ACTS_DEBUG(indent << "> translated into bounds: "
142  << tVolume->volumeBounds());
143  return tVolume;
144  }
145  }
146 
147  return nullptr;
148 }
149 
151 std::shared_ptr<const Acts::Layer>
153  Cache& cCache, const GeometryContext& gctx, const SurfaceKDT& kdt,
154  const ProtoVolume& plVolume, const std::string& indent) const {
155  ACTS_DEBUG(indent + "Processing ProtoVolume: " << plVolume.name);
156 
157  // This is only called if the volume has internal structure
158  auto& its = plVolume.internal.value();
159 
160  // Try to pull from the kd tree
161  RangeXD<2u, ActsScalar> zrRange;
162  zrRange[0u] = plVolume.extent.range(Acts::binZ);
163  zrRange[1u] = plVolume.extent.range(Acts::binR);
164 
165  auto layerSurfaces = kdt.rangeSearchWithKey(zrRange);
166  ACTS_VERBOSE(indent + ">> looking z/r range = " << zrRange.toString());
167  ACTS_VERBOSE(indent + ">> found " << layerSurfaces.size()
168  << " surfaces in the KDTree.");
169  cCache.surfaceCounter += layerSurfaces.size();
170 
171  std::shared_ptr<const Acts::Layer> tLayer = nullptr;
172 
173  if (layerSurfaces.size() == 1u) {
174  auto surface = layerSurfaces[0u].second;
175  const auto& transform = surface->transform(gctx);
176  if (its.layerType == Acts::Surface::SurfaceType::Cylinder) {
177  ACTS_VERBOSE(indent +
178  ">> creating cylinder layer from a single surface.");
179  // Get the bounds
180  auto cylinderBounds =
181  dynamic_cast<const CylinderBounds*>(&(surface->bounds()));
182  auto cylinderBoundsClone =
183  std::make_shared<const CylinderBounds>(*cylinderBounds);
184  auto cylinderLayer =
185  CylinderLayer::create(transform, cylinderBoundsClone, nullptr, 1.);
186  cylinderLayer->assignSurfaceMaterial(surface->surfaceMaterialSharedPtr());
187  tLayer = cylinderLayer;
188  } else if (its.layerType == Acts::Surface::SurfaceType::Disc) {
189  ACTS_VERBOSE(indent +
190  ">> creating cylinder layer from a single surface.");
191  // Get the bounds
192  auto radialBounds =
193  dynamic_cast<const RadialBounds*>(&(surface->bounds()));
194  auto radialBoundsClone =
195  std::make_shared<const RadialBounds>(*radialBounds);
196  auto discLayer =
197  DiscLayer::create(transform, radialBoundsClone, nullptr, 1.);
198  discLayer->assignSurfaceMaterial(surface->surfaceMaterialSharedPtr());
199  tLayer = discLayer;
200  } else {
201  throw std::invalid_argument(
202  "KDTreeTrackingGeometryBuilder: layer type is neither cylinder nor "
203  "disk.");
204  }
205 
206  } else if (layerSurfaces.size() > 1u) {
207  // Make a const collection out of the surfaces
208  std::vector<std::shared_ptr<const Surface>> cLayerSurfaces;
209  cLayerSurfaces.reserve(layerSurfaces.size());
210  for (const auto& s : layerSurfaces) {
211  cLayerSurfaces.push_back(s.second);
212  }
213 
216  std::size_t bins0 = 0;
217  std::size_t bins1 = 0;
218  // In case explicit binning is given
219  if (its.surfaceBinning.size() == 2u) {
220  bType0 = its.surfaceBinning[0u].type;
221  bType1 = its.surfaceBinning[1u].type;
222  // In case explicit bin numbers are given in addition
223  if (bType0 == Acts::equidistant and bType1 == Acts::equidistant and
224  its.surfaceBinning[0u].bins() > 1u and
225  its.surfaceBinning[1u].bins() > 1u) {
226  bins0 = its.surfaceBinning[0u].bins();
227  bins1 = its.surfaceBinning[1u].bins();
228  ACTS_VERBOSE(indent + ">> binning provided externally to be "
229  << bins0 << " x " << bins1 << ".");
230  }
231  }
232 
233  Acts::ProtoLayer pLayer(gctx, cLayerSurfaces);
234  pLayer.envelope = plVolume.extent.envelope();
235  if (its.layerType == Acts::Surface::SurfaceType::Cylinder) {
236  ACTS_VERBOSE(indent + ">> creating cylinder layer with "
237  << cLayerSurfaces.size() << " surfaces.");
238  // Forced equidistant or auto-binned
239  tLayer = (bins0 * bins1 > 0)
240  ? m_cfg.layerCreator->cylinderLayer(gctx, cLayerSurfaces,
241  bins0, bins1, pLayer)
242  : m_cfg.layerCreator->cylinderLayer(gctx, cLayerSurfaces,
243  bType0, bType1, pLayer);
244 
245  } else if (its.layerType == Acts::Surface::SurfaceType::Disc) {
246  ACTS_VERBOSE(indent + ">> creating disc layer with "
247  << cLayerSurfaces.size() << " surfaces.");
248  // Forced equidistant or auto-binned
249  tLayer = (bins0 * bins1 > 0)
250  ? m_cfg.layerCreator->discLayer(gctx, cLayerSurfaces, bins0,
251  bins1, pLayer)
252 
253  : m_cfg.layerCreator->discLayer(gctx, cLayerSurfaces, bType0,
254  bType1, pLayer);
255  } else {
256  throw std::invalid_argument(
257  "KDTreeTrackingGeometryBuilder: layer type is neither cylinder nor "
258  "disk.");
259  }
260  }
261  if (tLayer != nullptr and tLayer->representingVolume() != nullptr) {
262  ACTS_DEBUG(indent << "> translated into layer bounds: "
263  << tLayer->representingVolume()->volumeBounds());
264  } else {
265  throw std::runtime_error(
266  "KDTreeTrackingGeometryBuilder: layer was not built.");
267  }
268 
269  return tLayer;
270 }