Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-07-06 07:48:24

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 #pragma once
0010 
0011 #include "Acts/Geometry/CuboidPortalShell.hpp"
0012 #include "Acts/Geometry/CuboidVolumeBounds.hpp"
0013 #include "Acts/Geometry/CylinderPortalShell.hpp"
0014 #include "Acts/Geometry/CylinderVolumeBounds.hpp"
0015 #include "Acts/Geometry/Portal.hpp"
0016 #include "Acts/Geometry/PortalShell.hpp"
0017 #include "Acts/Utilities/Logger.hpp"
0018 
0019 #include <algorithm>
0020 #include <format>
0021 #include <ostream>
0022 #include <regex>
0023 #include <stdexcept>
0024 #include <string>
0025 #include <typeinfo>
0026 #include <utility>
0027 #include <variant>
0028 #include <vector>
0029 
0030 #include <boost/core/demangle.hpp>
0031 
0032 namespace Acts::Experimental::detail {
0033 
0034 /// Extract the shape name from a portal shell type name, e.g.
0035 /// "Acts::CuboidPortalShell" -> "Cuboid". Falls back to full demangled name if
0036 /// the "PortalShell" suffix is not found.
0037 inline std::string portalShellShapeName(const std::type_info& type) {
0038   static const std::regex kPortalShellRegex{R"((\w+)PortalShell$)"};
0039   std::string full = boost::core::demangle(type.name());
0040   if (std::smatch match; std::regex_search(full, match, kPortalShellRegex)) {
0041     return match[1].str();
0042   }
0043   return full;
0044 }
0045 
0046 /// Designator that assigns string tags to specific faces of a portal shell.
0047 ///
0048 /// Unlike the material designator, tagging happens in the *finalize* phase of
0049 /// the blueprint construction (after all portal merging/fusing), so the portal
0050 /// retrieved here is the final, shared portal that ends up in the geometry.
0051 /// @tparam face_enum_t The volume-bounds face enum (e.g. CylinderVolumeBounds::Face)
0052 /// @tparam shell_type_t The concrete portal shell base type (e.g. CylinderPortalShell)
0053 template <typename face_enum_t, typename shell_type_t>
0054 class PortalTagDesignator {
0055  public:
0056   using Face = face_enum_t;
0057   using ShellType = shell_type_t;
0058 
0059   PortalTagDesignator(Face face, std::string label, const std::string& prefix) {
0060     validateDuplicate(face, prefix);
0061     m_tags.emplace_back(face, std::move(label));
0062   }
0063 
0064   std::string label() const {
0065     return std::format("{}PortalTagDesignator",
0066                        portalShellShapeName(typeid(ShellType)));
0067   }
0068 
0069   void apply(PortalShellBase& shell, const Logger& logger,
0070              const std::string& prefix) const {
0071     auto* concreteShell = dynamic_cast<ShellType*>(&shell);
0072     if (concreteShell == nullptr) {
0073       ACTS_ERROR(prefix << "Concrete shell type mismatch: configured for "
0074                         << portalShellShapeName(typeid(ShellType))
0075                         << " but received "
0076                         << portalShellShapeName(typeid(shell)));
0077       throw std::invalid_argument(prefix + "Concrete shell type mismatch");
0078     }
0079 
0080     for (const auto& [face, tag] : m_tags) {
0081       auto* portal = concreteShell->portal(face).get();
0082       if (portal == nullptr) {
0083         ACTS_ERROR(prefix << "Portal is nullptr");
0084         throw std::runtime_error("Portal is nullptr");
0085       }
0086 
0087       ACTS_DEBUG(prefix << "Tagging face " << face << " with '" << tag << "'");
0088       portal->addTag(tag);
0089     }
0090   }
0091 
0092   void graphvizLabel(std::ostream& os) const {
0093     os << "<br/><i>" << portalShellShapeName(typeid(ShellType)) << " tags</i>";
0094     for (const auto& [face, tag] : m_tags) {
0095       os << "<br/> at: " << face << ": " << tag;
0096     }
0097   }
0098 
0099   PortalTagDesignator merged(const PortalTagDesignator& other) const {
0100     PortalTagDesignator result = *this;
0101     std::ranges::copy(other.m_tags, std::back_inserter(result.m_tags));
0102     return result;
0103   }
0104 
0105  private:
0106   void validateDuplicate(Face face, const std::string& prefix) {
0107     if (std::ranges::find_if(m_tags, [&](const auto& entry) {
0108           return entry.first == face;
0109         }) != m_tags.end()) {
0110       throw std::invalid_argument(prefix +
0111                                   portalShellShapeName(typeid(ShellType)) +
0112                                   " face already tagged");
0113     }
0114   }
0115 
0116   std::vector<std::pair<Face, std::string>> m_tags;
0117 };
0118 
0119 using CylinderPortalTagDesignator =
0120     PortalTagDesignator<CylinderVolumeBounds::Face, CylinderPortalShell>;
0121 using CuboidPortalTagDesignator =
0122     PortalTagDesignator<CuboidVolumeBounds::Face, CuboidPortalShell>;
0123 
0124 /// Type-erased portal tag designator. std::monostate represents the
0125 /// unconfigured (null) state.
0126 using PortalTagDesignatorVariant =
0127     std::variant<std::monostate, CylinderPortalTagDesignator,
0128                  CuboidPortalTagDesignator>;
0129 
0130 /// Merge two designators. std::monostate acts as the identity element.
0131 /// Throws if the two active designator types are incompatible (e.g. cylinder
0132 /// vs cuboid).
0133 inline PortalTagDesignatorVariant mergeTags(
0134     const PortalTagDesignatorVariant& a, const PortalTagDesignatorVariant& b) {
0135   return std::visit(
0136       []<typename X, typename Y>(const X& x,
0137                                  const Y& y) -> PortalTagDesignatorVariant {
0138         if constexpr (std::is_same_v<X, std::monostate>) {
0139           return y;
0140         } else if constexpr (std::is_same_v<Y, std::monostate>) {
0141           return x;
0142         } else if constexpr (std::is_same_v<X, Y>) {
0143           return x.merged(y);
0144         } else {
0145           throw std::runtime_error(std::format(
0146               "PortalDesignator: Mixing {} with {} is not supported. A portal "
0147               "designator node can only tag faces of a single volume shape.",
0148               x.label(), y.label()));
0149         }
0150       },
0151       a, b);
0152 }
0153 
0154 /// Apply a designator to a portal shell.
0155 inline void applyTags(const PortalTagDesignatorVariant& d,
0156                       PortalShellBase& shell, const Logger& logger,
0157                       const std::string& prefix) {
0158   std::visit(
0159       [&]<typename T>(const T& des) {
0160         if constexpr (std::is_same_v<T, std::monostate>) {
0161           ACTS_WARNING(prefix << "PortalDesignator was not configured with any "
0162                               << "face tags! Check your configuration.");
0163         } else {
0164           des.apply(shell, logger, prefix);
0165         }
0166       },
0167       d);
0168 }
0169 
0170 /// Write a Graphviz label for a designator to an output stream.
0171 inline void graphvizLabelTags(const PortalTagDesignatorVariant& d,
0172                               std::ostream& os) {
0173   std::visit(
0174       [&]<typename T>(const T& des) {
0175         if constexpr (std::is_same_v<T, std::monostate>) {
0176           os << "<br/><i>NullPortalDesignator</i>";
0177         } else {
0178           des.graphvizLabel(os);
0179         }
0180       },
0181       d);
0182 }
0183 
0184 }  // namespace Acts::Experimental::detail