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