Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-08-09 08:18:17

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/ContainerBlueprintNode.hpp"
0010 
0011 #include "Acts/Geometry/CuboidPortalShell.hpp"
0012 #include "Acts/Geometry/CuboidVolumeStack.hpp"
0013 #include "Acts/Geometry/CylinderPortalShell.hpp"
0014 #include "Acts/Geometry/CylinderVolumeStack.hpp"
0015 #include "Acts/Geometry/Portal.hpp"
0016 #include "Acts/Surfaces/RegularSurface.hpp"
0017 
0018 #include <sstream>
0019 #include <string>
0020 #include <vector>
0021 
0022 namespace Acts {
0023 
0024 ContainerBlueprintNode::ContainerBlueprintNode(
0025     const std::string& name, AxisDirection axis,
0026     VolumeAttachmentStrategy attachmentStrategy,
0027     VolumeResizeStrategy resizeStrategy)
0028     : ContainerBlueprintNode(name, axis, attachmentStrategy,
0029                              {resizeStrategy, resizeStrategy}) {}
0030 
0031 ContainerBlueprintNode::ContainerBlueprintNode(
0032     const std::string& name, AxisDirection axis,
0033     VolumeAttachmentStrategy attachmentStrategy,
0034     std::pair<VolumeResizeStrategy, VolumeResizeStrategy> resizeStrategies)
0035     : m_name(name),
0036       m_direction(axis),
0037       m_attachmentStrategy(attachmentStrategy),
0038       m_resizeStrategies(resizeStrategies) {}
0039 
0040 const std::string& ContainerBlueprintNode::name() const {
0041   return m_name;
0042 }
0043 
0044 Volume& ContainerBlueprintNode::build(const BlueprintOptions& options,
0045                                       const GeometryContext& gctx,
0046                                       const Logger& logger) {
0047   ACTS_DEBUG(prefix() << "container build (dir=" << m_direction << ")");
0048 
0049   if (m_stack != nullptr) {
0050     ACTS_ERROR(prefix() << "Volume is already built");
0051     throw std::runtime_error("Volume is already built");
0052   }
0053 
0054   for (auto& child : children()) {
0055     Volume& volume = child.build(options, gctx, logger);
0056     m_childVolumes.push_back(&volume);
0057     // We need to remember which volume we got from which child, so we can
0058     // assemble a crrect portal shell later
0059     m_volumeToNode[&volume] = &child;
0060   }
0061   ACTS_VERBOSE(prefix() << "-> Collected " << m_childVolumes.size()
0062                         << " child volumes");
0063   ACTS_VERBOSE(prefix() << "-> Building the stack");
0064   m_stack = makeStack(gctx, m_childVolumes, logger);
0065   ACTS_DEBUG(prefix() << "-> Stack bounds are: " << m_stack->volumeBounds());
0066 
0067   ACTS_DEBUG(prefix() << " *** build complete ***");
0068 
0069   return *m_stack;
0070 }
0071 
0072 void ContainerBlueprintNode::finalize(const BlueprintOptions& options,
0073                                       const GeometryContext& gctx,
0074                                       TrackingVolume& parent,
0075                                       const Logger& logger) {
0076   ACTS_DEBUG(prefix() << "Finalizing container");
0077 
0078   if (m_stack == nullptr) {
0079     ACTS_ERROR(prefix() << "Volume is not built");
0080     throw std::runtime_error("Volume is not built");
0081   }
0082 
0083   if (m_shell == nullptr) {
0084     ACTS_ERROR(prefix() << "Volume is not connected");
0085     throw std::runtime_error("Volume is not connected");
0086   }
0087 
0088   const auto* policyFactory = options.defaultNavigationPolicyFactory.get();
0089 
0090   ACTS_DEBUG(prefix() << "Registering " << m_gaps.size()
0091                       << " gap volumes with parent");
0092   for (auto& [shell, gap] : m_gaps) {
0093     auto* gapPtr = gap.get();
0094     parent.addVolume(std::move(gap));
0095     shell->applyToVolume();
0096     auto policy = policyFactory->build(gctx, *gapPtr, logger);
0097     gapPtr->setNavigationPolicy(std::move(policy));
0098   }
0099 
0100   ACTS_DEBUG(prefix() << "Finalizing " << children().size() << " children");
0101 
0102   for (auto& child : children()) {
0103     child.finalize(options, gctx, parent, logger);
0104   }
0105 }
0106 
0107 ContainerBlueprintNode& ContainerBlueprintNode::setDirection(
0108     AxisDirection direction) {
0109   if (m_stack != nullptr) {
0110     throw std::runtime_error("Cannot change direction after build");
0111   }
0112   m_direction = direction;
0113   return *this;
0114 }
0115 
0116 ContainerBlueprintNode& ContainerBlueprintNode::setAttachmentStrategy(
0117     VolumeAttachmentStrategy attachmentStrategy) {
0118   if (m_stack != nullptr) {
0119     throw std::runtime_error("Cannot change direction after build");
0120   }
0121   m_attachmentStrategy = attachmentStrategy;
0122   return *this;
0123 }
0124 
0125 ContainerBlueprintNode& ContainerBlueprintNode::setResizeStrategy(
0126     VolumeResizeStrategy resizeStrategy) {
0127   if (m_stack != nullptr) {
0128     throw std::runtime_error("Cannot change direction after build");
0129   }
0130   m_resizeStrategies = {resizeStrategy, resizeStrategy};
0131   return *this;
0132 }
0133 
0134 ContainerBlueprintNode& ContainerBlueprintNode::setResizeStrategies(
0135     VolumeResizeStrategy inner, VolumeResizeStrategy outer) {
0136   if (m_stack != nullptr) {
0137     throw std::runtime_error("Cannot change direction after build");
0138   }
0139   m_resizeStrategies = {inner, outer};
0140   return *this;
0141 }
0142 
0143 AxisDirection ContainerBlueprintNode::direction() const {
0144   return m_direction;
0145 }
0146 
0147 VolumeAttachmentStrategy ContainerBlueprintNode::attachmentStrategy() const {
0148   return m_attachmentStrategy;
0149 }
0150 
0151 std::pair<VolumeResizeStrategy, VolumeResizeStrategy>
0152 ContainerBlueprintNode::resizeStrategies() const {
0153   return m_resizeStrategies;
0154 }
0155 
0156 void ContainerBlueprintNode::addToGraphviz(std::ostream& os) const {
0157   std::stringstream ss;
0158   ss << "<b>" + name() + "</b>";
0159   ss << "<br/>" << typeName() << "Container";
0160   ss << "<br/>dir: " << m_direction;
0161   GraphViz::Node node{
0162       .id = name(), .label = ss.str(), .shape = GraphViz::Shape::DoubleOctagon};
0163   os << node << std::endl;
0164   for (const auto& child : children()) {
0165     os << indent() << GraphViz::Edge{{.id = name()}, {.id = child.name()}}
0166        << std::endl;
0167     child.addToGraphviz(os);
0168   }
0169 }
0170 
0171 template <typename BaseShell, typename SingleShell>
0172 std::vector<BaseShell*> ContainerBlueprintNode::collectChildShells(
0173     const BlueprintOptions& options, const GeometryContext& gctx,
0174     VolumeStack& stack, const std::string& prefix, const Logger& logger) {
0175   std::vector<BaseShell*> shells;
0176   ACTS_DEBUG(prefix << "Have " << m_childVolumes.size() << " child volumes");
0177   std::size_t nGaps = 0;
0178   for (Volume* volume : m_childVolumes) {
0179     if (stack.isGapVolume(*volume)) {
0180       // We need to create a TrackingVolume from the gap and put it in the
0181       // shell
0182       auto gap = std::make_unique<TrackingVolume>(*volume);
0183       gap->setVolumeName(name() + "::Gap" + std::to_string(nGaps + 1));
0184       nGaps++;
0185       ACTS_DEBUG(prefix << " ~> Gap volume (" << gap->volumeName()
0186                         << "): " << gap->volumeBounds());
0187       auto shell = std::make_unique<SingleShell>(gctx, *gap);
0188       assert(shell->isValid());
0189       shells.push_back(shell.get());
0190 
0191       m_gaps.emplace_back(std::move(shell), std::move(gap));
0192 
0193     } else {
0194       // Figure out which child we got this volume from
0195       auto it = m_volumeToNode.find(volume);
0196       if (it == m_volumeToNode.end()) {
0197         throw std::runtime_error("Volume not found in child volumes");
0198       }
0199 
0200       BlueprintNode& child = *it->second;
0201 
0202       ACTS_DEBUG(prefix << " ~> Child (" << child.name()
0203                         << ") volume: " << volume->volumeBounds());
0204 
0205       auto* shell =
0206           dynamic_cast<BaseShell*>(&child.connect(options, gctx, logger));
0207       if (shell == nullptr) {
0208         ACTS_ERROR(prefix << "Child volume stack type mismatch");
0209         throw std::runtime_error("Child volume stack type mismatch");
0210       }
0211       assert(shell->isValid());
0212 
0213       shells.push_back(shell);
0214     }
0215   }
0216   return shells;
0217 }
0218 
0219 template <typename BaseShell, typename SingleShell, typename ShellStack>
0220 PortalShellBase& ContainerBlueprintNode::connectImpl(
0221     const BlueprintOptions& options, const GeometryContext& gctx,
0222     VolumeStack* stack, const std::string& prefix, const Logger& logger) {
0223   ACTS_DEBUG(prefix << "Container connect");
0224   if (stack == nullptr) {
0225     ACTS_ERROR(prefix << "Volume is not built");
0226     throw std::runtime_error("Volume is not built");
0227   }
0228   ACTS_DEBUG(prefix << "Collecting child shells from " << children().size()
0229                     << " children");
0230 
0231   // We have child volumes and gaps as bare Volumes in `m_childVolumes` after
0232   // `build()` has completed. For the stack shell, we need TrackingVolumes in
0233   // the right order.
0234 
0235   std::vector<BaseShell*> shells = collectChildShells<BaseShell, SingleShell>(
0236       options, gctx, *stack, prefix, logger);
0237 
0238   // Sanity checks
0239   throw_assert(shells.size() == m_childVolumes.size(),
0240                "Number of shells does not match number of child volumes");
0241 
0242   throw_assert(std::ranges::none_of(
0243                    shells, [](const auto* shell) { return shell == nullptr; }),
0244                "Invalid shell pointer");
0245 
0246   throw_assert(std::ranges::all_of(
0247                    shells, [](const auto* shell) { return shell->isValid(); }),
0248                "Invalid shell");
0249 
0250   // Detect (with a node-scoped message) if material has been designated on a
0251   // portal face that will be *merged* during stacking. Such material cannot
0252   // survive the merge and would otherwise trigger a deep, hard-to-trace failure
0253   // inside the stack shell construction. Faces that are *fused* (e.g. the
0254   // boundary between two stacked volumes) legitimately carry material and are
0255   // not flagged here.  With only a single child there is no actual merge, so
0256   // the check is skipped entirely to avoid false positives.
0257   const PortalMaterialMergePolicy materialPolicy =
0258       options.keepGoingOnMaterialMergeFailure
0259           ? PortalMaterialMergePolicy::eDiscardAndMark
0260           : PortalMaterialMergePolicy::eThrow;
0261 
0262   // With a single child there is nothing to merge, so the "merged" faces are
0263   // actually kept as-is. Skip the clash check to avoid false positives.
0264   std::vector<std::string> materialClashes;
0265   for (auto face : shells.size() > 1
0266                        ? ShellStack::mergedFaces(direction())
0267                        : std::vector<typename ShellStack::Face>{}) {
0268     for (auto* shell : shells) {
0269       auto portal = shell->portal(face);
0270       if (portal != nullptr && portal->surface().hasMaterial()) {
0271         std::stringstream ss;
0272         ss << shell->label() << " carries material on face " << face;
0273         materialClashes.push_back(ss.str());
0274       }
0275     }
0276   }
0277   if (!materialClashes.empty()) {
0278     std::stringstream ss;
0279     ss << prefix << "Material is designated on portal faces that are merged "
0280        << "when stacking child volumes in " << direction() << " direction.";
0281     for (const auto& clash : materialClashes) {
0282       ss << "\n  - " << clash;
0283     }
0284     if (materialPolicy == PortalMaterialMergePolicy::eThrow) {
0285       ss << "\nMove the material designation to a face that is not merged "
0286             "(e.g. "
0287             "the enclosing container's face).";
0288       ACTS_ERROR(ss.str());
0289       throw PortalMergingException{ss.str()};
0290     }
0291     ss << "\nContinuing anyway: this material will be discarded and the merged "
0292           "surface tagged with a MergedMaterialMarker.";
0293     ACTS_WARNING(ss.str());
0294   }
0295 
0296   ACTS_DEBUG(prefix << "Producing merged stack shell in " << direction()
0297                     << " direction from " << shells.size() << " shells");
0298   m_shell = std::make_unique<ShellStack>(gctx, std::move(shells), direction(),
0299                                          logger, materialPolicy);
0300 
0301   assert(m_shell != nullptr && "No shell was built at the end of connect");
0302   assert(m_shell->isValid() && "Shell is not valid at the end of connect");
0303   return *m_shell;
0304 }
0305 
0306 PortalShellBase& CylinderContainerBlueprintNode::connect(
0307     const BlueprintOptions& options, const GeometryContext& gctx,
0308     const Logger& logger) {
0309   return connectImpl<CylinderPortalShell, SingleCylinderPortalShell,
0310                      CylinderStackPortalShell>(options, gctx, m_stack.get(),
0311                                                prefix(), logger);
0312 }
0313 
0314 const std::string& CylinderContainerBlueprintNode::typeName() const {
0315   return s_typeName;
0316 }
0317 
0318 std::unique_ptr<VolumeStack> CylinderContainerBlueprintNode::makeStack(
0319     const GeometryContext& gctx, std::vector<Volume*>& volumes,
0320     const Logger& logger) {
0321   return std::make_unique<CylinderVolumeStack>(gctx, volumes, m_direction,
0322                                                m_attachmentStrategy,
0323                                                m_resizeStrategies, logger);
0324 }
0325 
0326 PortalShellBase& CuboidContainerBlueprintNode::connect(
0327     const BlueprintOptions& options, const GeometryContext& gctx,
0328     const Logger& logger) {
0329   return connectImpl<CuboidPortalShell, SingleCuboidPortalShell,
0330                      CuboidStackPortalShell>(options, gctx, m_stack.get(),
0331                                              prefix(), logger);
0332 }
0333 
0334 const std::string& CuboidContainerBlueprintNode::typeName() const {
0335   return s_typeName;
0336 }
0337 
0338 std::unique_ptr<VolumeStack> CuboidContainerBlueprintNode::makeStack(
0339     const GeometryContext& gctx, std::vector<Volume*>& volumes,
0340     const Logger& logger) {
0341   return std::make_unique<CuboidVolumeStack>(gctx, volumes, m_direction,
0342                                              m_attachmentStrategy,
0343                                              m_resizeStrategies.first, logger);
0344 }
0345 
0346 }  // namespace Acts