ECCE @ EIC Software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
CylinderVolumeBuilder.cpp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file CylinderVolumeBuilder.cpp
1 // This file is part of the Acts project.
2 //
3 // Copyright (C) 2016-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 
10 // CylinderVolumeBuilder.cpp, Acts project
12 
14 #include <algorithm>
15 #include <vector>
25 
27  const Acts::CylinderVolumeBuilder::Config& cvbConfig,
28  std::unique_ptr<const Logger> logger)
29  : Acts::ITrackingVolumeBuilder(), m_cfg(), m_logger(std::move(logger)) {
30  setConfiguration(cvbConfig);
31 }
32 
34 
36  const Acts::CylinderVolumeBuilder::Config& cvbConfig) {
37  // @todo check consistency
38  // copy the configuration
39  m_cfg = cvbConfig;
40 }
41 
43  std::unique_ptr<const Logger> newLogger) {
44  m_logger = std::move(newLogger);
45 }
46 
47 std::shared_ptr<Acts::TrackingVolume>
49  const GeometryContext& gctx, TrackingVolumePtr existingVolume,
50  VolumeBoundsPtr externalBounds) const {
51  ACTS_DEBUG("Configured to build volume : " << m_cfg.volumeName);
52  if (existingVolume) {
53  ACTS_DEBUG("- will wrap/enclose : " << existingVolume->volumeName());
54  }
55 
56  // the return volume
57  // -----------------------------------------------------------------------------
59 
60  // now analyize the layers that are provided
61  // -----------------------------------------------------
62  LayerVector negativeLayers;
63  LayerVector centralLayers;
64  LayerVector positiveLayers;
65 
66  // the wrapping configuration
67  WrappingConfig wConfig;
68 
69  // the layers are built by the layer builder
70  if (m_cfg.layerBuilder) {
71  // the negative Layers
72  negativeLayers = m_cfg.layerBuilder->negativeLayers(gctx);
73  // the central Layers
74  centralLayers = m_cfg.layerBuilder->centralLayers(gctx);
75  // the positive Layer
76  positiveLayers = m_cfg.layerBuilder->positiveLayers(gctx);
77  }
78 
79  // Build the confined volumes
80  MutableTrackingVolumeVector centralVolumes;
81  if (m_cfg.ctVolumeBuilder) {
82  centralVolumes = m_cfg.ctVolumeBuilder->centralVolumes();
83  }
84 
85  // (0) PREP WORK ------------------------------------------------
86  //
87  // a) volume config of the existing volume
88  if (existingVolume) {
89  // volume and existing volume
90  auto existingBounds = dynamic_cast<const CylinderVolumeBounds*>(
91  &existingVolume->volumeBounds());
92  // set the inside values
93  wConfig.existingVolumeConfig.present = true;
94  wConfig.existingVolumeConfig.rMin =
95  existingBounds->get(CylinderVolumeBounds::eMinR);
96  wConfig.existingVolumeConfig.rMax =
97  existingBounds->get(CylinderVolumeBounds::eMaxR);
98  wConfig.existingVolumeConfig.zMin =
99  existingVolume->center().z() -
100  existingBounds->get(CylinderVolumeBounds::eHalfLengthZ);
101  wConfig.existingVolumeConfig.zMax =
102  existingVolume->center().z() +
103  existingBounds->get(CylinderVolumeBounds::eHalfLengthZ);
104  }
105  //
106  // b) outside config
107  // the volume config for the Outside
108  VolumeConfig externalBoundConfig;
109  if (externalBounds) {
110  const CylinderVolumeBounds* ocvBounds =
111  dynamic_cast<const CylinderVolumeBounds*>(externalBounds.get());
112  // the cast to CylinderVolumeBounds needs to be successful
113  if (ocvBounds != nullptr) {
114  // get values from the out bounds
115  wConfig.externalVolumeConfig.present = true;
116  wConfig.externalVolumeConfig.rMin =
117  ocvBounds->get(CylinderVolumeBounds::eMinR);
118  wConfig.externalVolumeConfig.rMax =
119  ocvBounds->get(CylinderVolumeBounds::eMaxR);
120  wConfig.externalVolumeConfig.zMin =
122  wConfig.externalVolumeConfig.zMax =
124  }
125  }
126 
127  // ---------------------------------------------
128  // The Volume Config of the SubVolumes
129  // ---------------------------------------------
130  // sub volume / layer configuration (subVolumes only build of layers are
131  // present)
132  // --------------------------------------------------------------------------
133  //
134  // possbile configurations are (so far only synchronised):
135  //
136  // | Negative Endcap | Barrel | Positive Endcap | - all layers present
137  // | Barrel | - barrel present
138  // | Negative Endcap | | Positive Endcap | - only endcaps present
139  // - no layer present
140  // Check if already given through configuration
141  //
142  // (A) volume configuration
143  //
144 
145  // Find out with Layer analysis
146  // analyze the layers
147  wConfig.nVolumeConfig = analyzeContent(gctx, negativeLayers, {}); // TODO
148  wConfig.cVolumeConfig = analyzeContent(gctx, centralLayers, centralVolumes);
149  wConfig.pVolumeConfig = analyzeContent(gctx, positiveLayers, {}); // TODO
150 
151  std::string layerConfiguration = "|";
152  if (wConfig.nVolumeConfig) {
153  // negative layers are present
154  ACTS_VERBOSE("Negative layers are present: rmin, rmax | zmin, zmax = "
155  << wConfig.nVolumeConfig.toString());
156  // add to the string output
157  layerConfiguration += " Negative Endcap |";
158  }
159  if (wConfig.cVolumeConfig) {
160  // central layers are present
161  ACTS_VERBOSE("Central layers are present: rmin, rmax | zmin, zmax = "
162  << wConfig.cVolumeConfig.toString());
163  // add to the string output
164  layerConfiguration += " Barrel |";
165  }
166  if (wConfig.pVolumeConfig) {
167  // positive layers are present
168  ACTS_VERBOSE("Positive layers are present: rmin, rmax | zmin, zmax = "
169  << wConfig.pVolumeConfig.toString());
170  // add to the string output
171  layerConfiguration += " Positive Endcap |";
172  }
173  // screen output
174  ACTS_DEBUG("Layer configuration is : " << layerConfiguration);
175 
176  // (B) LAYER Config SYNCHRONISATION ----------------------------------
177  // synchronise the layer config
178  ACTS_VERBOSE("Configurations after layer parsing " << '\n'
179  << wConfig.toString());
180  // first let us arrange the new container volume
181  wConfig.configureContainerVolume();
182  ACTS_VERBOSE("Configuration after container synchronisation "
183  << '\n'
184  << wConfig.toString());
185  // now let's understand the wrapping if needed
186  if (wConfig.existingVolumeConfig) {
187  wConfig.wrapInsertAttach();
188  ACTS_VERBOSE("Configuration after wrapping, insertion, attachment "
189  << '\n'
190  << wConfig.toString());
191  } else {
192  // no wrapping around inner volume needed
193  // however there could be central, positive & negative volume which will
194  // need to be put into a container volume
195  wConfig.wCondition = NoWrapping;
196  }
197 
198  // (C) VOLUME CREATION ----------------------------------
199  auto tvHelper = m_cfg.trackingVolumeHelper;
200  // the barrel is always created
201  auto barrel =
202  wConfig.cVolumeConfig
203  ? tvHelper->createTrackingVolume(
204  gctx, wConfig.cVolumeConfig.layers,
205  wConfig.cVolumeConfig.volumes, m_cfg.volumeMaterial,
206  wConfig.cVolumeConfig.rMin, wConfig.cVolumeConfig.rMax,
207  wConfig.cVolumeConfig.zMin, wConfig.cVolumeConfig.zMax,
208  m_cfg.volumeName + "::Barrel")
209  : nullptr;
210 
211  // Helper method to check for
212 
213  // Helper method to create endcap volume
214  auto createEndcap =
215  [&](VolumeConfig& centralConfig, VolumeConfig& endcapConfig,
216  const std::string& endcapName) -> MutableTrackingVolumePtr {
217  // No config - no volume
218  if (not endcapConfig) {
219  return nullptr;
220  }
221  // Check for ring layout
222  if (m_cfg.checkRingLayout) {
223  ACTS_DEBUG("Configured to check for ring layout - parsing layers.");
224  // Parsing loop for ring layout
225  std::vector<double> innerRadii = {};
226  std::vector<double> outerRadii = {};
227  for (const auto& elay : endcapConfig.layers) {
228  auto discBounds = dynamic_cast<const RadialBounds*>(
229  &(elay->surfaceRepresentation().bounds()));
230  if (discBounds != nullptr) {
231  double tolerance = m_cfg.ringTolerance;
232  // Search for the rmin value - and insert if necessary
233  double rMin = discBounds->rMin();
234  auto innerSearch = std::find_if(
235  innerRadii.begin(), innerRadii.end(), [&](double reference) {
236  return std::abs(rMin - reference) < tolerance;
237  });
238  if (innerSearch == innerRadii.end()) {
239  innerRadii.push_back(rMin);
240  }
241  // Search for the rmax value - and insert if necessary
242  double rMax = discBounds->rMax();
243  auto outerSearch = std::find_if(
244  outerRadii.begin(), outerRadii.end(), [&](double reference) {
245  return std::abs(rMax - reference) < tolerance;
246  });
247  if (outerSearch == outerRadii.end()) {
248  outerRadii.push_back(rMax);
249  }
250  }
251  }
252  // Result of the parsing loop
253  if (innerRadii.size() == outerRadii.size() and not innerRadii.empty()) {
254  bool consistent = true;
255  // The inter volume radii
256  std::vector<double> interRadii = {};
257  for (int ir = 1; ir < int(innerRadii.size()); ++ir) {
258  // Check whether inner/outer radii are consistent
259  if (outerRadii[ir - 1] < innerRadii[ir]) {
260  interRadii.push_back(0.5 * (outerRadii[ir - 1] + innerRadii[ir]));
261  } else {
262  consistent = false;
263  break;
264  }
265  }
266  // Continue if the ring layout is consistent
267  if (consistent) {
268  ACTS_DEBUG("Ring layout detection: " << innerRadii.size()
269  << " volumes.");
270  // Separate the Layers into volumes
271  std::vector<std::pair<double, double>> volumeRminRmax = {};
272  for (unsigned int ii = 0; ii < interRadii.size(); ++ii) {
273  if (ii == 0) {
274  volumeRminRmax.push_back({endcapConfig.rMin, interRadii[ii]});
275  }
276  if (ii + 1 < interRadii.size()) {
277  volumeRminRmax.push_back({interRadii[ii], interRadii[ii + 1]});
278  } else {
279  volumeRminRmax.push_back({interRadii[ii], endcapConfig.rMax});
280  }
281  }
282  auto ringLayers =
283  std::vector<LayerVector>(innerRadii.size(), LayerVector());
284  // Filling loop
285  for (const auto& elay : endcapConfig.layers) {
286  // Getting the reference radius
287  double test =
288  elay->surfaceRepresentation().binningPositionValue(gctx, binR);
289  // Find the right bin
290  auto ringVolume = std::find_if(
291  volumeRminRmax.begin(), volumeRminRmax.end(),
292  [&](const auto& reference) {
293  return (test > reference.first and test < reference.second);
294  });
295  if (ringVolume != volumeRminRmax.end()) {
296  unsigned int ringBin =
297  std::distance(volumeRminRmax.begin(), ringVolume);
298  ringLayers[ringBin].push_back(elay);
299  }
300  }
301  // Subvolume construction
302  ACTS_DEBUG("Ring layout configuration: ");
303  // Endcap container
304  std::vector<TrackingVolumePtr> endcapContainer;
305  unsigned int ir = 0;
306  for (auto& rLayers : ringLayers) {
307  ACTS_DEBUG(" - ring volume " << ir << " with " << rLayers.size()
308  << " layers, and rmin/rmax = "
309  << volumeRminRmax[ir].first << "/"
310  << volumeRminRmax[ir].second);
311  endcapContainer.push_back(tvHelper->createTrackingVolume(
312  gctx, rLayers, centralConfig.volumes, m_cfg.volumeMaterial,
313  volumeRminRmax[ir].first, volumeRminRmax[ir].second,
314  endcapConfig.zMin, endcapConfig.zMax,
315  m_cfg.volumeName + endcapName + std::string("::Ring") +
316  std::to_string(ir)));
317  ++ir;
318  }
319  // Return a container of ring volumes
320  return tvHelper->createContainerTrackingVolume(gctx, endcapContainer);
321  }
322  }
323  }
324 
325  // No ring layout - return single volume
326  return tvHelper->createTrackingVolume(
327  gctx, endcapConfig.layers, centralConfig.volumes, m_cfg.volumeMaterial,
328  endcapConfig.rMin, endcapConfig.rMax, endcapConfig.zMin,
329  endcapConfig.zMax, m_cfg.volumeName + endcapName);
330  };
331 
332  // The negative endcap is created if present
333  auto nEndcap = createEndcap(wConfig.cVolumeConfig, wConfig.nVolumeConfig,
334  "::NegativeEndcap");
335 
336  // The positive endcap is created if present
337  auto pEndcap = createEndcap(wConfig.cVolumeConfig, wConfig.pVolumeConfig,
338  "::PositiveEndcap");
339 
340  ACTS_DEBUG("Newly created volume(s) will be " << wConfig.wConditionScreen);
341  // Standalone container, full wrapping, full insertion & if no existing volume
342  // is present needs a bare triple
343  if (wConfig.wCondition == Wrapping || wConfig.wCondition == Inserting ||
344  wConfig.wCondition == NoWrapping) {
345  ACTS_VERBOSE("Combined new container is being built.");
346  // Stuff into the container what you have
347  std::vector<TrackingVolumePtr> volumesContainer;
348  if (nEndcap) {
349  volumesContainer.push_back(nEndcap);
350  volume = nEndcap;
351  // Set the inner or outer material
352  if (not m_cfg.buildToRadiusZero) {
353  volume->assignBoundaryMaterial(m_cfg.boundaryMaterial[0],
355  }
356  volume->assignBoundaryMaterial(m_cfg.boundaryMaterial[1],
358  volume->assignBoundaryMaterial(m_cfg.boundaryMaterial[2],
360  volume->assignBoundaryMaterial(m_cfg.boundaryMaterial[3],
362  }
363  if (barrel) {
364  // Assign boundary material if existing
365  volumesContainer.push_back(barrel);
366  volume = barrel;
367  // Set the inner or outer material
368  if (not m_cfg.buildToRadiusZero) {
369  volume->assignBoundaryMaterial(m_cfg.boundaryMaterial[0],
371  }
372  volume->assignBoundaryMaterial(m_cfg.boundaryMaterial[1],
374  volume->assignBoundaryMaterial(m_cfg.boundaryMaterial[3],
376  volume->assignBoundaryMaterial(m_cfg.boundaryMaterial[4],
378  }
379  if (pEndcap) {
380  volumesContainer.push_back(pEndcap);
381  volume = pEndcap;
382  // Set the inner or outer material
383  if (not m_cfg.buildToRadiusZero) {
384  volume->assignBoundaryMaterial(m_cfg.boundaryMaterial[0],
386  }
387  volume->assignBoundaryMaterial(m_cfg.boundaryMaterial[1],
389  volume->assignBoundaryMaterial(m_cfg.boundaryMaterial[4],
391  volume->assignBoundaryMaterial(m_cfg.boundaryMaterial[5],
393  }
394  // and low lets create the new volume
395  volume =
396  volumesContainer.size() > 1
397  ? tvHelper->createContainerTrackingVolume(gctx, volumesContainer)
398  : volume;
399  } else if (wConfig.wCondition != Attaching) {
400  // the new volume is the only one present
401  volume = nEndcap ? nEndcap : (barrel ? barrel : pEndcap);
402  }
403 
404  // Prepare the gap volumes first
405  TrackingVolumePtr existingVolumeCp = existingVolume;
406  // Check if further action is needed on existing volumes and gap volumes
407  if (existingVolumeCp) {
408  // Check if gaps are needed
409  std::vector<TrackingVolumePtr> existingContainer;
410  if (wConfig.fGapVolumeConfig) {
411  // create the gap volume
412  auto fGap = tvHelper->createGapTrackingVolume(
413  gctx, wConfig.cVolumeConfig.volumes, m_cfg.volumeMaterial,
414  wConfig.fGapVolumeConfig.rMin, wConfig.fGapVolumeConfig.rMax,
415  wConfig.fGapVolumeConfig.zMin, wConfig.fGapVolumeConfig.zMax, 1,
416  false, m_cfg.volumeName + "::fGap");
417  // push it back into the list
418  existingContainer.push_back(fGap);
419  }
420  existingContainer.push_back(existingVolumeCp);
421  if (wConfig.sGapVolumeConfig) {
422  // create the gap volume
423  auto sGap = tvHelper->createGapTrackingVolume(
424  gctx, wConfig.cVolumeConfig.volumes, m_cfg.volumeMaterial,
425  wConfig.sGapVolumeConfig.rMin, wConfig.sGapVolumeConfig.rMax,
426  wConfig.sGapVolumeConfig.zMin, wConfig.sGapVolumeConfig.zMax, 1,
427  false, m_cfg.volumeName + "::sGap");
428  // push it back into the list
429  existingContainer.push_back(sGap);
430  }
431 
432  // And low lets create the new existing volume with gaps
433  existingVolumeCp =
434  existingContainer.size() > 1
435  ? tvHelper->createContainerTrackingVolume(gctx, existingContainer)
436  : existingVolumeCp;
437 
438  // for central wrapping or inserting, we need to update once more
439  // clear the container
440  existingContainer.clear();
441  if (wConfig.wCondition == CentralWrapping) {
442  existingContainer.push_back(existingVolumeCp);
443  existingContainer.push_back(barrel);
444  } else if (wConfig.wCondition == CentralInserting) {
445  existingContainer.push_back(barrel);
446  existingContainer.push_back(existingVolumeCp);
447  }
448  // update
449  existingVolumeCp =
450  !existingContainer.empty()
451  ? tvHelper->createContainerTrackingVolume(gctx, existingContainer)
452  : existingVolumeCp;
453 
454  std::vector<TrackingVolumePtr> totalContainer;
455  // check what to do with the existing
456  if (wConfig.wCondition == Attaching ||
457  wConfig.wCondition == CentralWrapping ||
458  wConfig.wCondition == CentralInserting) {
459  if (nEndcap) {
460  totalContainer.push_back(nEndcap);
461  }
462  totalContainer.push_back(existingVolumeCp);
463  if (pEndcap) {
464  totalContainer.push_back(pEndcap);
465  }
466  } else if (wConfig.wCondition == Inserting && volume) {
467  totalContainer.push_back(volume);
468  totalContainer.push_back(existingVolumeCp);
469  } else if (wConfig.wCondition == Wrapping && volume) {
470  totalContainer.push_back(existingVolumeCp);
471  totalContainer.push_back(volume);
472  } else {
473  ACTS_ERROR("Misconfiguration in volume building detected.");
474  return nullptr;
475  }
476  // now create the new container volume
477  volume = tvHelper->createContainerTrackingVolume(gctx, totalContainer);
478  }
479 
480  return volume;
481 }
482 
483 // -----------------------------
485  const GeometryContext& gctx, const LayerVector& lVector,
486  const MutableTrackingVolumeVector& mtvVector) const {
487  // @TODO add envelope tolerance
488  //
489  // return object
490  VolumeConfig lConfig;
491  // only if the vector is present it can actually be analyzed
492  if (!lVector.empty() || !mtvVector.empty()) {
493  // we have layers
494  lConfig.present = true;
495  // loop over the layer
496  for (auto& layer : lVector) {
497  // the thickness of the layer needs to be taken into account
498  double thickness = layer->thickness();
499  // get the center of the layer
500  const Vector3D& center = layer->surfaceRepresentation().center(gctx);
501  // check if it is a cylinder layer
502  const CylinderLayer* cLayer =
503  dynamic_cast<const CylinderLayer*>(layer.get());
504  if (cLayer != nullptr) {
505  // now we have access to all the information
506  double rMinC =
508  0.5 * thickness;
509  double rMaxC =
511  0.5 * thickness;
512 
513  double hZ = cLayer->surfaceRepresentation().bounds().get(
515  takeSmaller(lConfig.rMin, rMinC - m_cfg.layerEnvelopeR.first);
516  takeBigger(lConfig.rMax, rMaxC + m_cfg.layerEnvelopeR.second);
517  takeSmaller(lConfig.zMin, center.z() - hZ - m_cfg.layerEnvelopeZ);
518  takeBigger(lConfig.zMax, center.z() + hZ + m_cfg.layerEnvelopeZ);
519  }
520  // proceed further if it is a Disc layer
521  const RadialBounds* dBounds = dynamic_cast<const RadialBounds*>(
522  &(layer->surfaceRepresentation().bounds()));
523  if (dBounds != nullptr) {
524  // now we have access to all the information
525  double rMinD = dBounds->rMin();
526  double rMaxD = dBounds->rMax();
527  double zMinD = center.z() - 0.5 * thickness;
528  double zMaxD = center.z() + 0.5 * thickness;
529  takeSmaller(lConfig.rMin, rMinD - m_cfg.layerEnvelopeR.first);
530  takeBigger(lConfig.rMax, rMaxD + m_cfg.layerEnvelopeR.second);
531  takeSmaller(lConfig.zMin, zMinD - m_cfg.layerEnvelopeZ);
532  takeBigger(lConfig.zMax, zMaxD + m_cfg.layerEnvelopeZ);
533  }
534  }
535  for (auto& volume : mtvVector) {
536  const CylinderVolumeBounds* cvBounds =
537  dynamic_cast<const CylinderVolumeBounds*>(&volume->volumeBounds());
538  if (cvBounds != nullptr) {
539  takeSmaller(lConfig.rMin, cvBounds->get(CylinderVolumeBounds::eMinR));
540  takeBigger(lConfig.rMax, cvBounds->get(CylinderVolumeBounds::eMaxR));
541  takeSmaller(lConfig.zMin,
543  takeBigger(lConfig.zMax,
545  }
546  }
547  }
548 
549  // Set the layers to the layer vector
550  lConfig.layers = lVector;
551  // set the layers to the layer vector
552  lConfig.volumes = mtvVector;
553  // overwrite to radius 0 if needed
554  if (m_cfg.buildToRadiusZero) {
555  ACTS_VERBOSE("This layer builder is configured to build to the beamline.");
556  lConfig.rMin = 0.;
557  }
558 
559  // and return what you have
560  return lConfig;
561 }