Back to home page

sPhenix code displayed by LXR

 
 

    


File indexing completed on 2026-07-16 08:07:47

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/CylinderPortalShell.hpp"
0010 
0011 #include "Acts/Geometry/BoundarySurfaceFace.hpp"
0012 #include "Acts/Geometry/CylinderVolumeBounds.hpp"
0013 #include "Acts/Geometry/Portal.hpp"
0014 #include "Acts/Geometry/PortalLinkBase.hpp"
0015 #include "Acts/Geometry/TrackingVolume.hpp"
0016 
0017 #include <algorithm>
0018 #include <numeric>
0019 #include <stdexcept>
0020 
0021 #include <boost/algorithm/string/join.hpp>
0022 
0023 namespace Acts {
0024 
0025 void CylinderPortalShell::fill(TrackingVolume& volume) {
0026   for (Face face : {PositiveDisc, NegativeDisc, OuterCylinder, InnerCylinder,
0027                     NegativePhiPlane, PositivePhiPlane}) {
0028     const auto& portalAtFace = portal(face);
0029     if (portalAtFace != nullptr) {
0030       portalAtFace->fill(volume);
0031       volume.addPortal(portalAtFace);
0032     }
0033   }
0034 }
0035 
0036 SingleCylinderPortalShell::SingleCylinderPortalShell(TrackingVolume& volume)
0037     : m_volume{&volume} {
0038   if (m_volume->volumeBounds().type() != VolumeBounds::BoundsType::eCylinder) {
0039     throw std::invalid_argument(
0040         "CylinderPortalShell: Invalid volume bounds type");
0041   }
0042 
0043   const auto& bounds =
0044       dynamic_cast<const CylinderVolumeBounds&>(m_volume->volumeBounds());
0045 
0046   ACTS_PUSH_IGNORE_DEPRECATED()
0047   std::vector<OrientedSurface> orientedSurfaces =
0048       bounds.orientedSurfaces(m_volume->transform());
0049   ACTS_POP_IGNORE_DEPRECATED()
0050 
0051   auto handle = [&](Face face, std::size_t from) {
0052     const auto& source = orientedSurfaces.at(from);
0053     m_portals.at(toUnderlying(face)) =
0054         std::make_shared<Portal>(source.direction, source.surface, *m_volume);
0055   };
0056 
0057   if (orientedSurfaces.size() == 6) {
0058     // Fully equipped cylinder
0059     handle(PositiveDisc, positiveFaceXY);
0060     handle(NegativeDisc, negativeFaceXY);
0061     handle(OuterCylinder, tubeOuterCover);
0062     handle(InnerCylinder, tubeInnerCover);
0063     handle(NegativePhiPlane, tubeSectorNegativePhi);
0064     handle(PositivePhiPlane, tubeSectorPositivePhi);
0065   } else if (orientedSurfaces.size() == 5) {
0066     // Phi sector but no inner cylinder (rMin == 0)
0067     handle(PositiveDisc, positiveFaceXY);
0068     handle(NegativeDisc, negativeFaceXY);
0069     handle(OuterCylinder, tubeOuterCover);
0070     // Skip inner tube cover, requires offsetting
0071     handle(NegativePhiPlane, tubeSectorNegativePhi - 1);
0072     handle(PositivePhiPlane, tubeSectorPositivePhi - 1);
0073   } else if (orientedSurfaces.size() == 4) {
0074     // No phi sector but rMin > 0
0075     handle(PositiveDisc, positiveFaceXY);
0076     handle(NegativeDisc, negativeFaceXY);
0077     handle(OuterCylinder, tubeOuterCover);
0078     handle(InnerCylinder, tubeInnerCover);
0079   } else if (orientedSurfaces.size() == 3) {
0080     // No phi sector and rMin == 0
0081     handle(PositiveDisc, positiveFaceXY);
0082     handle(NegativeDisc, negativeFaceXY);
0083     handle(OuterCylinder, tubeOuterCover);
0084   } else {
0085     throw std::invalid_argument("Invalid number of oriented surfaces");
0086   }
0087 }
0088 
0089 std::shared_ptr<Portal> SingleCylinderPortalShell::portal(Face face) {
0090   return m_portals.at(toUnderlying(face));
0091 }
0092 
0093 void SingleCylinderPortalShell::setPortal(std::shared_ptr<Portal> portal,
0094                                           Face face) {
0095   assert(portal != nullptr);
0096   assert(portal->isValid());
0097   m_portals.at(toUnderlying(face)) = std::move(portal);
0098 }
0099 
0100 std::size_t SingleCylinderPortalShell::size() const {
0101   std::size_t count = 0;
0102   std::ranges::for_each(
0103       m_portals, [&count](const auto& portal) { count += portal ? 1 : 0; });
0104   return count;
0105 }
0106 
0107 void SingleCylinderPortalShell::applyToVolume() {
0108   for (std::size_t i = 0; i < m_portals.size(); i++) {
0109     const auto& portal = m_portals.at(i);
0110     if (portal != nullptr) {
0111       if (!portal->isValid()) {
0112         std::stringstream ss;
0113         ss << static_cast<Face>(i);
0114         throw std::runtime_error{"Invalid portal found in shell at " +
0115                                  ss.str()};
0116       }
0117       m_volume->addPortal(portal);
0118     }
0119   }
0120 }
0121 
0122 bool SingleCylinderPortalShell::isValid() const {
0123   return std::ranges::all_of(m_portals, [](const auto& portal) {
0124     return portal == nullptr || portal->isValid();
0125   });
0126 };
0127 
0128 std::string SingleCylinderPortalShell::label() const {
0129   std::stringstream ss;
0130   ss << "CylinderShell(vol=" << m_volume->volumeName() << ")";
0131   return ss.str();
0132 }
0133 
0134 CylinderStackPortalShell::CylinderStackPortalShell(
0135     const GeometryContext& gctx, std::vector<CylinderPortalShell*> shells,
0136     AxisDirection direction, const Logger& logger)
0137     : m_direction{direction}, m_shells{std::move(shells)} {
0138   ACTS_VERBOSE("Making cylinder stack shell in " << m_direction
0139                                                  << " direction");
0140   if (std::ranges::any_of(m_shells,
0141                           [](const auto* shell) { return shell == nullptr; })) {
0142     ACTS_ERROR("Invalid shell pointer");
0143     throw std::invalid_argument("Invalid shell pointer");
0144   }
0145 
0146   ACTS_VERBOSE(" ~> " << label());
0147 
0148   if (!std::ranges::all_of(
0149           m_shells, [](const auto* shell) { return shell->isValid(); })) {
0150     ACTS_ERROR("Invalid shell");
0151     throw std::invalid_argument("Invalid shell");
0152   }
0153 
0154   auto merge = [&](Face face) {
0155     std::vector<std::shared_ptr<Portal>> portals;
0156     std::ranges::transform(m_shells, std::back_inserter(portals),
0157                            [face](auto* shell) { return shell->portal(face); });
0158 
0159     auto merged = std::accumulate(
0160         std::next(portals.begin()), portals.end(), portals.front(),
0161         [&](const auto& aPortal,
0162             const auto& bPortal) -> std::shared_ptr<Portal> {
0163           assert(aPortal != nullptr);
0164           assert(bPortal != nullptr);
0165 
0166           return std::make_shared<Portal>(
0167               Portal::merge(gctx, *aPortal, *bPortal, direction, logger));
0168         });
0169 
0170     assert(merged != nullptr);
0171     assert(merged->isValid());
0172 
0173     // reset merged portal on all shells
0174     for (auto& shell : m_shells) {
0175       shell->setPortal(merged, face);
0176     }
0177   };
0178 
0179   auto fuse = [&](Face faceA, Face faceB) {
0180     for (std::size_t i = 1; i < m_shells.size(); i++) {
0181       auto& shellA = m_shells.at(i - 1);
0182       auto& shellB = m_shells.at(i);
0183       ACTS_VERBOSE("Fusing " << shellA->label() << " and " << shellB->label());
0184 
0185       auto fused = std::make_shared<Portal>(Portal::fuse(
0186           gctx, *shellA->portal(faceA), *shellB->portal(faceB), logger));
0187 
0188       assert(fused != nullptr && "Invalid fused portal");
0189       assert(fused->isValid() && "Fused portal is invalid");
0190 
0191       shellA->setPortal(fused, faceA);
0192       shellB->setPortal(fused, faceB);
0193 
0194       assert(shellA->isValid() && "Shell A is not valid after fusing");
0195       assert(shellB->isValid() && "Shell B is not valid after fusing");
0196     }
0197   };
0198 
0199   if (direction == AxisDirection::AxisR) {
0200     ACTS_VERBOSE("Merging portals at positive and negative discs");
0201     merge(PositiveDisc);
0202     merge(NegativeDisc);
0203 
0204     ACTS_VERBOSE("Fusing portals at outer and inner cylinders");
0205     fuse(OuterCylinder, InnerCylinder);
0206 
0207   } else if (direction == AxisDirection::AxisZ) {
0208     bool allHaveInnerCylinders = std::ranges::all_of(
0209         m_shells, [](const auto* shell) { return shell->size() == 4; });
0210 
0211     bool noneHaveInnerCylinders = std::ranges::all_of(
0212         m_shells, [](const auto* shell) { return shell->size() == 3; });
0213 
0214     if (!allHaveInnerCylinders && !noneHaveInnerCylinders) {
0215       ACTS_ERROR("Invalid inner cylinder configuration");
0216       throw std::invalid_argument("Invalid inner cylinder configuration");
0217     }
0218 
0219     m_hasInnerCylinder = allHaveInnerCylinders;
0220 
0221     ACTS_VERBOSE("Merging portals at outer cylinders");
0222     merge(OuterCylinder);
0223     assert(isValid() && "Shell is not valid after outer merging");
0224 
0225     if (m_hasInnerCylinder) {
0226       ACTS_VERBOSE("Merging portals at inner cylinders");
0227       merge(InnerCylinder);
0228       assert(isValid() && "Shell is not valid after inner merging");
0229     }
0230 
0231     ACTS_VERBOSE("Fusing portals at positive and negative discs");
0232     fuse(PositiveDisc, NegativeDisc);
0233     assert(isValid() && "Shell is not valid after disc fusing");
0234 
0235   } else {
0236     throw std::invalid_argument("Invalid direction");
0237   }
0238 
0239   assert(isValid() && "Shell is not valid after construction");
0240 }
0241 
0242 std::size_t CylinderStackPortalShell::size() const {
0243   return m_hasInnerCylinder ? 4 : 3;
0244 }
0245 
0246 std::shared_ptr<Portal> CylinderStackPortalShell::portal(Face face) {
0247   if (m_direction == AxisDirection::AxisR) {
0248     switch (face) {
0249       case NegativeDisc:
0250         return m_shells.front()->portal(NegativeDisc);
0251       case PositiveDisc:
0252         return m_shells.front()->portal(PositiveDisc);
0253       case OuterCylinder:
0254         return m_shells.back()->portal(OuterCylinder);
0255       case InnerCylinder:
0256         return m_shells.front()->portal(InnerCylinder);
0257       case NegativePhiPlane:
0258         [[fallthrough]];
0259       case PositivePhiPlane:
0260         return nullptr;
0261       default:
0262         std::stringstream ss;
0263         ss << "Invalid face: " << face;
0264         throw std::invalid_argument(ss.str());
0265     }
0266 
0267   } else {
0268     switch (face) {
0269       case NegativeDisc:
0270         return m_shells.front()->portal(NegativeDisc);
0271       case PositiveDisc:
0272         return m_shells.back()->portal(PositiveDisc);
0273       case OuterCylinder:
0274         [[fallthrough]];
0275       case InnerCylinder:
0276         return m_shells.front()->portal(face);
0277       case NegativePhiPlane:
0278         [[fallthrough]];
0279       case PositivePhiPlane:
0280         return nullptr;
0281       default:
0282         std::stringstream ss;
0283         ss << "Invalid face: " << face;
0284         throw std::invalid_argument(ss.str());
0285     }
0286   }
0287 }
0288 
0289 void CylinderStackPortalShell::setPortal(std::shared_ptr<Portal> portal,
0290                                          Face face) {
0291   assert(portal != nullptr);
0292 
0293   if (m_direction == AxisDirection::AxisR) {
0294     switch (face) {
0295       case NegativeDisc:
0296         [[fallthrough]];
0297       case PositiveDisc:
0298         for (auto* shell : m_shells) {
0299           shell->setPortal(portal, face);
0300         }
0301         break;
0302       case OuterCylinder:
0303         m_shells.back()->setPortal(std::move(portal), OuterCylinder);
0304         break;
0305       case InnerCylinder:
0306         if (!m_hasInnerCylinder) {
0307           throw std::invalid_argument("Inner cylinder not available");
0308         }
0309         m_shells.front()->setPortal(std::move(portal), InnerCylinder);
0310         break;
0311       default:
0312         std::stringstream ss;
0313         ss << "Invalid face: " << face;
0314         throw std::invalid_argument(ss.str());
0315     }
0316 
0317   } else {
0318     switch (face) {
0319       case NegativeDisc:
0320         m_shells.front()->setPortal(std::move(portal), NegativeDisc);
0321         break;
0322       case PositiveDisc:
0323         m_shells.back()->setPortal(std::move(portal), PositiveDisc);
0324         break;
0325       case InnerCylinder:
0326         if (!m_hasInnerCylinder) {
0327           throw std::invalid_argument("Inner cylinder not available");
0328         }
0329         [[fallthrough]];
0330       case OuterCylinder:
0331         for (auto* shell : m_shells) {
0332           shell->setPortal(portal, face);
0333         }
0334         break;
0335       default:
0336         std::stringstream ss;
0337         ss << "Invalid face: " << face;
0338         throw std::invalid_argument(ss.str());
0339     }
0340   }
0341 }
0342 
0343 bool CylinderStackPortalShell::isValid() const {
0344   return std::ranges::all_of(m_shells, [](const auto* shell) {
0345     assert(shell != nullptr);
0346     return shell->isValid();
0347   });
0348 }
0349 
0350 std::string CylinderStackPortalShell::label() const {
0351   std::stringstream ss;
0352   ss << "CylinderStackShell(dir=" << m_direction << ", children=";
0353 
0354   std::vector<std::string> labels;
0355   std::ranges::transform(m_shells, std::back_inserter(labels),
0356                          [](const auto* shell) { return shell->label(); });
0357   ss << boost::algorithm::join(labels, "|");
0358   ss << ")";
0359   return ss.str();
0360 }
0361 
0362 std::ostream& operator<<(std::ostream& os, CylinderPortalShell::Face face) {
0363   switch (face) {
0364     using enum CylinderVolumeBounds::Face;
0365     case PositiveDisc:
0366       return os << "PositiveDisc";
0367     case NegativeDisc:
0368       return os << "NegativeDisc";
0369     case OuterCylinder:
0370       return os << "OuterCylinder";
0371     case InnerCylinder:
0372       return os << "InnerCylinder";
0373     case NegativePhiPlane:
0374       return os << "NegativePhiPlane";
0375     case PositivePhiPlane:
0376       return os << "PositivePhiPlane";
0377     default:
0378       return os << "Invalid face";
0379   }
0380 }
0381 
0382 }  // namespace Acts