File indexing completed on 2025-01-18 09:11:24
0001
0002
0003
0004
0005
0006
0007
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
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
0064 handle(PositiveDisc, positiveFaceXY);
0065 handle(NegativeDisc, negativeFaceXY);
0066 handle(OuterCylinder, tubeOuterCover);
0067
0068 handle(NegativePhiPlane, tubeSectorNegativePhi - 1);
0069 handle(PositivePhiPlane, tubeSectorPositivePhi - 1);
0070 } else if (orientedSurfaces.size() == 4) {
0071
0072 handle(PositiveDisc, positiveFaceXY);
0073 handle(NegativeDisc, negativeFaceXY);
0074 handle(OuterCylinder, tubeOuterCover);
0075 handle(InnerCylinder, tubeInnerCover);
0076 } else if (orientedSurfaces.size() == 3) {
0077
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
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 }