Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
CuboidVolumeBuilder.cpp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file CuboidVolumeBuilder.cpp
1 // This file is part of the Acts project.
2 //
3 // Copyright (C) 2018-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 
10 
16 #include "Acts/Geometry/Extent.hpp"
30 
31 #include <algorithm>
32 #include <limits>
33 #include <stdexcept>
34 #include <type_traits>
35 
36 std::shared_ptr<const Acts::Surface> Acts::CuboidVolumeBuilder::buildSurface(
37  const GeometryContext& /*gctx*/,
39  std::shared_ptr<PlaneSurface> surface;
40 
41  // Build transformation
42  Transform3 trafo(Transform3::Identity() * cfg.rotation);
43  trafo.translation() = cfg.position;
44 
45  // Create and store surface
46  if (cfg.detElementConstructor) {
47  surface = Surface::makeShared<PlaneSurface>(
48  cfg.rBounds,
49  *(cfg.detElementConstructor(trafo, cfg.rBounds, cfg.thickness)));
50  } else {
51  surface = Surface::makeShared<PlaneSurface>(trafo, cfg.rBounds);
52  }
53  surface->assignSurfaceMaterial(cfg.surMat);
54  return surface;
55 }
56 
57 std::shared_ptr<const Acts::Layer> Acts::CuboidVolumeBuilder::buildLayer(
58  const GeometryContext& gctx,
60  if (cfg.surfaces.empty() && cfg.surfaceCfg.empty()) {
61  throw std::runtime_error{
62  "Neither surfaces nor config to build surfaces was provided. Cannot "
63  "proceed"};
64  }
65 
66  // Build the surface
67  if (cfg.surfaces.empty()) {
68  for (const auto& sCfg : cfg.surfaceCfg) {
69  cfg.surfaces.push_back(buildSurface(gctx, sCfg));
70  }
71  }
72  // Build transformation centered at the surface position
73  Vector3 centroid{0., 0., 0.};
74 
75  for (const auto& surface : cfg.surfaces) {
76  centroid += surface->transform(gctx).translation();
77  }
78 
79  centroid /= cfg.surfaces.size();
80 
81  // In the case the layer configuration doesn't define the rotation of the
82  // layer use the orientation of the first surface to define the layer rotation
83  // in space.
84  Transform3 trafo = Transform3::Identity();
85  trafo.translation() = centroid;
86  if (cfg.rotation) {
87  trafo.linear() = *cfg.rotation;
88  } else {
89  trafo.linear() = cfg.surfaces.front()->transform(gctx).rotation();
90  }
91 
93  lCfg.surfaceArrayCreator = std::make_shared<const SurfaceArrayCreator>();
94  LayerCreator layerCreator(lCfg);
95  ProtoLayer pl{gctx, cfg.surfaces};
96  pl.envelope[binX] = cfg.envelopeX;
97  pl.envelope[binY] = cfg.envelopeY;
98  pl.envelope[binZ] = cfg.envelopeZ;
99  return layerCreator.planeLayer(gctx, cfg.surfaces, cfg.binsY, cfg.binsZ,
100  BinningValue::binX, pl, trafo);
101 }
102 
103 std::pair<double, double> Acts::CuboidVolumeBuilder::binningRange(
104  const GeometryContext& gctx,
106  using namespace UnitLiterals;
107  // Construct return value
108  std::pair<double, double> minMax = std::make_pair(
109  std::numeric_limits<double>::max(), -std::numeric_limits<double>::max());
110 
111  // Compute the min volume boundaries for computing the binning start
112  // See
113  // https://acts.readthedocs.io/en/latest/core/geometry.html#geometry-building
114  // !! IMPORTANT !! The volume is assumed to be already rotated into the
115  // telescope geometry
116  Vector3 minVolumeBoundaries = cfg.position - 0.5 * cfg.length;
117  Vector3 maxVolumeBoundaries = cfg.position + 0.5 * cfg.length;
118 
119  // Compute first the min-max from the layers
120 
121  for (const auto& layercfg : cfg.layerCfg) {
122  // recreating the protolayer for each layer => slow, but only few sensors
123  ProtoLayer pl{gctx, layercfg.surfaces};
124  pl.envelope[binX] = layercfg.envelopeX;
125 
126  double surfacePosMin = pl.min(binX);
127  double surfacePosMax = pl.max(binX);
128 
129  // Test if new extreme is found and set it
130  if (surfacePosMin < minMax.first) {
131  minMax.first = surfacePosMin;
132  }
133  if (surfacePosMax > minMax.second) {
134  minMax.second = surfacePosMax;
135  }
136  }
137 
138  // Use the volume boundaries as limits for the binning
139  minMax.first = std::min(minMax.first, minVolumeBoundaries(binX));
140  minMax.second = std::max(minMax.second, maxVolumeBoundaries(binX));
141 
142  return minMax;
143 }
144 
145 std::shared_ptr<Acts::TrackingVolume> Acts::CuboidVolumeBuilder::buildVolume(
146  const GeometryContext& gctx,
148  // Build transformation
149  Transform3 trafo(Transform3::Identity());
150  trafo.translation() = cfg.position;
151  // Set bounds
152  auto bounds = std::make_shared<const CuboidVolumeBounds>(
153  cfg.length.x() * 0.5, cfg.length.y() * 0.5, cfg.length.z() * 0.5);
154 
155  if (cfg.layerCfg.empty()) {
156  // Build dummy layer if no layer is given (tmp solution)
157  SurfaceConfig sCfg;
158  sCfg.position = cfg.position;
159  // Rotation of the surfaces: +pi/2 around axis y
160  Vector3 xPos(0., 0., 1.);
161  Vector3 yPos(0., 1., 0.);
162  Vector3 zPos(-1., 0., 0.);
163  sCfg.rotation.col(0) = xPos;
164  sCfg.rotation.col(1) = yPos;
165  sCfg.rotation.col(2) = zPos;
166  // Bounds
167  sCfg.rBounds = std::make_shared<const RectangleBounds>(
168  RectangleBounds(cfg.length.y() * 0.5, cfg.length.z() * 0.5));
169 
170  LayerConfig lCfg;
171  lCfg.surfaceCfg = {sCfg};
172  lCfg.envelopeX = {0.1 * UnitConstants::mm, 0.1 * UnitConstants::mm};
173  lCfg.envelopeY = {0.1 * UnitConstants::mm, 0.1 * UnitConstants::mm};
174  lCfg.envelopeZ = {0.1 * UnitConstants::mm, 0.1 * UnitConstants::mm};
175 
176  cfg.layerCfg.push_back(lCfg);
177  }
178 
179  // Gather the layers
180  LayerVector layVec;
181  if (cfg.layers.empty()) {
182  cfg.layers.reserve(cfg.layerCfg.size());
183 
184  for (auto& layerCfg : cfg.layerCfg) {
185  cfg.layers.push_back(buildLayer(gctx, layerCfg));
186  layVec.push_back(cfg.layers.back());
187  }
188  } else {
189  for (auto& lay : cfg.layers) {
190  layVec.push_back(lay);
191  }
192  }
193 
194  // Build layer array
195  std::pair<double, double> minMax = binningRange(gctx, cfg);
197  LayerArrayCreator layArrCreator(
198  lacCnf, getDefaultLogger("LayerArrayCreator", Logging::INFO));
199  std::unique_ptr<const LayerArray> layArr(
200  layArrCreator.layerArray(gctx, layVec, minMax.first, minMax.second,
202 
203  // Build confined volumes
204  if (cfg.trackingVolumes.empty()) {
205  for (VolumeConfig vc : cfg.volumeCfg) {
206  cfg.trackingVolumes.push_back(buildVolume(gctx, vc));
207  }
208  }
209 
210  std::shared_ptr<TrackingVolume> trackVolume;
211  if (layVec.empty()) {
212  // Build TrackingVolume
213  trackVolume =
214  TrackingVolume::create(trafo, bounds, cfg.volumeMaterial, nullptr,
215  nullptr, cfg.trackingVolumes, cfg.name);
216  } else {
217  // Build TrackingVolume
218  trackVolume = TrackingVolume::create(trafo, bounds, cfg.volumeMaterial,
219  std::move(layArr), nullptr,
220  cfg.trackingVolumes, cfg.name);
221  }
222  return trackVolume;
223 }
224 
227  Acts::VolumeBoundsPtr /*bounds*/) const {
228  // Build volumes
229  std::vector<std::shared_ptr<TrackingVolume>> volumes;
230  volumes.reserve(m_cfg.volumeCfg.size());
231  for (VolumeConfig volCfg : m_cfg.volumeCfg) {
232  volumes.push_back(buildVolume(gctx, volCfg));
233  }
234 
235  // Sort the volumes vectors according to the center location, otherwise the
236  // binning boundaries will fail
237  std::sort(volumes.begin(), volumes.end(),
238  [](const TrackingVolumePtr& lhs, const TrackingVolumePtr& rhs) {
239  return lhs->center().x() < rhs->center().x();
240  });
241 
242  // Glue volumes
243  for (unsigned int i = 0; i < volumes.size() - 1; i++) {
244  volumes[i + 1]->glueTrackingVolume(
245  gctx, BoundarySurfaceFace::negativeFaceYZ, volumes[i].get(),
247  volumes[i]->glueTrackingVolume(gctx, BoundarySurfaceFace::positiveFaceYZ,
248  volumes[i + 1].get(),
250  }
251 
252  // Translation
253  Transform3 trafo(Transform3::Identity());
254  trafo.translation() = m_cfg.position;
255 
256  // Size of the volume
257  auto volume = std::make_shared<const CuboidVolumeBounds>(
258  m_cfg.length.x() * 0.5, m_cfg.length.y() * 0.5, m_cfg.length.z() * 0.5);
259 
260  // Build vector of confined volumes
261  std::vector<std::pair<TrackingVolumePtr, Vector3>> tapVec;
262  tapVec.reserve(m_cfg.volumeCfg.size());
263  for (auto& tVol : volumes) {
264  tapVec.push_back(std::make_pair(tVol, tVol->center()));
265  }
266 
267  // Set bin boundaries along binning
268  std::vector<float> binBoundaries;
269  binBoundaries.push_back(volumes[0]->center().x() -
270  m_cfg.volumeCfg[0].length.x() * 0.5);
271  for (size_t i = 0; i < volumes.size(); i++) {
272  binBoundaries.push_back(volumes[i]->center().x() +
273  m_cfg.volumeCfg[i].length.x() * 0.5);
274  }
275 
276  // Build binning
277  BinningData binData(BinningOption::open, BinningValue::binX, binBoundaries);
278  auto bu = std::make_unique<const BinUtility>(binData);
279 
280  // Build TrackingVolume array
281  std::shared_ptr<const TrackingVolumeArray> trVolArr(
283 
284  // Create world volume
286  TrackingVolume::create(trafo, volume, trVolArr, "World"));
287 
288  return mtvp;
289 }