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