File indexing completed on 2025-12-16 09:23:04
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/Material/HomogeneousSurfaceMaterial.hpp"
0016 #include "Acts/Material/ProtoSurfaceMaterial.hpp"
0017
0018 #include <boost/core/demangle.hpp>
0019
0020 namespace Acts::Experimental::detail {
0021
0022 class CylinderProtoDesignator;
0023 class CuboidProtoDesignator;
0024 class NullDesignator;
0025 template <typename face_enum_t, typename shell_type_t>
0026 class ISurfaceMaterialDesignator;
0027
0028 using CylinderHomogeneousMaterialDesignator =
0029 ISurfaceMaterialDesignator<CylinderVolumeBounds::Face, CylinderPortalShell>;
0030
0031 using CuboidHomogeneousMaterialDesignator =
0032 ISurfaceMaterialDesignator<CuboidVolumeBounds::Face, CuboidPortalShell>;
0033
0034 class DesignatorBase {
0035 public:
0036 virtual ~DesignatorBase() = default;
0037
0038 virtual void apply(PortalShellBase& shell, const Logger& logger,
0039 const std::string& prefix) = 0;
0040
0041 virtual std::string label() const = 0;
0042
0043 using FaceVariant =
0044 std::variant<CylinderVolumeBounds::Face, CuboidVolumeBounds::Face>;
0045
0046 virtual std::unique_ptr<DesignatorBase> merged(
0047 const DesignatorBase& other) const = 0;
0048
0049 virtual std::unique_ptr<DesignatorBase> merged(
0050 const CylinderProtoDesignator& other) const;
0051
0052 virtual std::unique_ptr<DesignatorBase> merged(
0053 const CuboidProtoDesignator& other) const;
0054
0055 virtual std::unique_ptr<DesignatorBase> merged(
0056 const NullDesignator& other) const;
0057
0058 virtual std::unique_ptr<DesignatorBase> merged(
0059 const CylinderHomogeneousMaterialDesignator& other) const;
0060
0061 virtual std::unique_ptr<DesignatorBase> merged(
0062 const CuboidHomogeneousMaterialDesignator& other) const;
0063
0064 virtual void graphvizLabel(std::ostream& os) const = 0;
0065 };
0066
0067 class CylinderProtoDesignator : public DesignatorBase {
0068 public:
0069 using Face = CylinderVolumeBounds::Face;
0070
0071 CylinderProtoDesignator(Face face, const DirectedProtoAxis& loc0,
0072 const DirectedProtoAxis& loc1,
0073 const std::string& prefix) {
0074 validate(face, loc0, loc1, prefix);
0075
0076 m_binning.emplace_back(face, loc0, loc1);
0077 }
0078
0079 std::string label() const override { return "CylinderProtoDesignator"; }
0080
0081 void apply(PortalShellBase& shell, const Logger& logger,
0082 const std::string& prefix) override {
0083 auto* cylShell = dynamic_cast<CylinderPortalShell*>(&shell);
0084 if (cylShell == nullptr) {
0085 throw std::invalid_argument(prefix +
0086 "Cylinder faces must use a valid face");
0087 }
0088
0089 ACTS_DEBUG(prefix << "Binning is set to compatible type");
0090 using enum CylinderVolumeBounds::Face;
0091
0092 for (const auto& [face, loc0, loc1] : m_binning) {
0093 auto* portal = cylShell->portal(face);
0094 if (portal == nullptr) {
0095 ACTS_ERROR(prefix << "Portal is nullptr");
0096 throw std::runtime_error("Portal is nullptr");
0097 }
0098
0099 ACTS_DEBUG(prefix << "Assigning material with binning: " << loc0 << ", "
0100 << loc1 << " to face " << face);
0101
0102 portal->surface().assignSurfaceMaterial(
0103 std::make_shared<ProtoGridSurfaceMaterial>(std::vector{loc0, loc1}));
0104 }
0105 }
0106
0107 void graphvizLabel(std::ostream& os) const override {
0108 os << "<br/><i>Cylinder Binning</i>";
0109 for (const auto& [face, loc0, loc1] : m_binning) {
0110 os << "<br/> at: " << face;
0111 os << ": " << loc0.getAxisDirection() << "=" << loc0.getAxis().getNBins();
0112 os << ", " << loc1.getAxisDirection() << "=" << loc1.getAxis().getNBins();
0113 }
0114 }
0115
0116 std::unique_ptr<DesignatorBase> merged(
0117 const DesignatorBase& other) const override {
0118 return other.merged(*this);
0119 }
0120
0121 std::unique_ptr<DesignatorBase> merged(
0122 const CylinderProtoDesignator& other) const override {
0123 auto merged = std::make_unique<CylinderProtoDesignator>(*this);
0124 std::ranges::copy(other.m_binning, std::back_inserter(merged->m_binning));
0125 return merged;
0126 }
0127
0128 std::unique_ptr<DesignatorBase> merged(
0129 const NullDesignator& ) const override {
0130 return std::make_unique<CylinderProtoDesignator>(*this);
0131 }
0132
0133 private:
0134 void validate(Face face, const DirectedProtoAxis& loc0,
0135 const DirectedProtoAxis& loc1, const std::string& prefix) {
0136 using enum CylinderVolumeBounds::Face;
0137 using enum AxisDirection;
0138
0139
0140 switch (face) {
0141 case NegativeDisc:
0142 case PositiveDisc:
0143 if (loc0.getAxisDirection() != AxisR ||
0144 loc1.getAxisDirection() != AxisPhi) {
0145 throw std::invalid_argument(prefix +
0146 "Disc faces must use (r, phi) binning");
0147 }
0148 break;
0149 case OuterCylinder:
0150 case InnerCylinder:
0151 if (loc0.getAxisDirection() != AxisRPhi ||
0152 loc1.getAxisDirection() != AxisZ) {
0153 throw std::invalid_argument(
0154 prefix + "Cylinder faces must use (rphi, z) binning");
0155 }
0156 break;
0157 case NegativePhiPlane:
0158 case PositivePhiPlane:
0159 throw std::invalid_argument(prefix +
0160 "Phi plane faces are not supported");
0161 break;
0162 }
0163
0164 if (std::ranges::find_if(m_binning, [&](const auto& bin) {
0165 return std::get<0>(bin) == face;
0166 }) != m_binning.end()) {
0167 throw std::invalid_argument(prefix + "Cylinder face already configured");
0168 }
0169 }
0170
0171 std::vector<std::tuple<Face, DirectedProtoAxis, DirectedProtoAxis>> m_binning;
0172 };
0173
0174 class CuboidProtoDesignator : public DesignatorBase {
0175 public:
0176 using Face = CuboidVolumeBounds::Face;
0177
0178 CuboidProtoDesignator(Face face, const DirectedProtoAxis& loc0,
0179 const DirectedProtoAxis& loc1,
0180 const std::string& prefix) {
0181 validate(face, loc0, loc1, prefix);
0182
0183 m_binning.emplace_back(face, loc0, loc1);
0184 }
0185
0186 std::string label() const override { return "CuboidProtoDesignator"; }
0187
0188 void apply(PortalShellBase& shell, const Logger& logger,
0189 const std::string& prefix) override {
0190 auto* cuboidShell = dynamic_cast<CuboidPortalShell*>(&shell);
0191 if (cuboidShell == nullptr) {
0192 throw std::invalid_argument(prefix +
0193 "Cuboid faces must use a valid face");
0194 }
0195
0196 ACTS_DEBUG(prefix << "Binning is set to compatible type");
0197 using enum CuboidVolumeBounds::Face;
0198
0199 for (const auto& [face, loc0, loc1] : m_binning) {
0200 auto* portal = cuboidShell->portal(face);
0201 if (portal == nullptr) {
0202 ACTS_ERROR(prefix << "Portal is nullptr");
0203 throw std::runtime_error("Portal is nullptr");
0204 }
0205
0206 ACTS_DEBUG(prefix << "Assigning material with binning: " << loc0 << ", "
0207 << loc1 << " to face " << face);
0208
0209 portal->surface().assignSurfaceMaterial(
0210 std::make_shared<ProtoGridSurfaceMaterial>(std::vector{loc0, loc1}));
0211 }
0212 }
0213
0214 void graphvizLabel(std::ostream& os) const override {
0215 os << "<br/><i>Cuboid Binning</i>";
0216 for (const auto& [face, loc0, loc1] : m_binning) {
0217 os << "<br/> at: " << face;
0218 os << ": " << loc0.getAxisDirection() << "=" << loc0.getAxis().getNBins();
0219 os << ", " << loc1.getAxisDirection() << "=" << loc1.getAxis().getNBins();
0220 }
0221 }
0222
0223 std::unique_ptr<DesignatorBase> merged(
0224 const DesignatorBase& other) const override {
0225 return other.merged(*this);
0226 }
0227
0228 std::unique_ptr<DesignatorBase> merged(
0229 const CuboidProtoDesignator& other) const override {
0230 auto merged = std::make_unique<CuboidProtoDesignator>(*this);
0231 std::ranges::copy(other.m_binning, std::back_inserter(merged->m_binning));
0232 return merged;
0233 }
0234
0235 std::unique_ptr<DesignatorBase> merged(
0236 const NullDesignator& ) const override {
0237 return std::make_unique<CuboidProtoDesignator>(*this);
0238 }
0239
0240 private:
0241 void validate(Face face, const DirectedProtoAxis& loc0,
0242 const DirectedProtoAxis& loc1, const std::string& prefix) {
0243 using enum CuboidVolumeBounds::Face;
0244 using enum AxisDirection;
0245
0246
0247 if (loc0.getAxisDirection() != AxisX || loc1.getAxisDirection() != AxisY) {
0248 throw std::invalid_argument(prefix +
0249 "Cuboid faces must use (x, y) binning");
0250 }
0251
0252 if (std::ranges::find_if(m_binning, [&](const auto& bin) {
0253 return std::get<0>(bin) == face;
0254 }) != m_binning.end()) {
0255 throw std::invalid_argument(prefix + "Cuboid face already configured");
0256 }
0257 }
0258
0259 std::vector<std::tuple<Face, DirectedProtoAxis, DirectedProtoAxis>> m_binning;
0260 };
0261
0262 template <typename face_enum_t, typename shell_type_t>
0263 class ISurfaceMaterialDesignator : public DesignatorBase {
0264 public:
0265 using FaceType = face_enum_t;
0266 using ShellType = shell_type_t;
0267
0268 ISurfaceMaterialDesignator(FaceType face,
0269 std::shared_ptr<const ISurfaceMaterial> material) {
0270 m_materials.emplace_back(face, std::move(material));
0271 }
0272
0273 std::string label() const override {
0274 return std::string{shellTypeName()} + " ISurfaceMaterialDesignator";
0275 }
0276
0277 void apply(PortalShellBase& shell, const Logger& logger,
0278 const std::string& prefix) override {
0279 auto* concreteShell = dynamic_cast<ShellType*>(&shell);
0280 if (concreteShell == nullptr) {
0281 ACTS_ERROR(prefix << "Concrete shell type mismatch: configured for "
0282 << boost::core::demangle(typeid(ShellType).name())
0283 << " but received "
0284 << boost::core::demangle(typeid(shell).name()));
0285
0286 throw std::invalid_argument(prefix + "Concrete shell type mismatch");
0287 }
0288
0289 for (const auto& [face, material] : m_materials) {
0290 auto* portal = concreteShell->portal(face);
0291 if (portal == nullptr) {
0292 ACTS_ERROR(prefix << "Portal is nullptr");
0293 throw std::runtime_error("Portal is nullptr");
0294 }
0295
0296 portal->surface().assignSurfaceMaterial(material);
0297 }
0298 }
0299
0300 constexpr std::string_view shellTypeName() const {
0301 if constexpr (std::is_same_v<ShellType, CylinderPortalShell>) {
0302 return "Cylinder";
0303 } else if constexpr (std::is_same_v<ShellType, CuboidPortalShell>) {
0304 return "Cuboid";
0305 } else {
0306 throw std::runtime_error("Unknown shell type");
0307 }
0308 }
0309
0310 void graphvizLabel(std::ostream& os) const override {
0311 os << "<br/><i>Homog. " << shellTypeName() << " material</i>";
0312 for (const auto& [face, material] : m_materials) {
0313 os << "<br/> at: " << face;
0314 }
0315 }
0316
0317 std::unique_ptr<DesignatorBase> merged(
0318 const DesignatorBase& other) const override {
0319 return other.merged(*this);
0320 }
0321
0322 std::unique_ptr<DesignatorBase> merged(
0323 const ISurfaceMaterialDesignator& other) const override {
0324 auto merged = std::make_unique<ISurfaceMaterialDesignator>(*this);
0325 std::ranges::copy(other.m_materials,
0326 std::back_inserter(merged->m_materials));
0327 return merged;
0328 }
0329
0330 std::unique_ptr<DesignatorBase> merged(
0331 const NullDesignator& ) const override {
0332 return std::make_unique<ISurfaceMaterialDesignator>(*this);
0333 }
0334
0335 private:
0336 std::vector<std::pair<FaceType, std::shared_ptr<const ISurfaceMaterial>>>
0337 m_materials;
0338 };
0339
0340 class NullDesignator : public DesignatorBase {
0341 public:
0342 void apply(PortalShellBase& , const Logger& logger,
0343 const std::string& prefix) override {
0344 ACTS_WARNING(prefix << "MaterialDesignator was not configured with any "
0345 << "material designation! Check your configuration.");
0346 }
0347
0348 std::string label() const override { return "NullDesignator"; }
0349
0350 std::unique_ptr<DesignatorBase> merged(
0351 const DesignatorBase& other) const override {
0352 return other.merged(*this);
0353 }
0354
0355 std::unique_ptr<DesignatorBase> merged(
0356 const CylinderProtoDesignator& other) const override {
0357 return std::make_unique<CylinderProtoDesignator>(other);
0358 }
0359
0360 std::unique_ptr<DesignatorBase> merged(
0361 const CuboidProtoDesignator& other) const override {
0362 return std::make_unique<CuboidProtoDesignator>(other);
0363 }
0364
0365 std::unique_ptr<DesignatorBase> merged(
0366 const NullDesignator& ) const override {
0367 return std::make_unique<NullDesignator>();
0368 }
0369
0370 std::unique_ptr<DesignatorBase> merged(
0371 const CylinderHomogeneousMaterialDesignator& other) const override {
0372 return other.merged(*this);
0373 }
0374
0375 std::unique_ptr<DesignatorBase> merged(
0376 const CuboidHomogeneousMaterialDesignator& other) const override {
0377 return other.merged(*this);
0378 }
0379
0380 void graphvizLabel(std::ostream& os) const override {
0381 os << "<br/><i>NullDesignator</i>";
0382 }
0383 };
0384
0385 inline std::string mergingError(const DesignatorBase& lhs,
0386 const DesignatorBase& rhs) {
0387 return "MaterialDesignator: Merging of " + lhs.label() + " with " +
0388 rhs.label() +
0389 " is not supported. This means you are trying to "
0390 "configure the same face with different binning or "
0391 "materials. Please check your configuration.";
0392 }
0393
0394 inline std::unique_ptr<DesignatorBase> DesignatorBase::merged(
0395 const CylinderProtoDesignator& other) const {
0396 throw std::runtime_error(mergingError(*this, other));
0397 }
0398
0399 inline std::unique_ptr<DesignatorBase> DesignatorBase::merged(
0400 const CuboidProtoDesignator& other) const {
0401 throw std::runtime_error(mergingError(*this, other));
0402 }
0403
0404 inline std::unique_ptr<DesignatorBase> DesignatorBase::merged(
0405 const NullDesignator& other) const {
0406 throw std::runtime_error(mergingError(*this, other));
0407 }
0408
0409 inline std::unique_ptr<DesignatorBase> DesignatorBase::merged(
0410 const CylinderHomogeneousMaterialDesignator& other) const {
0411 throw std::runtime_error(mergingError(*this, other));
0412 }
0413
0414 inline std::unique_ptr<DesignatorBase> DesignatorBase::merged(
0415 const CuboidHomogeneousMaterialDesignator& other) const {
0416 throw std::runtime_error(mergingError(*this, other));
0417 }
0418
0419 }