File indexing completed on 2026-07-16 08:07:47
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include "Acts/Geometry/CuboidPortalShell.hpp"
0010
0011 #include "Acts/Definitions/Algebra.hpp"
0012 #include "Acts/Geometry/BoundarySurfaceFace.hpp"
0013 #include "Acts/Geometry/CuboidVolumeBounds.hpp"
0014 #include "Acts/Geometry/Portal.hpp"
0015 #include "Acts/Geometry/PortalLinkBase.hpp"
0016 #include "Acts/Geometry/TrackingVolume.hpp"
0017 #include "Acts/Utilities/AxisDefinitions.hpp"
0018
0019 #include <algorithm>
0020 #include <array>
0021 #include <cstddef>
0022 #include <numeric>
0023 #include <stdexcept>
0024 #include <unordered_map>
0025
0026 #include <boost/algorithm/string/join.hpp>
0027
0028 namespace Acts {
0029
0030 void CuboidPortalShell::fill(TrackingVolume& volume) {
0031 using enum CuboidVolumeBounds::Face;
0032 for (Face face : {NegativeZFace, PositiveZFace, NegativeXFace, PositiveXFace,
0033 NegativeYFace, PositiveYFace}) {
0034 const auto& portalAtFace = portal(face);
0035 if (portalAtFace != nullptr) {
0036 portalAtFace->fill(volume);
0037 volume.addPortal(portalAtFace);
0038 }
0039 }
0040 }
0041
0042 SingleCuboidPortalShell::SingleCuboidPortalShell(TrackingVolume& volume)
0043 : m_volume{&volume} {
0044 using enum CuboidVolumeBounds::Face;
0045 if (m_volume->volumeBounds().type() != VolumeBounds::BoundsType::eCuboid) {
0046 throw std::invalid_argument(
0047 "CuboidPortalShell: Invalid volume bounds type");
0048 }
0049
0050 const auto& bounds =
0051 dynamic_cast<const CuboidVolumeBounds&>(m_volume->volumeBounds());
0052
0053 ACTS_PUSH_IGNORE_DEPRECATED()
0054 std::vector<OrientedSurface> orientedSurfaces =
0055 bounds.orientedSurfaces(m_volume->transform());
0056 ACTS_POP_IGNORE_DEPRECATED()
0057
0058 auto handle = [&](Face face, std::size_t from) {
0059 const auto& source = orientedSurfaces.at(from);
0060 m_portals.at(toUnderlying(face)) =
0061 std::make_shared<Portal>(source.direction, source.surface, *m_volume);
0062 };
0063
0064 handle(NegativeZFace, negativeFaceXY);
0065 handle(PositiveZFace, positiveFaceXY);
0066 handle(NegativeXFace, negativeFaceYZ);
0067 handle(PositiveXFace, positiveFaceYZ);
0068 handle(NegativeYFace, negativeFaceZX);
0069 handle(PositiveYFace, positiveFaceZX);
0070 }
0071
0072 std::shared_ptr<Portal> SingleCuboidPortalShell::portal(Face face) {
0073 return m_portals.at(toUnderlying(face));
0074 }
0075
0076 void SingleCuboidPortalShell::setPortal(std::shared_ptr<Portal> portal,
0077 Face face) {
0078 assert(portal != nullptr);
0079 assert(portal->isValid());
0080 m_portals.at(toUnderlying(face)) = std::move(portal);
0081 }
0082
0083 std::size_t SingleCuboidPortalShell::size() const {
0084 std::size_t count = 0;
0085 std::ranges::for_each(
0086 m_portals, [&count](const auto& portal) { count += portal ? 1 : 0; });
0087 return count;
0088 }
0089
0090 void SingleCuboidPortalShell::applyToVolume() {
0091 for (std::size_t i = 0; i < m_portals.size(); i++) {
0092 const auto& portal = m_portals.at(i);
0093 if (portal != nullptr) {
0094 if (!portal->isValid()) {
0095 std::stringstream ss;
0096 ss << static_cast<Face>(i);
0097 throw std::runtime_error{"Invalid portal found in shell at " +
0098 ss.str()};
0099 }
0100 m_volume->addPortal(portal);
0101 }
0102 }
0103 }
0104
0105 bool SingleCuboidPortalShell::isValid() const {
0106 return std::ranges::all_of(m_portals, [](const auto& portal) {
0107 return portal == nullptr || portal->isValid();
0108 });
0109 };
0110
0111 std::string SingleCuboidPortalShell::label() const {
0112 std::stringstream ss;
0113 ss << "CuboidShell(vol=" << m_volume->volumeName() << ")";
0114 return ss.str();
0115 }
0116
0117 CuboidStackPortalShell::CuboidStackPortalShell(
0118 const GeometryContext& gctx, std::vector<CuboidPortalShell*> shells,
0119 AxisDirection direction, const Logger& logger)
0120 : m_direction(direction), m_shells{std::move(shells)} {
0121 using enum CuboidVolumeBounds::Face;
0122 using enum AxisDirection;
0123 std::tie(m_frontFace, m_backFace, m_sideFaces) =
0124 CuboidVolumeBounds::facesFromAxisDirection(m_direction);
0125
0126 std::unordered_map<Face, AxisDirection> onSurfaceDirs;
0127 switch (m_direction) {
0128 case AxisX:
0129 onSurfaceDirs = {{NegativeZFace, AxisX},
0130 {PositiveZFace, AxisX},
0131 {NegativeYFace, AxisY},
0132 {PositiveYFace, AxisY}};
0133 break;
0134 case AxisY:
0135 onSurfaceDirs = {{NegativeZFace, AxisY},
0136 {PositiveZFace, AxisY},
0137 {NegativeXFace, AxisX},
0138 {PositiveXFace, AxisX}};
0139 break;
0140 case AxisZ:
0141 onSurfaceDirs = {{NegativeXFace, AxisY},
0142 {PositiveXFace, AxisY},
0143 {NegativeYFace, AxisX},
0144 {PositiveYFace, AxisX}};
0145 break;
0146 default:
0147 throw std::invalid_argument("CuboidPortalShell: Invalid axis direction");
0148 }
0149
0150 ACTS_VERBOSE("Making cuboid stack shell in " << m_direction << " direction");
0151 if (std::ranges::any_of(m_shells,
0152 [](const auto* shell) { return shell == nullptr; })) {
0153 ACTS_ERROR("Invalid shell pointer");
0154 throw std::invalid_argument("Invalid shell pointer");
0155 }
0156
0157 ACTS_VERBOSE(" ~> " << label());
0158
0159 if (!std::ranges::all_of(
0160 m_shells, [](const auto* shell) { return shell->isValid(); })) {
0161 ACTS_ERROR("Invalid shell");
0162 throw std::invalid_argument("Invalid shell");
0163 }
0164
0165 std::ranges::sort(
0166 m_shells, [*this, &gctx](const auto& shellA, const auto& shellB) {
0167 switch (m_direction) {
0168 case AxisX:
0169 return (shellA->localToGlobalTransform(gctx).translation().x() <
0170 shellB->localToGlobalTransform(gctx).translation().x());
0171 case AxisY:
0172 return (shellA->localToGlobalTransform(gctx).translation().y() <
0173 shellB->localToGlobalTransform(gctx).translation().y());
0174 case AxisZ:
0175 return (shellA->localToGlobalTransform(gctx).translation().z() <
0176 shellB->localToGlobalTransform(gctx).translation().z());
0177 default:
0178 throw std::invalid_argument(
0179 "CuboidPortalShell: Invalid axis direction");
0180 }
0181 });
0182
0183 auto merge = [&](Face face) {
0184 std::vector<std::shared_ptr<Portal>> portals;
0185 std::ranges::transform(m_shells, std::back_inserter(portals),
0186 [face](auto* shell) { return shell->portal(face); });
0187
0188 auto merged = std::accumulate(
0189 std::next(portals.begin()), portals.end(), portals.front(),
0190 [&](const auto& aPortal,
0191 const auto& bPortal) -> std::shared_ptr<Portal> {
0192 assert(aPortal != nullptr);
0193 assert(bPortal != nullptr);
0194
0195 AxisDirection onSurfaceAxis = onSurfaceDirs.at(face);
0196
0197 return std::make_shared<Portal>(
0198 Portal::merge(gctx, *aPortal, *bPortal, onSurfaceAxis, logger));
0199 });
0200
0201 assert(merged != nullptr);
0202 assert(merged->isValid());
0203
0204
0205 for (auto& shell : m_shells) {
0206 shell->setPortal(merged, face);
0207 }
0208 };
0209
0210 auto fuse = [&](Face faceA, Face faceB) {
0211 for (std::size_t i = 1; i < m_shells.size(); i++) {
0212 auto& shellA = m_shells.at(i - 1);
0213 auto& shellB = m_shells.at(i);
0214 ACTS_VERBOSE("Fusing " << shellA->label() << " and " << shellB->label());
0215
0216 auto fused = std::make_shared<Portal>(Portal::fuse(
0217 gctx, *shellA->portal(faceA), *shellB->portal(faceB), logger));
0218
0219 assert(fused != nullptr && "Invalid fused portal");
0220 assert(fused->isValid() && "Fused portal is invalid");
0221
0222 shellA->setPortal(fused, faceA);
0223 shellB->setPortal(fused, faceB);
0224
0225 assert(shellA->isValid() && "Shell A is not valid after fusing");
0226 assert(shellB->isValid() && "Shell B is not valid after fusing");
0227 }
0228 };
0229
0230 for (const auto& face : m_sideFaces) {
0231 merge(face);
0232 }
0233 fuse(m_backFace, m_frontFace);
0234 assert(isValid() && "Shell is not valid after construction");
0235 }
0236
0237 std::size_t CuboidStackPortalShell::size() const {
0238 return 6;
0239 }
0240
0241 std::shared_ptr<Portal> CuboidStackPortalShell::portal(Face face) {
0242 if (face == m_backFace) {
0243 return m_shells.back()->portal(face);
0244 } else {
0245 return m_shells.front()->portal(face);
0246 }
0247 }
0248
0249 void CuboidStackPortalShell::setPortal(std::shared_ptr<Portal> portal,
0250 Face face) {
0251 assert(portal != nullptr);
0252
0253 if (face == m_backFace) {
0254 m_shells.back()->setPortal(std::move(portal), face);
0255 } else if (face == m_frontFace) {
0256 m_shells.front()->setPortal(std::move(portal), face);
0257 } else {
0258 for (auto& shell : m_shells) {
0259 shell->setPortal(portal, face);
0260 }
0261 }
0262 }
0263
0264 bool CuboidStackPortalShell::isValid() const {
0265 return std::ranges::all_of(m_shells, [](const auto* shell) {
0266 assert(shell != nullptr);
0267 return shell->isValid();
0268 });
0269 }
0270
0271 const Transform3& CuboidStackPortalShell::localToGlobalTransform(
0272 const GeometryContext& gctx) const {
0273 return m_shells.front()->localToGlobalTransform(gctx);
0274 }
0275
0276 std::string CuboidStackPortalShell::label() const {
0277 std::stringstream ss;
0278 ss << "CuboidStackShell(dir=" << m_direction << ", children=";
0279
0280 std::vector<std::string> labels;
0281 std::ranges::transform(m_shells, std::back_inserter(labels),
0282 [](const auto* shell) { return shell->label(); });
0283 ss << boost::algorithm::join(labels, "|");
0284 ss << ")";
0285 return ss.str();
0286 }
0287
0288 std::ostream& operator<<(std::ostream& os, CuboidPortalShell::Face face) {
0289 switch (face) {
0290 using enum CuboidVolumeBounds::Face;
0291 case PositiveZFace:
0292 return os << "PositiveZFace";
0293 case NegativeZFace:
0294 return os << "NegativeZFace";
0295 case PositiveXFace:
0296 return os << "PositiveXFace";
0297 case NegativeXFace:
0298 return os << "NegativeXFace";
0299 case PositiveYFace:
0300 return os << "PositiveYFace";
0301 case NegativeYFace:
0302 return os << "NegativeYFace";
0303 default:
0304 return os << "Invalid face";
0305 }
0306 }
0307
0308 }