Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-09-15 08:14:03

0001 // This file is part of the ACTS project.
0002 //
0003 // Copyright (C) 2016 CERN for the benefit of the ACTS project
0004 //
0005 // This Source Code Form is subject to the terms of the Mozilla Public
0006 // License, v. 2.0. If a copy of the MPL was not distributed with this
0007 // file, You can obtain one at https://mozilla.org/MPL/2.0/.
0008 
0009 #include "Acts/Geometry/CylinderVolumeBuilder.hpp"
0010 
0011 #include "Acts/Definitions/Algebra.hpp"
0012 #include "Acts/Definitions/Common.hpp"
0013 #include "Acts/Geometry/BoundarySurfaceFace.hpp"
0014 #include "Acts/Geometry/CylinderLayer.hpp"
0015 #include "Acts/Geometry/CylinderVolumeBounds.hpp"
0016 #include "Acts/Geometry/IConfinedTrackingVolumeBuilder.hpp"
0017 #include "Acts/Geometry/ILayerBuilder.hpp"
0018 #include "Acts/Geometry/ITrackingVolumeHelper.hpp"
0019 #include "Acts/Geometry/Layer.hpp"
0020 #include "Acts/Geometry/TrackingVolume.hpp"
0021 #include "Acts/Geometry/VolumeBounds.hpp"
0022 #include "Acts/Surfaces/CylinderBounds.hpp"
0023 #include "Acts/Surfaces/CylinderSurface.hpp"
0024 #include "Acts/Surfaces/RadialBounds.hpp"
0025 #include "Acts/Surfaces/Surface.hpp"
0026 
0027 #include <algorithm>
0028 #include <cmath>
0029 #include <iterator>
0030 #include <vector>
0031 
0032 #include <boost/algorithm/string.hpp>
0033 
0034 namespace Acts {
0035 
0036 CylinderVolumeBuilder::CylinderVolumeBuilder(
0037     const CylinderVolumeBuilder::Config& cvbConfig,
0038     std::unique_ptr<const Logger> logger)
0039     : ITrackingVolumeBuilder(), m_cfg(), m_logger(std::move(logger)) {
0040   setConfiguration(cvbConfig);
0041 }
0042 
0043 CylinderVolumeBuilder::~CylinderVolumeBuilder() = default;
0044 
0045 void CylinderVolumeBuilder::setConfiguration(
0046     const CylinderVolumeBuilder::Config& cvbConfig) {
0047   // @todo check consistency
0048   // copy the configuration
0049   m_cfg = cvbConfig;
0050 }
0051 
0052 void CylinderVolumeBuilder::setLogger(std::unique_ptr<const Logger> newLogger) {
0053   m_logger = std::move(newLogger);
0054 }
0055 
0056 std::shared_ptr<TrackingVolume> CylinderVolumeBuilder::trackingVolume(
0057     const GeometryContext& gctx, TrackingVolumePtr existingVolume,
0058     std::shared_ptr<const VolumeBounds> externalBounds) const {
0059   ACTS_DEBUG("Configured to build volume : " << m_cfg.volumeName);
0060   if (existingVolume) {
0061     ACTS_DEBUG("- will wrap/enclose : " << existingVolume->volumeName());
0062   }
0063 
0064   // the return volume
0065   // -----------------------------------------------------------------------------
0066   MutableTrackingVolumePtr volume = nullptr;
0067 
0068   // now analyize the layers that are provided
0069   // -----------------------------------------------------
0070   ACTS_DEBUG("-> Building layers");
0071   LayerVector negativeLayers;
0072   LayerVector centralLayers;
0073   LayerVector positiveLayers;
0074 
0075   // the wrapping configuration
0076   WrappingConfig wConfig;
0077 
0078   // the layers are built by the layer builder
0079   if (m_cfg.layerBuilder) {
0080     // the negative Layers
0081     negativeLayers = m_cfg.layerBuilder->negativeLayers(gctx);
0082     // the central Layers
0083     centralLayers = m_cfg.layerBuilder->centralLayers(gctx);
0084     // the positive Layer
0085     positiveLayers = m_cfg.layerBuilder->positiveLayers(gctx);
0086   }
0087   ACTS_DEBUG("-> Building layers complete");
0088 
0089   // Build the confined volumes
0090   MutableTrackingVolumeVector centralVolumes;
0091   if (m_cfg.ctVolumeBuilder) {
0092     centralVolumes = m_cfg.ctVolumeBuilder->centralVolumes();
0093   }
0094 
0095   // (0) PREP WORK ------------------------------------------------
0096   //
0097   // a) volume config of the existing volume
0098   if (existingVolume) {
0099     // volume and existing volume
0100     auto existingBounds = dynamic_cast<const CylinderVolumeBounds*>(
0101         &existingVolume->volumeBounds());
0102     // set the inside values
0103     wConfig.existingVolumeConfig.present = true;
0104     wConfig.existingVolumeConfig.rMin =
0105         existingBounds->get(CylinderVolumeBounds::eMinR);
0106     wConfig.existingVolumeConfig.rMax =
0107         existingBounds->get(CylinderVolumeBounds::eMaxR);
0108     wConfig.existingVolumeConfig.zMin =
0109         existingVolume->center().z() -
0110         existingBounds->get(CylinderVolumeBounds::eHalfLengthZ);
0111     wConfig.existingVolumeConfig.zMax =
0112         existingVolume->center().z() +
0113         existingBounds->get(CylinderVolumeBounds::eHalfLengthZ);
0114   }
0115   //
0116   // b) outside config
0117   // the volume config for the Outside
0118   VolumeConfig externalBoundConfig;
0119   if (externalBounds) {
0120     const CylinderVolumeBounds* ocvBounds =
0121         dynamic_cast<const CylinderVolumeBounds*>(externalBounds.get());
0122     // the cast to CylinderVolumeBounds needs to be successful
0123     if (ocvBounds != nullptr) {
0124       // get values from the out bounds
0125       wConfig.externalVolumeConfig.present = true;
0126       wConfig.externalVolumeConfig.rMin =
0127           ocvBounds->get(CylinderVolumeBounds::eMinR);
0128       wConfig.externalVolumeConfig.rMax =
0129           ocvBounds->get(CylinderVolumeBounds::eMaxR);
0130       wConfig.externalVolumeConfig.zMin =
0131           -ocvBounds->get(CylinderVolumeBounds::eHalfLengthZ);
0132       wConfig.externalVolumeConfig.zMax =
0133           ocvBounds->get(CylinderVolumeBounds::eHalfLengthZ);
0134     }
0135   }
0136 
0137   // ---------------------------------------------
0138   // The Volume Config of the SubVolumes
0139   // ---------------------------------------------
0140   // sub volume / layer configuration (subVolumes only build of layers are
0141   // present)
0142   // --------------------------------------------------------------------------
0143   //
0144   // possible configurations are (so far only synchronised):
0145   //
0146   // | Negative Endcap | Barrel | Positive Endcap | -  all layers present
0147   //                   | Barrel |                   -  barrel present
0148   // | Negative Endcap |        | Positive Endcap | - only endcaps present
0149   //                                                -  no layer present
0150   // Check if already given through configuration
0151   //
0152   // (A) volume configuration
0153   //
0154 
0155   // Find out with Layer analysis
0156   // analyze the layers
0157   wConfig.nVolumeConfig = analyzeContent(gctx, negativeLayers, {});  // TODO
0158   wConfig.cVolumeConfig = analyzeContent(gctx, centralLayers, centralVolumes);
0159   wConfig.pVolumeConfig = analyzeContent(gctx, positiveLayers, {});  // TODO
0160 
0161   bool hasLayers = wConfig.nVolumeConfig.present ||
0162                    wConfig.cVolumeConfig.present ||
0163                    wConfig.pVolumeConfig.present;
0164 
0165   if (!hasLayers) {
0166     ACTS_INFO("No layers present, returning nullptr");
0167     return nullptr;
0168   }
0169 
0170   std::string layerConfiguration = "|";
0171   if (wConfig.nVolumeConfig.present) {
0172     // negative layers are present
0173     ACTS_VERBOSE("Negative layers are present: rmin, rmax | zmin, zmax = "
0174                  << wConfig.nVolumeConfig.toString());
0175     std::vector<std::string> centers;
0176     std::transform(negativeLayers.begin(), negativeLayers.end(),
0177                    std::back_inserter(centers), [&](const auto& layer) {
0178                      return std::to_string(
0179                          layer->surfaceRepresentation().center(gctx)[eZ]);
0180                    });
0181     ACTS_VERBOSE("-> z locations: " << boost::algorithm::join(centers, ", "));
0182     // add to the string output
0183     layerConfiguration += " Negative Endcap |";
0184   }
0185   if (wConfig.cVolumeConfig.present) {
0186     // central layers are present
0187     ACTS_VERBOSE("Central layers are present:  rmin, rmax | zmin, zmax = "
0188                  << wConfig.cVolumeConfig.toString());
0189     std::vector<std::string> centers;
0190     std::transform(centralLayers.begin(), centralLayers.end(),
0191                    std::back_inserter(centers), [&](const auto& layer) {
0192                      return std::to_string(VectorHelpers::perp(
0193                          layer->surfaceRepresentation().center(gctx)));
0194                    });
0195     ACTS_VERBOSE("-> radii: " << boost::algorithm::join(centers, ", "));
0196     // add to the string output
0197     layerConfiguration += " Barrel |";
0198   }
0199   if (wConfig.pVolumeConfig.present) {
0200     // positive layers are present
0201     ACTS_VERBOSE("Positive layers are present: rmin, rmax | zmin, zmax = "
0202                  << wConfig.pVolumeConfig.toString());
0203     std::vector<std::string> centers;
0204     std::transform(positiveLayers.begin(), positiveLayers.end(),
0205                    std::back_inserter(centers), [&](const auto& layer) {
0206                      return std::to_string(
0207                          layer->surfaceRepresentation().center(gctx)[eZ]);
0208                    });
0209     ACTS_VERBOSE("-> z locations: " << boost::algorithm::join(centers, ", "));
0210     // add to the string output
0211     layerConfiguration += " Positive Endcap |";
0212   }
0213   // screen output
0214   ACTS_DEBUG("Layer configuration is : " << layerConfiguration);
0215 
0216   // (B) LAYER Config SYNCHRONISATION ----------------------------------
0217   // synchronise the layer config
0218   ACTS_VERBOSE("Configurations after layer parsing " << '\n'
0219                                                      << wConfig.toString());
0220   // first let us arrange the new container volume
0221   wConfig.configureContainerVolume();
0222   ACTS_VERBOSE("Configuration after container synchronisation "
0223                << '\n'
0224                << wConfig.toString());
0225   // now let's understand the wrapping if needed
0226   if (wConfig.existingVolumeConfig.present) {
0227     wConfig.wrapInsertAttach();
0228     ACTS_VERBOSE("Configuration after wrapping, insertion, attachment "
0229                  << '\n'
0230                  << wConfig.toString());
0231   } else {
0232     // no wrapping around inner volume needed
0233     // however there could be central, positive & negative volume which will
0234     // need to be put into a container volume
0235     wConfig.wCondition = NoWrapping;
0236   }
0237 
0238   // (C) VOLUME CREATION ----------------------------------
0239   auto tvHelper = m_cfg.trackingVolumeHelper;
0240   // the barrel is always created
0241   auto barrel =
0242       wConfig.cVolumeConfig.present
0243           ? tvHelper->createTrackingVolume(
0244                 gctx, wConfig.cVolumeConfig.layers,
0245                 wConfig.cVolumeConfig.volumes, m_cfg.volumeMaterial,
0246                 wConfig.cVolumeConfig.rMin, wConfig.cVolumeConfig.rMax,
0247                 wConfig.cVolumeConfig.zMin, wConfig.cVolumeConfig.zMax,
0248                 m_cfg.volumeName + "::Barrel")
0249           : nullptr;
0250 
0251   // Helper method to check for
0252 
0253   // Helper method to create endcap volume
0254   auto createEndcap =
0255       [&](VolumeConfig& centralConfig, VolumeConfig& endcapConfig,
0256           const std::string& endcapName) -> MutableTrackingVolumePtr {
0257     // No config - no volume
0258     if (!endcapConfig.present) {
0259       return nullptr;
0260     }
0261     // Check for ring layout
0262     if (m_cfg.checkRingLayout) {
0263       ACTS_DEBUG("Configured to check for ring layout - parsing layers.");
0264       // Parsing loop for ring layout
0265       std::vector<double> innerRadii = {};
0266       std::vector<double> outerRadii = {};
0267       for (const auto& elay : endcapConfig.layers) {
0268         auto discBounds = dynamic_cast<const RadialBounds*>(
0269             &(elay->surfaceRepresentation().bounds()));
0270         if (discBounds != nullptr) {
0271           double tolerance = m_cfg.ringTolerance;
0272           // Search for the rmin value  - and insert if necessary
0273           double rMin = discBounds->rMin();
0274           auto innerSearch = std::ranges::find_if(innerRadii, [&](double r) {
0275             return std::abs(rMin - r) < tolerance;
0276           });
0277           if (innerSearch == innerRadii.end()) {
0278             innerRadii.push_back(rMin);
0279           }
0280           // Search for the rmax value - and insert if necessary
0281           double rMax = discBounds->rMax();
0282           auto outerSearch = std::ranges::find_if(outerRadii, [&](double r) {
0283             return std::abs(rMax - r) < tolerance;
0284           });
0285           if (outerSearch == outerRadii.end()) {
0286             outerRadii.push_back(rMax);
0287           }
0288         }
0289       }
0290 
0291       // we check radii for consistency from the inside outwards, so need to
0292       // sort
0293       std::ranges::sort(innerRadii);
0294       std::ranges::sort(outerRadii);
0295 
0296       ACTS_DEBUG("Inner radii:" << [&]() {
0297         std::stringstream ss;
0298         for (double f : innerRadii) {
0299           ss << " " << f;
0300         }
0301         return ss.str();
0302       }());
0303 
0304       ACTS_DEBUG("Outer radii:" << [&]() {
0305         std::stringstream ss;
0306         for (double f : outerRadii) {
0307           ss << " " << f;
0308         }
0309         return ss.str();
0310       }());
0311       // Result of the parsing loop
0312       if (innerRadii.size() == outerRadii.size() && !innerRadii.empty()) {
0313         bool consistent = true;
0314         // The inter volume radii
0315         ACTS_VERBOSE("Checking ring radius consistency");
0316         std::vector<double> interRadii = {};
0317         for (std::size_t ir = 1; ir < innerRadii.size(); ++ir) {
0318           // Check whether inner/outer radii are consistent
0319           ACTS_VERBOSE(
0320               "or #" << ir - 1 << " < ir #" << ir << ": " << outerRadii[ir - 1]
0321                      << " < " << innerRadii[ir] << ", ok: "
0322                      << (outerRadii[ir - 1] < innerRadii[ir] ? "yes" : "no"));
0323           if (outerRadii[ir - 1] < innerRadii[ir]) {
0324             interRadii.push_back(0.5 * (outerRadii[ir - 1] + innerRadii[ir]));
0325           } else {
0326             consistent = false;
0327             break;
0328           }
0329         }
0330         // Continue if the ring layout is consistent
0331         if (consistent) {
0332           ACTS_DEBUG("Ring layout detection: " << innerRadii.size()
0333                                                << " volumes.");
0334           // Separate the Layers into volumes
0335           std::vector<std::pair<double, double>> volumeRminRmax = {};
0336           for (unsigned int ii = 0; ii < interRadii.size(); ++ii) {
0337             if (ii == 0) {
0338               volumeRminRmax.push_back({endcapConfig.rMin, interRadii[ii]});
0339             }
0340             if (ii + 1 < interRadii.size()) {
0341               volumeRminRmax.push_back({interRadii[ii], interRadii[ii + 1]});
0342             } else {
0343               volumeRminRmax.push_back({interRadii[ii], endcapConfig.rMax});
0344             }
0345           }
0346           auto ringLayers =
0347               std::vector<LayerVector>(innerRadii.size(), LayerVector());
0348           // Filling loop
0349           for (const auto& elay : endcapConfig.layers) {
0350             // Getting the reference radius
0351             double test = elay->surfaceRepresentation().referencePositionValue(
0352                 gctx, AxisDirection::AxisR);
0353             // Find the right bin
0354             auto ringVolume =
0355                 std::ranges::find_if(volumeRminRmax, [&](const auto& vrr) {
0356                   return (test > vrr.first && test < vrr.second);
0357                 });
0358             if (ringVolume != volumeRminRmax.end()) {
0359               unsigned int ringBin =
0360                   std::distance(volumeRminRmax.begin(), ringVolume);
0361               ringLayers[ringBin].push_back(elay);
0362             }
0363           }
0364           // Subvolume construction
0365           ACTS_DEBUG("Ring layout configuration: ");
0366           // Endcap container
0367           std::vector<TrackingVolumePtr> endcapContainer;
0368           unsigned int ir = 0;
0369           for (auto& rLayers : ringLayers) {
0370             ACTS_DEBUG(" - ring volume " << ir << " with " << rLayers.size()
0371                                          << " layers, and rmin/rmax = "
0372                                          << volumeRminRmax[ir].first << "/"
0373                                          << volumeRminRmax[ir].second);
0374             endcapContainer.push_back(tvHelper->createTrackingVolume(
0375                 gctx, rLayers, centralConfig.volumes, m_cfg.volumeMaterial,
0376                 volumeRminRmax[ir].first, volumeRminRmax[ir].second,
0377                 endcapConfig.zMin, endcapConfig.zMax,
0378                 m_cfg.volumeName + endcapName + std::string("::Ring") +
0379                     std::to_string(ir)));
0380             ++ir;
0381           }
0382           // Return a container of ring volumes
0383           return tvHelper->createContainerTrackingVolume(gctx, endcapContainer);
0384         } else {
0385           ACTS_DEBUG("Ring radii found to be inconsistent");
0386         }
0387       } else {
0388         ACTS_DEBUG("Have " << innerRadii.size() << " inner radii and "
0389                            << outerRadii.size() << " outer radii");
0390       }
0391     }
0392 
0393     // No ring layout - return single volume
0394     return tvHelper->createTrackingVolume(
0395         gctx, endcapConfig.layers, centralConfig.volumes, m_cfg.volumeMaterial,
0396         endcapConfig.rMin, endcapConfig.rMax, endcapConfig.zMin,
0397         endcapConfig.zMax, m_cfg.volumeName + endcapName);
0398   };
0399 
0400   // The negative endcap is created if present
0401   auto nEndcap = createEndcap(wConfig.cVolumeConfig, wConfig.nVolumeConfig,
0402                               "::NegativeEndcap");
0403 
0404   // The positive endcap is created if present
0405   auto pEndcap = createEndcap(wConfig.cVolumeConfig, wConfig.pVolumeConfig,
0406                               "::PositiveEndcap");
0407 
0408   ACTS_DEBUG("Newly created volume(s) will be " << wConfig.wConditionScreen);
0409   // Standalone container, full wrapping, full insertion & if no existing volume
0410   // is present needs a bare triple
0411   if (wConfig.wCondition == Wrapping || wConfig.wCondition == Inserting ||
0412       wConfig.wCondition == NoWrapping) {
0413     ACTS_VERBOSE("Combined new container is being built.");
0414     // Stuff into the container what you have
0415     std::vector<TrackingVolumePtr> volumesContainer;
0416     if (nEndcap) {
0417       volumesContainer.push_back(nEndcap);
0418       volume = nEndcap;
0419       // Set the inner or outer material
0420       if (!m_cfg.buildToRadiusZero) {
0421         volume->assignBoundaryMaterial(m_cfg.boundaryMaterial[0],
0422                                        tubeInnerCover);
0423       }
0424       volume->assignBoundaryMaterial(m_cfg.boundaryMaterial[1], tubeOuterCover);
0425       volume->assignBoundaryMaterial(m_cfg.boundaryMaterial[2], negativeFaceXY);
0426       volume->assignBoundaryMaterial(m_cfg.boundaryMaterial[3], positiveFaceXY);
0427     }
0428     if (barrel) {
0429       // Assign boundary material if existing
0430       volumesContainer.push_back(barrel);
0431       volume = barrel;
0432       // Set the inner or outer material
0433       if (!m_cfg.buildToRadiusZero) {
0434         volume->assignBoundaryMaterial(m_cfg.boundaryMaterial[0],
0435                                        tubeInnerCover);
0436       }
0437       volume->assignBoundaryMaterial(m_cfg.boundaryMaterial[1], tubeOuterCover);
0438       volume->assignBoundaryMaterial(m_cfg.boundaryMaterial[3], negativeFaceXY);
0439       volume->assignBoundaryMaterial(m_cfg.boundaryMaterial[4], positiveFaceXY);
0440     }
0441     if (pEndcap) {
0442       volumesContainer.push_back(pEndcap);
0443       volume = pEndcap;
0444       // Set the inner or outer material
0445       if (!m_cfg.buildToRadiusZero) {
0446         volume->assignBoundaryMaterial(m_cfg.boundaryMaterial[0],
0447                                        tubeInnerCover);
0448       }
0449       volume->assignBoundaryMaterial(m_cfg.boundaryMaterial[1], tubeOuterCover);
0450       volume->assignBoundaryMaterial(m_cfg.boundaryMaterial[4], negativeFaceXY);
0451       volume->assignBoundaryMaterial(m_cfg.boundaryMaterial[5], positiveFaceXY);
0452     }
0453     // and low lets create the new volume
0454     volume =
0455         volumesContainer.size() > 1
0456             ? tvHelper->createContainerTrackingVolume(gctx, volumesContainer)
0457             : volume;
0458   } else if (wConfig.wCondition != Attaching) {
0459     // the new volume is the only one present
0460     volume = nEndcap ? nEndcap : (barrel ? barrel : pEndcap);
0461   }
0462 
0463   // Prepare the gap volumes first
0464   TrackingVolumePtr existingVolumeCp = existingVolume;
0465   // Check if further action is needed on existing volumes and gap volumes
0466   if (existingVolumeCp) {
0467     // Check if gaps are needed
0468     std::vector<TrackingVolumePtr> existingContainer;
0469     if (wConfig.fGapVolumeConfig.present) {
0470       // create the gap volume
0471       auto fGap = tvHelper->createGapTrackingVolume(
0472           gctx, wConfig.cVolumeConfig.volumes, m_cfg.volumeMaterial,
0473           wConfig.fGapVolumeConfig.rMin, wConfig.fGapVolumeConfig.rMax,
0474           wConfig.fGapVolumeConfig.zMin, wConfig.fGapVolumeConfig.zMax, 1,
0475           false, m_cfg.volumeName + "::fGap");
0476       // push it back into the list
0477       existingContainer.push_back(fGap);
0478     }
0479     existingContainer.push_back(existingVolumeCp);
0480     if (wConfig.sGapVolumeConfig.present) {
0481       // create the gap volume
0482       auto sGap = tvHelper->createGapTrackingVolume(
0483           gctx, wConfig.cVolumeConfig.volumes, m_cfg.volumeMaterial,
0484           wConfig.sGapVolumeConfig.rMin, wConfig.sGapVolumeConfig.rMax,
0485           wConfig.sGapVolumeConfig.zMin, wConfig.sGapVolumeConfig.zMax, 1,
0486           false, m_cfg.volumeName + "::sGap");
0487       // push it back into the list
0488       existingContainer.push_back(sGap);
0489     }
0490 
0491     // And low lets create the new existing volume with gaps
0492     existingVolumeCp =
0493         existingContainer.size() > 1
0494             ? tvHelper->createContainerTrackingVolume(gctx, existingContainer)
0495             : existingVolumeCp;
0496 
0497     // for central wrapping or inserting, we need to update once more
0498     // clear the container
0499     existingContainer.clear();
0500     if (wConfig.wCondition == CentralWrapping) {
0501       existingContainer.push_back(existingVolumeCp);
0502       existingContainer.push_back(barrel);
0503     } else if (wConfig.wCondition == CentralInserting) {
0504       existingContainer.push_back(barrel);
0505       existingContainer.push_back(existingVolumeCp);
0506     }
0507     // update
0508     existingVolumeCp =
0509         !existingContainer.empty()
0510             ? tvHelper->createContainerTrackingVolume(gctx, existingContainer)
0511             : existingVolumeCp;
0512 
0513     std::vector<TrackingVolumePtr> totalContainer;
0514     // check what to do with the existing
0515     if (wConfig.wCondition == Attaching ||
0516         wConfig.wCondition == CentralWrapping ||
0517         wConfig.wCondition == CentralInserting) {
0518       if (nEndcap) {
0519         totalContainer.push_back(nEndcap);
0520       }
0521       totalContainer.push_back(existingVolumeCp);
0522       if (pEndcap) {
0523         totalContainer.push_back(pEndcap);
0524       }
0525     } else if (wConfig.wCondition == Inserting && volume) {
0526       totalContainer.push_back(volume);
0527       totalContainer.push_back(existingVolumeCp);
0528     } else if (wConfig.wCondition == Wrapping && volume) {
0529       totalContainer.push_back(existingVolumeCp);
0530       totalContainer.push_back(volume);
0531     } else {
0532       ACTS_ERROR("Misconfiguration in volume building detected.");
0533       return nullptr;
0534     }
0535     // now create the new container volume
0536     volume = tvHelper->createContainerTrackingVolume(gctx, totalContainer);
0537   }
0538 
0539   return volume;
0540 }
0541 
0542 // -----------------------------
0543 VolumeConfig CylinderVolumeBuilder::analyzeContent(
0544     const GeometryContext& gctx, const LayerVector& lVector,
0545     const MutableTrackingVolumeVector& mtvVector) const {
0546   // @TODO add envelope tolerance
0547   //
0548   // return object
0549   VolumeConfig lConfig;
0550   // only if the vector is present it can actually be analyzed
0551   if (!lVector.empty() || !mtvVector.empty()) {
0552     // we have layers
0553     lConfig.present = true;
0554     // loop over the layer
0555     for (auto& layer : lVector) {
0556       // the thickness of the layer needs to be taken into account
0557       double thickness = layer->thickness();
0558       // get the center of the layer
0559       const Vector3& center = layer->surfaceRepresentation().center(gctx);
0560       double rCenter = std::hypot(center.x(), center.y());
0561       // check if it is a cylinder layer
0562       const CylinderLayer* cLayer =
0563           dynamic_cast<const CylinderLayer*>(layer.get());
0564       if (cLayer != nullptr) {
0565         // now we have access to all the information
0566         double rMinC =
0567             cLayer->surfaceRepresentation().bounds().get(CylinderBounds::eR) -
0568             0.5 * thickness;
0569         double rMaxC =
0570             cLayer->surfaceRepresentation().bounds().get(CylinderBounds::eR) +
0571             0.5 * thickness;
0572 
0573         double hZ = cLayer->surfaceRepresentation().bounds().get(
0574             CylinderBounds::eHalfLengthZ);
0575         lConfig.rMin =
0576             std::min(lConfig.rMin, rMinC - m_cfg.layerEnvelopeR.first);
0577         lConfig.rMax = std::max(lConfig.rMax,
0578                                 rCenter + rMaxC + m_cfg.layerEnvelopeR.second);
0579         lConfig.zMin =
0580             std::min(lConfig.zMin, center.z() - hZ - m_cfg.layerEnvelopeZ);
0581         lConfig.zMax =
0582             std::max(lConfig.zMax, center.z() + hZ + m_cfg.layerEnvelopeZ);
0583       }
0584       // proceed further if it is a Disc layer
0585       const RadialBounds* dBounds = dynamic_cast<const RadialBounds*>(
0586           &(layer->surfaceRepresentation().bounds()));
0587       if (dBounds != nullptr) {
0588         // now we have access to all the information
0589         double rMinD = dBounds->rMin();
0590         double rMaxD = dBounds->rMax();
0591         double zMinD = center.z() - 0.5 * thickness;
0592         double zMaxD = center.z() + 0.5 * thickness;
0593         lConfig.rMin =
0594             std::min(lConfig.rMin, rMinD - m_cfg.layerEnvelopeR.first);
0595         lConfig.rMax = std::max(lConfig.rMax,
0596                                 rCenter + rMaxD + m_cfg.layerEnvelopeR.second);
0597         lConfig.rMin = std::max(0.0, lConfig.rMin);
0598         lConfig.zMin = std::min(lConfig.zMin, zMinD - m_cfg.layerEnvelopeZ);
0599         lConfig.zMax = std::max(lConfig.zMax, zMaxD + m_cfg.layerEnvelopeZ);
0600       }
0601     }
0602     for (auto& volume : mtvVector) {
0603       const CylinderVolumeBounds* cvBounds =
0604           dynamic_cast<const CylinderVolumeBounds*>(&volume->volumeBounds());
0605       if (cvBounds != nullptr) {
0606         lConfig.rMin =
0607             std::min(lConfig.rMin, cvBounds->get(CylinderVolumeBounds::eMinR));
0608         lConfig.rMax =
0609             std::max(lConfig.rMax, cvBounds->get(CylinderVolumeBounds::eMaxR));
0610         lConfig.zMin = std::min(
0611             lConfig.zMin, -cvBounds->get(CylinderVolumeBounds::eHalfLengthZ));
0612         lConfig.zMax = std::max(
0613             lConfig.zMax, cvBounds->get(CylinderVolumeBounds::eHalfLengthZ));
0614       }
0615     }
0616   }
0617 
0618   // Set the layers to the layer vector
0619   lConfig.layers = lVector;
0620   // set the layers to the layer vector
0621   lConfig.volumes = mtvVector;
0622   // overwrite to radius 0 if needed
0623   if (m_cfg.buildToRadiusZero) {
0624     ACTS_VERBOSE("This layer builder is configured to build to the beamline.");
0625     lConfig.rMin = 0.;
0626   }
0627 
0628   // and return what you have
0629   return lConfig;
0630 }
0631 
0632 }  // namespace Acts