File indexing completed on 2026-04-17 07:46:40
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/DiamondPortalShell.hpp"
0016 #include "Acts/Geometry/DiamondVolumeBounds.hpp"
0017 #include "Acts/Geometry/TrapezoidPortalShell.hpp"
0018 #include "Acts/Geometry/TrapezoidVolumeBounds.hpp"
0019 #include "Acts/Material/HomogeneousSurfaceMaterial.hpp"
0020 #include "Acts/Material/ProtoSurfaceMaterial.hpp"
0021
0022 #include <format>
0023 #include <regex>
0024 #include <string>
0025 #include <variant>
0026
0027 #include <boost/core/demangle.hpp>
0028
0029 namespace Acts::Experimental::detail {
0030
0031 const std::regex kPortalShellRegex{R"((\w+)PortalShell$)"};
0032
0033
0034
0035
0036 inline std::string portalShellShapeName(const std::type_info& type) {
0037 std::string full = boost::core::demangle(type.name());
0038
0039 if (std::smatch match; std::regex_search(full, match, kPortalShellRegex)) {
0040 return match[1].str();
0041 }
0042 return full;
0043 }
0044
0045 template <typename face_enum_t, typename shell_type_t>
0046 class ProtoDesignator {
0047 public:
0048 using Face = face_enum_t;
0049 using ShellType = shell_type_t;
0050
0051 ProtoDesignator(Face face, const DirectedProtoAxis& loc0,
0052 const DirectedProtoAxis& loc1, const std::string& prefix) {
0053 validateAxes(face, loc0, loc1, prefix);
0054 validateDuplicate(face, prefix);
0055 m_binning.emplace_back(face, loc0, loc1);
0056 }
0057
0058 std::string label() const {
0059 return std::format("{}ProtoDesignator",
0060 portalShellShapeName(typeid(ShellType)));
0061 }
0062
0063 void apply(PortalShellBase& shell, const Logger& logger,
0064 const std::string& prefix) const {
0065 auto* concreteShell = dynamic_cast<ShellType*>(&shell);
0066 if (concreteShell == nullptr) {
0067 throw std::invalid_argument(prefix +
0068 portalShellShapeName(typeid(ShellType)) +
0069 " faces must use a valid face");
0070 }
0071
0072 ACTS_DEBUG(prefix << "Binning is set to compatible type");
0073
0074 for (const auto& [face, loc0, loc1] : m_binning) {
0075 auto* portal = concreteShell->portal(face).get();
0076 if (portal == nullptr) {
0077 ACTS_ERROR(prefix << "Portal is nullptr");
0078 throw std::runtime_error("Portal is nullptr");
0079 }
0080
0081 ACTS_DEBUG(prefix << "Assigning material with binning: " << loc0 << ", "
0082 << loc1 << " to face " << face);
0083
0084 portal->surface().assignSurfaceMaterial(
0085 std::make_shared<ProtoGridSurfaceMaterial>(std::vector{loc0, loc1}));
0086 }
0087 }
0088
0089 void graphvizLabel(std::ostream& os) const {
0090 os << "<br/><i>" << portalShellShapeName(typeid(ShellType))
0091 << " Binning</i>";
0092 for (const auto& [face, loc0, loc1] : m_binning) {
0093 os << "<br/> at: " << face;
0094 os << ": " << loc0.getAxisDirection() << "=" << loc0.getAxis().getNBins();
0095 os << ", " << loc1.getAxisDirection() << "=" << loc1.getAxis().getNBins();
0096 }
0097 }
0098
0099 ProtoDesignator merged(const ProtoDesignator& other) const {
0100 ProtoDesignator result = *this;
0101 std::ranges::copy(other.m_binning, std::back_inserter(result.m_binning));
0102 return result;
0103 }
0104
0105 private:
0106 void validateAxes(Face face, const DirectedProtoAxis& loc0,
0107 const DirectedProtoAxis& loc1,
0108 const std::string& prefix) const {
0109 using enum AxisDirection;
0110
0111 if constexpr (std::is_same_v<ShellType, CylinderPortalShell>) {
0112 using enum CylinderVolumeBounds::Face;
0113 switch (face) {
0114 case NegativeDisc:
0115 case PositiveDisc:
0116 if (loc0.getAxisDirection() != AxisR ||
0117 loc1.getAxisDirection() != AxisPhi) {
0118 throw std::invalid_argument(prefix +
0119 "Disc faces must use (r, phi) binning");
0120 }
0121 break;
0122 case OuterCylinder:
0123 case InnerCylinder:
0124 if (loc0.getAxisDirection() != AxisRPhi ||
0125 loc1.getAxisDirection() != AxisZ) {
0126 throw std::invalid_argument(
0127 prefix + "Cylinder faces must use (rphi, z) binning");
0128 }
0129 break;
0130 case NegativePhiPlane:
0131 case PositivePhiPlane:
0132 throw std::invalid_argument(prefix +
0133 "Phi plane faces are not supported");
0134 default:
0135 throw std::invalid_argument(prefix + "Unknown face type");
0136 }
0137 } else if constexpr (std::is_same_v<ShellType, CuboidPortalShell>) {
0138 if (loc0.getAxisDirection() != AxisX ||
0139 loc1.getAxisDirection() != AxisY) {
0140 throw std::invalid_argument(prefix +
0141 "Cuboid faces must use (x, y) binning");
0142 }
0143 } else {
0144 static_assert(std::is_same_v<ShellType, void>, "Unknown shell type");
0145 }
0146 }
0147
0148 void validateDuplicate(Face face, const std::string& prefix) {
0149 if (std::ranges::find_if(m_binning, [&](const auto& bin) {
0150 return std::get<0>(bin) == face;
0151 }) != m_binning.end()) {
0152 throw std::invalid_argument(prefix +
0153 portalShellShapeName(typeid(ShellType)) +
0154 " face already configured");
0155 }
0156 }
0157
0158 std::vector<std::tuple<Face, DirectedProtoAxis, DirectedProtoAxis>> m_binning;
0159 };
0160
0161 using CylinderProtoDesignator =
0162 ProtoDesignator<CylinderVolumeBounds::Face, CylinderPortalShell>;
0163 using CuboidProtoDesignator =
0164 ProtoDesignator<CuboidVolumeBounds::Face, CuboidPortalShell>;
0165
0166 template <typename face_enum_t, typename shell_type_t>
0167 class ISurfaceMaterialDesignator {
0168 public:
0169 using FaceType = face_enum_t;
0170 using ShellType = shell_type_t;
0171
0172 ISurfaceMaterialDesignator(FaceType face,
0173 std::shared_ptr<const ISurfaceMaterial> material) {
0174 m_materials.emplace_back(face, std::move(material));
0175 }
0176
0177 std::string label() const {
0178 return std::format("{} ISurfaceMaterialDesignator",
0179 portalShellShapeName(typeid(ShellType)));
0180 }
0181
0182 void apply(PortalShellBase& shell, const Logger& logger,
0183 const std::string& prefix) const {
0184 auto* concreteShell = dynamic_cast<ShellType*>(&shell);
0185 if (concreteShell == nullptr) {
0186 ACTS_ERROR(prefix << "Concrete shell type mismatch: configured for "
0187 << portalShellShapeName(typeid(ShellType))
0188 << " but received "
0189 << portalShellShapeName(typeid(shell)));
0190
0191 throw std::invalid_argument(prefix + "Concrete shell type mismatch");
0192 }
0193
0194 for (const auto& [face, material] : m_materials) {
0195 auto* portal = concreteShell->portal(face).get();
0196 if (portal == nullptr) {
0197 ACTS_ERROR(prefix << "Portal is nullptr");
0198 throw std::runtime_error("Portal is nullptr");
0199 }
0200
0201 portal->surface().assignSurfaceMaterial(material);
0202 }
0203 }
0204
0205 void graphvizLabel(std::ostream& os) const {
0206 os << "<br/><i>Homog. " << portalShellShapeName(typeid(ShellType))
0207 << " material</i>";
0208 for (const auto& [face, material] : m_materials) {
0209 os << "<br/> at: " << face;
0210 }
0211 }
0212
0213 ISurfaceMaterialDesignator merged(
0214 const ISurfaceMaterialDesignator& other) const {
0215 ISurfaceMaterialDesignator result = *this;
0216 std::ranges::copy(other.m_materials,
0217 std::back_inserter(result.m_materials));
0218 return result;
0219 }
0220
0221 private:
0222 std::vector<std::pair<FaceType, std::shared_ptr<const ISurfaceMaterial>>>
0223 m_materials;
0224 };
0225
0226 using CylinderHomogeneousMaterialDesignator =
0227 ISurfaceMaterialDesignator<CylinderVolumeBounds::Face, CylinderPortalShell>;
0228
0229 using CuboidHomogeneousMaterialDesignator =
0230 ISurfaceMaterialDesignator<CuboidVolumeBounds::Face, CuboidPortalShell>;
0231
0232 using TrapezoidHomogeneousMaterialDesignator =
0233 ISurfaceMaterialDesignator<TrapezoidVolumeBounds::Face,
0234 TrapezoidPortalShell>;
0235
0236 using DiamondHomogeneousMaterialDesignator =
0237 ISurfaceMaterialDesignator<DiamondVolumeBounds::Face, DiamondPortalShell>;
0238
0239
0240
0241 using Designator =
0242 std::variant<std::monostate, CylinderProtoDesignator, CuboidProtoDesignator,
0243 CylinderHomogeneousMaterialDesignator,
0244 CuboidHomogeneousMaterialDesignator,
0245 TrapezoidHomogeneousMaterialDesignator,
0246 DiamondHomogeneousMaterialDesignator>;
0247
0248
0249
0250
0251 inline Designator merge(const Designator& a, const Designator& b) {
0252 return std::visit(
0253 []<typename X, typename Y>(const X& x, const Y& y) -> Designator {
0254 if constexpr (std::is_same_v<X, std::monostate>) {
0255 return y;
0256 } else if constexpr (std::is_same_v<Y, std::monostate>) {
0257 return x;
0258 } else if constexpr (std::is_same_v<X, Y>) {
0259 return x.merged(y);
0260 } else {
0261 throw std::runtime_error(
0262 std::format("MaterialDesignator: Merging of {} with {} is not "
0263 "supported. This means you are trying to configure "
0264 "the same face with different binning or materials. "
0265 "Please check your configuration.",
0266 x.label(), y.label()));
0267 }
0268 },
0269 a, b);
0270 }
0271
0272
0273 inline void apply(const Designator& d, PortalShellBase& shell,
0274 const Logger& logger, const std::string& prefix) {
0275 std::visit(
0276 [&]<typename T>(const T& des) {
0277 if constexpr (std::is_same_v<T, std::monostate>) {
0278 ACTS_WARNING(prefix << "MaterialDesignator was not configured with "
0279 << "any material designation! Check your "
0280 << "configuration.");
0281 } else {
0282 des.apply(shell, logger, prefix);
0283 }
0284 },
0285 d);
0286 }
0287
0288
0289 inline void graphvizLabel(const Designator& d, std::ostream& os) {
0290 std::visit(
0291 [&]<typename T>(const T& des) {
0292 if constexpr (std::is_same_v<T, std::monostate>) {
0293 os << "<br/><i>NullDesignator</i>";
0294 } else {
0295 des.graphvizLabel(os);
0296 }
0297 },
0298 d);
0299 }
0300
0301 }