Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-09-16 08:19:35

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 #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 <sstream>
0024 #include <stdexcept>
0025 #include <unordered_map>
0026 
0027 namespace Acts {
0028 
0029 void CuboidPortalShell::fill(TrackingVolume& volume) {
0030   using enum CuboidVolumeBounds::Face;
0031   for (Face face : {NegativeZFace, PositiveZFace, NegativeXFace, PositiveXFace,
0032                     NegativeYFace, PositiveYFace}) {
0033     const auto& portalAtFace = portal(face);
0034     if (portalAtFace != nullptr) {
0035       portalAtFace->fill(volume);
0036       volume.addPortal(portalAtFace);
0037     }
0038   }
0039 }
0040 
0041 SingleCuboidPortalShell::SingleCuboidPortalShell(const GeometryContext& gctx,
0042                                                  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   std::vector<OrientedSurface> orientedSurfaces =
0054       bounds.orientedSurfaces(m_volume->localToGlobalTransform(gctx));
0055 
0056   auto handle = [&](Face face, std::size_t from) {
0057     const auto& source = orientedSurfaces.at(from);
0058     m_portals.at(toUnderlying(face)) =
0059         std::make_shared<Portal>(source.direction, source.surface, *m_volume);
0060   };
0061 
0062   handle(NegativeZFace, negativeFaceXY);
0063   handle(PositiveZFace, positiveFaceXY);
0064   handle(NegativeXFace, negativeFaceYZ);
0065   handle(PositiveXFace, positiveFaceYZ);
0066   handle(NegativeYFace, negativeFaceZX);
0067   handle(PositiveYFace, positiveFaceZX);
0068 }
0069 
0070 std::shared_ptr<Portal> SingleCuboidPortalShell::portal(Face face) {
0071   return m_portals.at(toUnderlying(face));
0072 }
0073 
0074 void SingleCuboidPortalShell::setPortal(std::shared_ptr<Portal> portal,
0075                                         Face face) {
0076   assert(portal != nullptr);
0077   assert(portal->isValid());
0078   m_portals.at(toUnderlying(face)) = std::move(portal);
0079 }
0080 
0081 std::size_t SingleCuboidPortalShell::size() const {
0082   std::size_t count = 0;
0083   std::ranges::for_each(
0084       m_portals, [&count](const auto& portal) { count += portal ? 1 : 0; });
0085   return count;
0086 }
0087 
0088 void SingleCuboidPortalShell::applyToVolume() {
0089   for (std::size_t i = 0; i < m_portals.size(); i++) {
0090     const auto& portal = m_portals.at(i);
0091     if (portal != nullptr) {
0092       if (!portal->isValid()) {
0093         std::stringstream ss;
0094         ss << static_cast<Face>(i);
0095         throw std::runtime_error{"Invalid portal found in shell at " +
0096                                  ss.str()};
0097       }
0098       m_volume->addPortal(portal);
0099     }
0100   }
0101 }
0102 
0103 bool SingleCuboidPortalShell::isValid() const {
0104   return std::ranges::all_of(m_portals, [](const auto& portal) {
0105     return portal == nullptr || portal->isValid();
0106   });
0107 };
0108 
0109 std::string SingleCuboidPortalShell::label() const {
0110   std::stringstream ss;
0111   ss << "CuboidShell(vol=" << m_volume->volumeName() << ")";
0112   return ss.str();
0113 }
0114 
0115 CuboidStackPortalShell::CuboidStackPortalShell(
0116     const GeometryContext& gctx, std::vector<CuboidPortalShell*> shells,
0117     AxisDirection direction, const Logger& logger,
0118     PortalMaterialMergePolicy materialPolicy)
0119     : m_direction(direction), m_shells{std::move(shells)} {
0120   using enum CuboidVolumeBounds::Face;
0121   using enum AxisDirection;
0122   std::tie(m_frontFace, m_backFace, m_sideFaces) =
0123       CuboidVolumeBounds::facesFromAxisDirection(m_direction);
0124 
0125   std::unordered_map<Face, AxisDirection> onSurfaceDirs;
0126   switch (m_direction) {
0127     case AxisX:
0128       onSurfaceDirs = {{NegativeZFace, AxisX},
0129                        {PositiveZFace, AxisX},
0130                        {NegativeYFace, AxisY},
0131                        {PositiveYFace, AxisY}};
0132       break;
0133     case AxisY:
0134       onSurfaceDirs = {{NegativeZFace, AxisY},
0135                        {PositiveZFace, AxisY},
0136                        {NegativeXFace, AxisX},
0137                        {PositiveXFace, AxisX}};
0138       break;
0139     case AxisZ:
0140       onSurfaceDirs = {{NegativeXFace, AxisY},
0141                        {PositiveXFace, AxisY},
0142                        {NegativeYFace, AxisX},
0143                        {PositiveYFace, AxisX}};
0144       break;
0145     default:
0146       throw std::invalid_argument("CuboidPortalShell: Invalid axis direction");
0147   }
0148 
0149   ACTS_VERBOSE("Making cuboid stack shell in " << m_direction << " direction");
0150   if (std::ranges::any_of(m_shells,
0151                           [](const auto* shell) { return shell == nullptr; })) {
0152     ACTS_ERROR("Invalid shell pointer");
0153     throw std::invalid_argument("Invalid shell pointer");
0154   }
0155 
0156   ACTS_VERBOSE(" ~> " << label());
0157 
0158   if (!std::ranges::all_of(
0159           m_shells, [](const auto* shell) { return shell->isValid(); })) {
0160     ACTS_ERROR("Invalid shell");
0161     throw std::invalid_argument("Invalid shell");
0162   }
0163 
0164   std::ranges::sort(
0165       m_shells, [*this, &gctx](const auto& shellA, const auto& shellB) {
0166         switch (m_direction) {
0167           case AxisX:
0168             return (shellA->localToGlobalTransform(gctx).translation().x() <
0169                     shellB->localToGlobalTransform(gctx).translation().x());
0170           case AxisY:
0171             return (shellA->localToGlobalTransform(gctx).translation().y() <
0172                     shellB->localToGlobalTransform(gctx).translation().y());
0173           case AxisZ:
0174             return (shellA->localToGlobalTransform(gctx).translation().z() <
0175                     shellB->localToGlobalTransform(gctx).translation().z());
0176           default:
0177             throw std::invalid_argument(
0178                 "CuboidPortalShell: Invalid axis direction");
0179         }
0180       });
0181 
0182   auto merge = [&](Face face) {
0183     std::vector<std::shared_ptr<Portal>> portals;
0184     std::ranges::transform(m_shells, std::back_inserter(portals),
0185                            [face](auto* shell) { return shell->portal(face); });
0186 
0187     std::shared_ptr<Portal> merged;
0188     try {
0189       merged = std::accumulate(
0190           std::next(portals.begin()), portals.end(), portals.front(),
0191           [&](const auto& aPortal,
0192               const auto& bPortal) -> std::shared_ptr<Portal> {
0193             assert(aPortal != nullptr);
0194             assert(bPortal != nullptr);
0195 
0196             AxisDirection onSurfaceAxis = onSurfaceDirs.at(face);
0197 
0198             return std::make_shared<Portal>(
0199                 Portal::merge(gctx, *aPortal, *bPortal, onSurfaceAxis, logger,
0200                               materialPolicy));
0201           });
0202     } catch (const PortalMergingException& e) {
0203       std::stringstream ss;
0204       ss << "Failed to merge portals at face " << face << " while building "
0205          << label() << " (stacking in " << m_direction << "). Shells involved:";
0206       for (const auto* shell : m_shells) {
0207         ss << "\n  - " << shell->label();
0208       }
0209       ss << "\nUnderlying error: " << e.what();
0210       ACTS_ERROR(ss.str());
0211       throw PortalMergingException{ss.str()};
0212     }
0213 
0214     assert(merged != nullptr);
0215     assert(merged->isValid());
0216 
0217     // reset merged portal on all shells
0218     for (auto& shell : m_shells) {
0219       shell->setPortal(merged, face);
0220     }
0221   };
0222 
0223   auto fuse = [&](Face faceA, Face faceB) {
0224     for (std::size_t i = 1; i < m_shells.size(); i++) {
0225       auto& shellA = m_shells.at(i - 1);
0226       auto& shellB = m_shells.at(i);
0227       ACTS_VERBOSE("Fusing " << shellA->label() << " and " << shellB->label());
0228 
0229       auto fused = std::make_shared<Portal>(Portal::fuse(
0230           gctx, *shellA->portal(faceA), *shellB->portal(faceB), logger));
0231 
0232       assert(fused != nullptr && "Invalid fused portal");
0233       assert(fused->isValid() && "Fused portal is invalid");
0234 
0235       shellA->setPortal(fused, faceA);
0236       shellB->setPortal(fused, faceB);
0237 
0238       assert(shellA->isValid() && "Shell A is not valid after fusing");
0239       assert(shellB->isValid() && "Shell B is not valid after fusing");
0240     }
0241   };
0242 
0243   for (const auto& face : m_sideFaces) {
0244     merge(face);
0245   }
0246   fuse(m_backFace, m_frontFace);
0247   assert(isValid() && "Shell is not valid after construction");
0248 }
0249 
0250 std::size_t CuboidStackPortalShell::size() const {
0251   return 6;
0252 }
0253 
0254 std::vector<CuboidStackPortalShell::Face> CuboidStackPortalShell::mergedFaces(
0255     AxisDirection direction) {
0256   // The side faces (parallel to the stacking direction) are merged, the front
0257   // and back faces are fused.
0258   const auto& [frontFace, backFace, sideFaces] =
0259       CuboidVolumeBounds::facesFromAxisDirection(direction);
0260   return {sideFaces.begin(), sideFaces.end()};
0261 }
0262 
0263 std::shared_ptr<Portal> CuboidStackPortalShell::portal(Face face) {
0264   if (face == m_backFace) {
0265     return m_shells.back()->portal(face);
0266   } else {
0267     return m_shells.front()->portal(face);
0268   }
0269 }
0270 
0271 void CuboidStackPortalShell::setPortal(std::shared_ptr<Portal> portal,
0272                                        Face face) {
0273   assert(portal != nullptr);
0274 
0275   if (face == m_backFace) {
0276     m_shells.back()->setPortal(std::move(portal), face);
0277   } else if (face == m_frontFace) {
0278     m_shells.front()->setPortal(std::move(portal), face);
0279   } else {
0280     for (auto& shell : m_shells) {
0281       shell->setPortal(portal, face);
0282     }
0283   }
0284 }
0285 
0286 bool CuboidStackPortalShell::isValid() const {
0287   return std::ranges::all_of(m_shells, [](const auto* shell) {
0288     assert(shell != nullptr);
0289     return shell->isValid();
0290   });
0291 }
0292 
0293 const Transform3& CuboidStackPortalShell::localToGlobalTransform(
0294     const GeometryContext& gctx) const {
0295   return m_shells.front()->localToGlobalTransform(gctx);
0296 }
0297 
0298 std::string CuboidStackPortalShell::label() const {
0299   // Deliberately shallow: only describe this shell, not the whole subtree.
0300   // Expanding child labels recursively makes error messages for non-trivial
0301   // geometries unreadable. The immediate children are reported separately where
0302   // relevant (e.g. in merge-failure messages).
0303   std::stringstream ss;
0304   ss << "CuboidStackShell(dir=" << m_direction << ", " << m_shells.size()
0305      << " children)";
0306   return ss.str();
0307 }
0308 
0309 std::ostream& operator<<(std::ostream& os, CuboidPortalShell::Face face) {
0310   switch (face) {
0311     using enum CuboidVolumeBounds::Face;
0312     case PositiveZFace:
0313       return os << "PositiveZFace";
0314     case NegativeZFace:
0315       return os << "NegativeZFace";
0316     case PositiveXFace:
0317       return os << "PositiveXFace";
0318     case NegativeXFace:
0319       return os << "NegativeXFace";
0320     case PositiveYFace:
0321       return os << "PositiveYFace";
0322     case NegativeYFace:
0323       return os << "NegativeYFace";
0324     default:
0325       return os << "Invalid face";
0326   }
0327 }
0328 
0329 }  // namespace Acts