File indexing completed on 2026-07-06 07:48:24
0001
0002
0003
0004
0005
0006
0007
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
0035
0036
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
0047
0048
0049
0050
0051
0052
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
0125
0126 using PortalTagDesignatorVariant =
0127 std::variant<std::monostate, CylinderPortalTagDesignator,
0128 CuboidPortalTagDesignator>;
0129
0130
0131
0132
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
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
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 }