Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:11:24

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