File indexing completed on 2026-07-16 08:07:48
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include "Acts/Geometry/CylinderVolumeStack.hpp"
0010
0011 #include "Acts/Definitions/Algebra.hpp"
0012 #include "Acts/Definitions/Common.hpp"
0013 #include "Acts/Definitions/Tolerance.hpp"
0014 #include "Acts/Geometry/CylinderVolumeBounds.hpp"
0015 #include "Acts/Utilities/Logger.hpp"
0016 #include "Acts/Utilities/StringHelpers.hpp"
0017
0018 #include <algorithm>
0019 #include <memory>
0020 #include <numbers>
0021 #include <sstream>
0022
0023 namespace Acts {
0024
0025 struct CylinderVolumeStack::VolumeTuple {
0026 Volume* volume{};
0027 const CylinderVolumeBounds* bounds{};
0028 std::shared_ptr<CylinderVolumeBounds> updatedBounds{};
0029 Transform3 localTransform = Transform3::Identity();
0030 Transform3 globalTransform = Transform3::Identity();
0031
0032 bool transformDirty = false;
0033
0034 explicit VolumeTuple(const GeometryContext& gctx, Volume& volume_,
0035 const Transform3& groupTransform)
0036 : volume{&volume_},
0037 localTransform{groupTransform.inverse() *
0038 volume_.localToGlobalTransform(gctx)},
0039 globalTransform{volume_.localToGlobalTransform(gctx)} {
0040 bounds = dynamic_cast<const CylinderVolumeBounds*>(&volume_.volumeBounds());
0041 assert(bounds != nullptr);
0042 updatedBounds = std::make_shared<CylinderVolumeBounds>(*bounds);
0043 }
0044
0045 double midZ() const { return localTransform.translation()[eZ]; }
0046 double halfLengthZ() const {
0047 return updatedBounds->get(CylinderVolumeBounds::eHalfLengthZ);
0048 }
0049 double minZ() const { return midZ() - halfLengthZ(); }
0050 double maxZ() const { return midZ() + halfLengthZ(); }
0051
0052 double minR() const {
0053 return updatedBounds->get(CylinderVolumeBounds::eMinR);
0054 }
0055 double maxR() const {
0056 return updatedBounds->get(CylinderVolumeBounds::eMaxR);
0057 }
0058 double midR() const { return (minR() + maxR()) / 2.0; }
0059
0060 void set(std::initializer_list<
0061 std::pair<CylinderVolumeBounds::BoundValues, double>>
0062 keyValues) {
0063 updatedBounds->set(keyValues);
0064 }
0065
0066 void setLocalTransform(const Transform3& transform,
0067 const Transform3& groupTransform) {
0068 localTransform = transform;
0069 globalTransform = groupTransform * localTransform;
0070 transformDirty = true;
0071 }
0072
0073 void commit(const GeometryContext& gctx, const Logger& logger) {
0074
0075 auto copy = std::make_shared<CylinderVolumeBounds>(*updatedBounds);
0076
0077 std::optional<Transform3> transform = std::nullopt;
0078 if (transformDirty) {
0079 transform = globalTransform;
0080 }
0081
0082 volume->update(gctx, std::move(updatedBounds), transform, logger);
0083 bounds = copy.get();
0084 updatedBounds = std::move(copy);
0085 transformDirty = false;
0086 }
0087 };
0088
0089 CylinderVolumeStack::CylinderVolumeStack(const GeometryContext& gctx,
0090 std::vector<Volume*>& volumes,
0091 AxisDirection direction,
0092 VolumeAttachmentStrategy strategy,
0093 VolumeResizeStrategy resizeStrategy,
0094 const Logger& logger)
0095 : CylinderVolumeStack{
0096 gctx, volumes, direction, strategy, {resizeStrategy, resizeStrategy},
0097 logger} {}
0098
0099 CylinderVolumeStack::CylinderVolumeStack(
0100 const GeometryContext& gctx, std::vector<Volume*>& volumes,
0101 AxisDirection direction, VolumeAttachmentStrategy strategy,
0102 std::pair<VolumeResizeStrategy, VolumeResizeStrategy> resizeStrategies,
0103 const Logger& logger)
0104 : VolumeStack(volumes, direction,
0105 {resizeStrategies.first, resizeStrategies.second}) {
0106 initializeOuterVolume(gctx, direction, strategy, logger);
0107 }
0108
0109 void CylinderVolumeStack::initializeOuterVolume(
0110 const GeometryContext& gctx, AxisDirection direction,
0111 VolumeAttachmentStrategy strategy, const Logger& logger) {
0112 ACTS_DEBUG("Creating CylinderVolumeStack from "
0113 << m_volumes.size() << " volumes in direction "
0114 << axisDirectionName(direction));
0115 if (m_volumes.empty()) {
0116 throw std::invalid_argument(
0117 "CylinderVolumeStack requires at least one volume");
0118 }
0119
0120 if (direction != Acts::AxisDirection::AxisZ &&
0121 direction != Acts::AxisDirection::AxisR) {
0122 throw std::invalid_argument(axisDirectionName(direction) +
0123 " is not supported ");
0124 }
0125
0126
0127 m_groupTransform = m_volumes.front()->localToGlobalTransform(gctx);
0128 ACTS_VERBOSE("Initial group transform is:\n" << m_groupTransform.matrix());
0129
0130 std::vector<VolumeTuple> volumeTuples;
0131 volumeTuples.reserve(m_volumes.size());
0132
0133 for (const auto& volume : m_volumes) {
0134 const auto* cylinderBounds =
0135 dynamic_cast<const CylinderVolumeBounds*>(&volume->volumeBounds());
0136 if (cylinderBounds == nullptr) {
0137 throw std::invalid_argument{
0138 "CylinderVolumeStack requires all volumes to "
0139 "have CylinderVolumeBounds"};
0140 }
0141
0142 checkNoPhiOrBevel(*cylinderBounds, logger);
0143
0144 volumeTuples.emplace_back(gctx, *volume, m_groupTransform);
0145 }
0146
0147 ACTS_DEBUG("*** Initial volume configuration:");
0148 printVolumeSequence(volumeTuples, logger, Acts::Logging::DEBUG);
0149
0150 if (m_volumes.size() == 1) {
0151 ACTS_VERBOSE("Only one volume, returning");
0152 setTransform(m_volumes.front()->localToGlobalTransform(gctx));
0153 const auto* cylBounds = dynamic_cast<const CylinderVolumeBounds*>(
0154 &m_volumes.front()->volumeBounds());
0155 assert(cylBounds != nullptr && "Volume bounds are not cylinder bounds");
0156 Volume::update(gctx, std::make_shared<CylinderVolumeBounds>(*cylBounds),
0157 std::nullopt, logger);
0158 ACTS_VERBOSE(
0159 "Transform is now: " << toString(localToGlobalTransform(gctx)));
0160 return;
0161 }
0162
0163 ACTS_VERBOSE("Checking volume alignment");
0164 checkVolumeAlignment(volumeTuples, logger);
0165
0166 if (direction == Acts::AxisDirection::AxisZ) {
0167 ACTS_VERBOSE("Sorting by volume z position");
0168 std::ranges::sort(volumeTuples, {}, [](const auto& v) {
0169 return v.localTransform.translation()[eZ];
0170 });
0171
0172 ACTS_VERBOSE("Checking for overlaps and attaching volumes in z");
0173 std::vector<VolumeTuple> gapVolumes =
0174 checkOverlapAndAttachInZ(gctx, volumeTuples, strategy, logger);
0175
0176 ACTS_VERBOSE("Appending "
0177 << gapVolumes.size()
0178 << " gap volumes to the end of the volume vector");
0179 std::copy(gapVolumes.begin(), gapVolumes.end(),
0180 std::back_inserter(volumeTuples));
0181
0182 ACTS_VERBOSE("*** Volume configuration after z attachment:");
0183 printVolumeSequence(volumeTuples, logger, Acts::Logging::VERBOSE);
0184
0185 ACTS_VERBOSE("Synchronizing bounds in r");
0186 const auto [minR, maxR] = synchronizeRBounds(volumeTuples, logger);
0187
0188 for (auto& vt : volumeTuples) {
0189 ACTS_VERBOSE("Updated bounds for volume at z: "
0190 << vt.localTransform.translation()[eZ]);
0191 ACTS_VERBOSE(*vt.updatedBounds);
0192
0193 vt.commit(gctx, logger);
0194 }
0195
0196 ACTS_VERBOSE("*** Volume configuration after r synchronization:");
0197 printVolumeSequence(volumeTuples, logger, Acts::Logging::VERBOSE);
0198
0199 std::ranges::sort(volumeTuples, {}, [](const auto& v) { return v.midZ(); });
0200
0201 m_volumes.clear();
0202 for (const auto& vt : volumeTuples) {
0203 m_volumes.push_back(vt.volume);
0204 }
0205
0206 ACTS_DEBUG("*** Volume configuration after final z sorting:");
0207 printVolumeSequence(volumeTuples, logger, Acts::Logging::DEBUG);
0208
0209 double minZ = volumeTuples.front().minZ();
0210 double maxZ = volumeTuples.back().maxZ();
0211
0212 double midZ = (minZ + maxZ) / 2.0;
0213 double hlZ = (maxZ - minZ) / 2.0;
0214
0215 Volume::update(gctx,
0216 std::make_shared<CylinderVolumeBounds>(minR, maxR, hlZ),
0217 m_groupTransform * Translation3{0, 0, midZ}, logger);
0218 ACTS_DEBUG("Outer bounds are:\n" << volumeBounds());
0219 ACTS_DEBUG("Outer transform / new group transform is:\n"
0220 << toString(localToGlobalTransform(gctx)));
0221
0222
0223
0224 m_groupTransform = localToGlobalTransform(gctx);
0225
0226 } else if (direction == Acts::AxisDirection::AxisR) {
0227 ACTS_VERBOSE("Sorting by volume r middle point");
0228 std::ranges::sort(volumeTuples, {}, [](const auto& v) { return v.midR(); });
0229
0230 ACTS_VERBOSE("Checking for overlaps and attaching volumes in r");
0231 std::vector<VolumeTuple> gapVolumes =
0232 checkOverlapAndAttachInR(gctx, volumeTuples, strategy, logger);
0233
0234 ACTS_VERBOSE("Appending "
0235 << gapVolumes.size()
0236 << " gap volumes to the end of the volume vector");
0237 std::copy(gapVolumes.begin(), gapVolumes.end(),
0238 std::back_inserter(volumeTuples));
0239
0240 ACTS_VERBOSE("*** Volume configuration after r attachment:");
0241 printVolumeSequence(volumeTuples, logger, Acts::Logging::VERBOSE);
0242
0243 ACTS_VERBOSE("Synchronizing bounds in z");
0244 const auto [minZ, maxZ] = synchronizeZBounds(volumeTuples, logger);
0245
0246 for (auto& vt : volumeTuples) {
0247 ACTS_VERBOSE("Updated bounds for volume at r: " << vt.midR());
0248 ACTS_VERBOSE(*vt.updatedBounds);
0249 vt.commit(gctx, logger);
0250 }
0251
0252 ACTS_VERBOSE("*** Volume configuration after z synchronization:");
0253 printVolumeSequence(volumeTuples, logger, Acts::Logging::VERBOSE);
0254
0255 std::ranges::sort(volumeTuples, {}, [](const auto& v) { return v.midR(); });
0256
0257 m_volumes.clear();
0258 for (const auto& vt : volumeTuples) {
0259 m_volumes.push_back(vt.volume);
0260 }
0261
0262 ACTS_DEBUG("*** Volume configuration after final r sorting:");
0263 printVolumeSequence(volumeTuples, logger, Acts::Logging::DEBUG);
0264
0265 double minR = volumeTuples.front().minR();
0266 double maxR = volumeTuples.back().maxR();
0267
0268 double midZ = (minZ + maxZ) / 2.0;
0269 double hlZ = (maxZ - minZ) / 2.0;
0270
0271 Volume::update(gctx,
0272 std::make_shared<CylinderVolumeBounds>(minR, maxR, hlZ),
0273 m_groupTransform * Translation3{0, 0, midZ}, logger);
0274
0275 ACTS_DEBUG("Outer bounds are:\n" << volumeBounds());
0276 ACTS_DEBUG("Outer transform is:\n"
0277 << toString(localToGlobalTransform(gctx)));
0278
0279
0280
0281 m_groupTransform = localToGlobalTransform(gctx);
0282
0283 } else {
0284 ACTS_ERROR("Binning in " << axisDirectionName(direction)
0285 << " is not supported");
0286 throw std::invalid_argument(axisDirectionName(direction) +
0287 " is not supported ");
0288 }
0289 }
0290
0291 void CylinderVolumeStack::overlapPrint(
0292 AxisDirection direction, const CylinderVolumeStack::VolumeTuple& a,
0293 const CylinderVolumeStack::VolumeTuple& b, const Logger& logger) {
0294 if (logger().doPrint(Acts::Logging::DEBUG)) {
0295 std::stringstream ss;
0296 ss << std::fixed;
0297 ss << std::setprecision(3);
0298 ss << std::setfill(' ');
0299
0300 int w = 9;
0301
0302 ACTS_VERBOSE("Checking overlap between");
0303 if (direction == AxisDirection::AxisZ) {
0304 ss << " - " << " z: [ " << std::setw(w) << a.minZ() << " <- "
0305 << std::setw(w) << a.midZ() << " -> " << std::setw(w) << a.maxZ()
0306 << " ]";
0307 ACTS_VERBOSE(ss.str());
0308
0309 ss.str("");
0310 ss << " - " << " z: [ " << std::setw(w) << b.minZ() << " <- "
0311 << std::setw(w) << b.midZ() << " -> " << std::setw(w) << b.maxZ()
0312 << " ]";
0313 ACTS_VERBOSE(ss.str());
0314 } else {
0315 ss << " - " << " r: [ " << std::setw(w) << a.minR() << " <-> "
0316 << std::setw(w) << a.maxR() << " ]";
0317 ACTS_VERBOSE(ss.str());
0318
0319 ss.str("");
0320 ss << " - " << " r: [ " << std::setw(w) << b.minR() << " <-> "
0321 << std::setw(w) << b.maxR() << " ]";
0322 ACTS_VERBOSE(ss.str());
0323 }
0324 }
0325 }
0326
0327 std::vector<CylinderVolumeStack::VolumeTuple>
0328 CylinderVolumeStack::checkOverlapAndAttachInZ(const GeometryContext& gctx,
0329 std::vector<VolumeTuple>& volumes,
0330 VolumeAttachmentStrategy strategy,
0331 const Logger& logger) {
0332
0333 std::vector<VolumeTuple> gapVolumes;
0334 for (std::size_t i = 0; i < volumes.size() - 1; i++) {
0335 std::size_t j = i + 1;
0336 auto& a = volumes.at(i);
0337 auto& b = volumes.at(j);
0338
0339 overlapPrint(AxisDirection::AxisZ, a, b, logger);
0340
0341 if (a.maxZ() > b.minZ()) {
0342 ACTS_ERROR(" -> Overlap in z");
0343 throw std::invalid_argument("Volumes overlap in z");
0344 } else {
0345 ACTS_VERBOSE(" -> No overlap");
0346 }
0347
0348 constexpr auto tolerance = s_onSurfaceTolerance;
0349 if (std::abs(a.maxZ() - b.minZ()) < tolerance) {
0350 ACTS_VERBOSE("No gap between volumes, no attachment needed");
0351 } else {
0352 double gapWidth = b.minZ() - a.maxZ();
0353 ACTS_VERBOSE("Gap width: " << gapWidth);
0354
0355 ACTS_VERBOSE("Synchronizing bounds in z with strategy: " << strategy);
0356 switch (strategy) {
0357 case VolumeAttachmentStrategy::Midpoint: {
0358 ACTS_VERBOSE(" -> Strategy: Expand both volumes to midpoint");
0359
0360 double aZMidNew = (a.minZ() + a.maxZ()) / 2.0 + gapWidth / 4.0;
0361 double aHlZNew = a.halfLengthZ() + gapWidth / 4.0;
0362 ACTS_VERBOSE(" - New halflength for first volume: " << aHlZNew);
0363 ACTS_VERBOSE(" - New bounds for first volume: ["
0364 << (aZMidNew - aHlZNew) << " <- " << aZMidNew << " -> "
0365 << (aZMidNew + aHlZNew) << "]");
0366
0367 assert(std::abs(a.minZ() - (aZMidNew - aHlZNew)) < 1e-9 &&
0368 "Volume shrunk");
0369 assert(aHlZNew >= a.halfLengthZ() && "Volume shrunk");
0370
0371 double bZMidNew = (b.minZ() + b.maxZ()) / 2.0 - gapWidth / 4.0;
0372 double bHlZNew = b.halfLengthZ() + gapWidth / 4.0;
0373 ACTS_VERBOSE(" - New halflength for second volume: " << bHlZNew);
0374 ACTS_VERBOSE(" - New bounds for second volume: ["
0375 << (bZMidNew - bHlZNew) << " <- " << bZMidNew << " -> "
0376 << (bZMidNew + bHlZNew) << "]");
0377
0378 assert(bHlZNew >= b.halfLengthZ() && "Volume shrunk");
0379 assert(std::abs(b.maxZ() - (bZMidNew + bHlZNew)) < 1e-9 &&
0380 "Volume shrunk");
0381
0382 a.setLocalTransform(Transform3{Translation3{0, 0, aZMidNew}},
0383 m_groupTransform);
0384 a.updatedBounds->set(CylinderVolumeBounds::eHalfLengthZ, aHlZNew);
0385
0386 b.setLocalTransform(Transform3{Translation3{0, 0, bZMidNew}},
0387 m_groupTransform);
0388 b.updatedBounds->set(CylinderVolumeBounds::eHalfLengthZ, bHlZNew);
0389
0390 break;
0391 }
0392 case VolumeAttachmentStrategy::First: {
0393 ACTS_VERBOSE(" -> Strategy: Expand first volume");
0394 double aZMidNew = (a.minZ() + b.minZ()) / 2.0;
0395 double aHlZNew = (b.minZ() - a.minZ()) / 2.0;
0396 ACTS_VERBOSE(" - Gap width: " << gapWidth);
0397 ACTS_VERBOSE(" - New bounds for first volume: ["
0398 << (aZMidNew - aHlZNew) << " <- " << aZMidNew << " -> "
0399 << (aZMidNew + aHlZNew) << "]");
0400
0401 assert(std::abs(a.minZ() - (aZMidNew - aHlZNew)) < 1e-9 &&
0402 "Volume shrunk");
0403 assert(aHlZNew >= a.halfLengthZ() && "Volume shrunk");
0404
0405 a.setLocalTransform(Transform3{Translation3{0, 0, aZMidNew}},
0406 m_groupTransform);
0407 a.updatedBounds->set(CylinderVolumeBounds::eHalfLengthZ, aHlZNew);
0408
0409 break;
0410 }
0411 case VolumeAttachmentStrategy::Second: {
0412 ACTS_VERBOSE(" -> Strategy: Expand second volume");
0413 double bZMidNew = (a.maxZ() + b.maxZ()) / 2.0;
0414 double bHlZNew = (b.maxZ() - a.maxZ()) / 2.0;
0415 ACTS_VERBOSE(" - New halflength for second volume: " << bHlZNew);
0416 ACTS_VERBOSE(" - New bounds for second volume: ["
0417 << (bZMidNew - bHlZNew) << " <- " << bZMidNew << " -> "
0418 << (bZMidNew + bHlZNew) << "]");
0419
0420 assert(bHlZNew >= b.halfLengthZ() && "Volume shrunk");
0421 assert(std::abs(b.maxZ() - (bZMidNew + bHlZNew)) < 1e-9 &&
0422 "Volume shrunk");
0423
0424 b.setLocalTransform(Transform3{Translation3{0, 0, bZMidNew}},
0425 m_groupTransform);
0426 b.updatedBounds->set(CylinderVolumeBounds::eHalfLengthZ, bHlZNew);
0427 break;
0428 }
0429 case VolumeAttachmentStrategy::Gap: {
0430 ACTS_VERBOSE(" -> Strategy: Create a gap volume");
0431 double gapHlZ = (b.minZ() - a.maxZ()) / 2.0;
0432 double gapMidZ = (b.minZ() + a.maxZ()) / 2.0;
0433
0434 ACTS_VERBOSE(" - Gap half length: " << gapHlZ
0435 << " at z: " << gapMidZ);
0436
0437 double minR = std::min(a.minR(), b.minR());
0438 double maxR = std::max(a.maxR(), b.maxR());
0439
0440 Transform3 gapLocalTransform{Translation3{0, 0, gapMidZ}};
0441 Transform3 gapGlobalTransform = m_groupTransform * gapLocalTransform;
0442 auto gapBounds =
0443 std::make_shared<CylinderVolumeBounds>(minR, maxR, gapHlZ);
0444
0445 auto gap = addGapVolume(gapGlobalTransform, gapBounds);
0446 gapVolumes.emplace_back(gctx, *gap, m_groupTransform);
0447
0448 break;
0449 }
0450 default:
0451 ACTS_ERROR("Attachment strategy " << strategy << " not implemented");
0452 std::stringstream ss;
0453 ss << strategy;
0454 throw std::invalid_argument("Attachment strategy " + ss.str() +
0455 " not implemented");
0456 }
0457 }
0458 }
0459
0460 return gapVolumes;
0461 }
0462
0463 std::vector<CylinderVolumeStack::VolumeTuple>
0464 CylinderVolumeStack::checkOverlapAndAttachInR(const GeometryContext& gctx,
0465 std::vector<VolumeTuple>& volumes,
0466 VolumeAttachmentStrategy strategy,
0467 const Logger& logger) {
0468 std::vector<VolumeTuple> gapVolumes;
0469 for (std::size_t i = 0; i < volumes.size() - 1; i++) {
0470 std::size_t j = i + 1;
0471 auto& a = volumes.at(i);
0472 auto& b = volumes.at(j);
0473
0474 overlapPrint(AxisDirection::AxisR, a, b, logger);
0475
0476 if (a.maxR() > b.minR()) {
0477 ACTS_ERROR(" -> Overlap in r");
0478 throw std::invalid_argument("Volumes overlap in r");
0479 } else {
0480 ACTS_VERBOSE(" -> No overlap");
0481 }
0482
0483 constexpr auto tolerance = s_onSurfaceTolerance;
0484 if (std::abs(a.maxR() - b.minR()) < tolerance) {
0485 ACTS_VERBOSE("No gap between volumes, no attachment needed");
0486 } else {
0487 double gapWidth = b.minR() - a.maxR();
0488 ACTS_VERBOSE("Gap width: " << gapWidth);
0489
0490 ACTS_VERBOSE("Synchronizing bounds in r with strategy: " << strategy);
0491 switch (strategy) {
0492 case VolumeAttachmentStrategy::Midpoint: {
0493 ACTS_VERBOSE(" -> Strategy: Expand both volumes to midpoint");
0494
0495 a.set({{CylinderVolumeBounds::eMaxR, a.maxR() + gapWidth / 2.0}});
0496 b.set({{CylinderVolumeBounds::eMinR, b.minR() - gapWidth / 2.0}});
0497
0498 break;
0499 }
0500 case VolumeAttachmentStrategy::First: {
0501 ACTS_VERBOSE(" -> Strategy: Expand first volume");
0502
0503 a.set({{CylinderVolumeBounds::eMaxR, b.minR()}});
0504
0505 break;
0506 }
0507 case VolumeAttachmentStrategy::Second: {
0508 ACTS_VERBOSE(" -> Strategy: Expand second volume");
0509
0510 b.set({{CylinderVolumeBounds::eMinR, a.maxR()}});
0511
0512 break;
0513 }
0514 case VolumeAttachmentStrategy::Gap: {
0515 ACTS_VERBOSE(" -> Strategy: Create a gap volume");
0516
0517 auto gapBounds = std::make_shared<CylinderVolumeBounds>(
0518 a.maxR(), b.minR(), a.halfLengthZ());
0519 auto gap = addGapVolume(m_groupTransform, gapBounds);
0520
0521 gapVolumes.emplace_back(gctx, *gap, m_groupTransform);
0522 break;
0523 }
0524 default:
0525 ACTS_ERROR("Attachment strategy " << strategy << " not implemented");
0526 std::stringstream ss;
0527 ss << strategy;
0528 throw std::invalid_argument("Attachment strategy " + ss.str() +
0529 " not implemented");
0530 }
0531 }
0532 }
0533
0534 return gapVolumes;
0535 }
0536
0537 void CylinderVolumeStack::printVolumeSequence(
0538 const std::vector<VolumeTuple>& volumes, const Logger& logger,
0539 Acts::Logging::Level lvl) {
0540 if (!logger().doPrint(lvl)) {
0541 return;
0542 }
0543 for (const auto& vt : volumes) {
0544 std::stringstream ss;
0545 ss << std::fixed;
0546 ss << std::setprecision(3);
0547 ss << std::setfill(' ');
0548
0549 int w = 9;
0550 ss << "z: [ " << std::setw(w) << vt.minZ() << " <- " << std::setw(w)
0551 << vt.midZ() << " -> " << std::setw(w) << vt.maxZ() << " ], r: [ "
0552 << std::setw(w) << vt.minR() << " <-> " << std::setw(w) << vt.maxR()
0553 << " ]";
0554
0555 logger().log(lvl, ss.str());
0556 }
0557 }
0558
0559 void CylinderVolumeStack::checkVolumeAlignment(
0560 const std::vector<VolumeTuple>& volumes, const Logger& logger) {
0561 std::size_t n = 0;
0562 for (auto& vt : volumes) {
0563 ACTS_VERBOSE("Checking volume #"
0564 << n << " at z: " << vt.localTransform.translation()[eZ]);
0565 ACTS_VERBOSE("- Local transform is:\n" << vt.localTransform.matrix());
0566
0567
0568 constexpr auto tolerance = s_onSurfaceTolerance;
0569
0570
0571
0572
0573 if (std::abs(vt.localTransform.rotation().col(eX)[eZ]) >= tolerance ||
0574 std::abs(vt.localTransform.rotation().col(eY)[eZ]) >= tolerance) {
0575 ACTS_ERROR("Volumes are not aligned: rotation is different");
0576 throw std::invalid_argument(
0577 "Volumes are not aligned: rotation is different");
0578 }
0579
0580 ACTS_VERBOSE(" -> Rotation is ok!");
0581
0582
0583 Vector2 translation = vt.localTransform.translation().head<2>();
0584 if (std::abs(translation[0]) > tolerance ||
0585 std::abs(translation[1]) > tolerance) {
0586 ACTS_ERROR("Volumes are not aligned: translation in x or y");
0587 throw std::invalid_argument(
0588 "Volumes are not aligned: translation in x or y");
0589 }
0590 ACTS_VERBOSE(" -> Translation in x/y is ok!");
0591
0592 n++;
0593 }
0594 }
0595
0596 std::pair<double, double> CylinderVolumeStack::synchronizeRBounds(
0597 std::vector<VolumeTuple>& volumes, const Logger& logger) {
0598 const double minR =
0599 std::min_element(volumes.begin(), volumes.end(),
0600 [](const auto& a, const auto& b) {
0601 return a.bounds->get(CylinderVolumeBounds::eMinR) <
0602 b.bounds->get(CylinderVolumeBounds::eMinR);
0603 })
0604 ->bounds->get(CylinderVolumeBounds::eMinR);
0605
0606 const double maxR =
0607 std::max_element(volumes.begin(), volumes.end(),
0608 [](const auto& a, const auto& b) {
0609 return a.bounds->get(CylinderVolumeBounds::eMaxR) <
0610 b.bounds->get(CylinderVolumeBounds::eMaxR);
0611 })
0612 ->bounds->get(CylinderVolumeBounds::eMaxR);
0613 ACTS_VERBOSE("Found: minR: " << minR << " maxR: " << maxR);
0614
0615 for (auto& vt : volumes) {
0616 vt.set({
0617 {CylinderVolumeBounds::eMinR, minR},
0618 {CylinderVolumeBounds::eMaxR, maxR},
0619 });
0620 }
0621
0622 return {minR, maxR};
0623 }
0624
0625 std::pair<double, double> CylinderVolumeStack::synchronizeZBounds(
0626 std::vector<VolumeTuple>& volumes, const Logger& logger) {
0627 const double minZ = std::min_element(volumes.begin(), volumes.end(),
0628 [](const auto& a, const auto& b) {
0629 return a.minZ() < b.minZ();
0630 })
0631 ->minZ();
0632
0633 const double maxZ = std::max_element(volumes.begin(), volumes.end(),
0634 [](const auto& a, const auto& b) {
0635 return a.maxZ() < b.maxZ();
0636 })
0637 ->maxZ();
0638 const double midZ = (minZ + maxZ) / 2.0;
0639 const double hlZ = (maxZ - minZ) / 2.0;
0640 ACTS_DEBUG("Found overall z bounds: [ " << minZ << " <- " << midZ << " -> "
0641 << maxZ << " ]");
0642 const Transform3 transform{Translation3{0, 0, midZ}};
0643
0644 for (auto& vt : volumes) {
0645 vt.set({{CylinderVolumeBounds::eHalfLengthZ, hlZ}});
0646 vt.setLocalTransform(transform, m_groupTransform);
0647 }
0648
0649 return {minZ, maxZ};
0650 }
0651
0652 void CylinderVolumeStack::update(const GeometryContext& gctx,
0653 std::shared_ptr<VolumeBounds> volbounds,
0654 std::optional<Transform3> transform,
0655 const Logger& logger) {
0656 ACTS_DEBUG(
0657 "Resizing CylinderVolumeStack with strategy: " << m_resizeStrategies);
0658 ACTS_DEBUG("Currently have " << m_volumes.size() << " children");
0659 ACTS_DEBUG(m_gaps.size() << " gaps");
0660 for (const auto& v : m_volumes) {
0661 ACTS_DEBUG(" - volume bounds: \n" << v->volumeBounds());
0662 ACTS_DEBUG(" transform: \n"
0663 << v->localToGlobalTransform(gctx).matrix());
0664 }
0665
0666 ACTS_DEBUG("New bounds are: \n" << *volbounds);
0667
0668 auto cylBounds = std::dynamic_pointer_cast<CylinderVolumeBounds>(volbounds);
0669 if (cylBounds == nullptr) {
0670 throw std::invalid_argument(
0671 "CylinderVolumeStack requires CylinderVolumeBounds");
0672 }
0673
0674 if (cylBounds == nullptr) {
0675 throw std::invalid_argument("New bounds are nullptr");
0676 }
0677
0678 if (*cylBounds == volumeBounds()) {
0679 ACTS_VERBOSE("Bounds are the same, no resize needed");
0680 return;
0681 }
0682
0683 ACTS_VERBOSE("Group transform is:\n" << toString(m_groupTransform));
0684 ACTS_VERBOSE("Current transform is:\n"
0685 << toString(localToGlobalTransform(gctx)));
0686 if (transform.has_value()) {
0687 ACTS_VERBOSE("Input transform:\n" << toString(transform.value()));
0688 }
0689
0690 VolumeTuple oldVolume{gctx, *this, localToGlobalTransform(gctx)};
0691 VolumeTuple newVolume{gctx, *this, localToGlobalTransform(gctx)};
0692 newVolume.updatedBounds = std::make_shared<CylinderVolumeBounds>(*cylBounds);
0693 newVolume.globalTransform = transform.value_or(localToGlobalTransform(gctx));
0694 newVolume.localTransform =
0695 globalToLocalTransform(gctx) * newVolume.globalTransform;
0696
0697 if (!transform.has_value()) {
0698 ACTS_VERBOSE("Local transform does not change");
0699 } else {
0700 ACTS_VERBOSE("Local transform changes from\n"
0701 << m_groupTransform.matrix() << "\nto\n"
0702 << newVolume.localTransform.matrix());
0703 ACTS_VERBOSE("Checking transform consistency");
0704
0705 std::vector<VolumeTuple> volTemp{newVolume};
0706 checkVolumeAlignment(volTemp, logger);
0707 }
0708
0709 checkNoPhiOrBevel(*cylBounds, logger);
0710
0711 const double newMinR = newVolume.minR();
0712 const double newMaxR = newVolume.maxR();
0713 const double newMinZ = newVolume.minZ();
0714 const double newMaxZ = newVolume.maxZ();
0715 const double newMidZ = newVolume.midZ();
0716 const double newHlZ = newVolume.halfLengthZ();
0717
0718 const double oldMinR = oldVolume.minR();
0719 const double oldMaxR = oldVolume.maxR();
0720 const double oldMinZ = oldVolume.minZ();
0721 const double oldMaxZ = oldVolume.maxZ();
0722 const double oldMidZ = oldVolume.midZ();
0723 const double oldHlZ = oldVolume.halfLengthZ();
0724
0725 ACTS_VERBOSE("Previous bounds are: z: [ "
0726 << oldMinZ << " <- " << oldMidZ << " -> " << oldMaxZ << " ] ("
0727 << oldHlZ << "), r: [ " << oldMinR << " <-> " << oldMaxR
0728 << " ]");
0729 ACTS_VERBOSE("New bounds are: z: [ "
0730 << newMinZ << " <- " << newMidZ << " -> " << newMaxZ << " ] ("
0731 << newHlZ << "), r: [ " << newMinR << " <-> " << newMaxR
0732 << " ]");
0733
0734 constexpr auto tolerance = s_onSurfaceTolerance;
0735 auto same = [](double a, double b) { return std::abs(a - b) < tolerance; };
0736
0737 if (!same(newMinZ, oldMinZ) && newMinZ > oldMinZ) {
0738 ACTS_ERROR("Shrinking the stack size in z is not supported: "
0739 << newMinZ << " -> " << oldMinZ);
0740 throw std::invalid_argument("Shrinking the stack in z is not supported");
0741 }
0742
0743 if (!same(newMaxZ, oldMaxZ) && newMaxZ < oldMaxZ) {
0744 ACTS_ERROR("Shrinking the stack size in z is not supported: "
0745 << newMaxZ << " -> " << oldMaxZ);
0746 throw std::invalid_argument("Shrinking the stack in z is not supported");
0747 }
0748
0749 if (!same(newMinR, oldMinR) && newMinR > oldMinR) {
0750 ACTS_ERROR("Shrinking the stack size in r is not supported: "
0751 << newMinR << " -> " << oldMinR);
0752 throw std::invalid_argument("Shrinking the stack in r is not supported");
0753 }
0754
0755 if (!same(newMaxR, oldMaxR) && newMaxR < oldMaxR) {
0756 ACTS_ERROR("Shrinking the stack size in r is not supported: "
0757 << newMaxR << " -> " << oldMaxR);
0758 throw std::invalid_argument("Shrinking the stack is r in not supported");
0759 }
0760
0761 auto isGap = [this](const Volume* vol) {
0762 return std::ranges::any_of(
0763 m_gaps, [&](const auto& gap) { return vol == gap.get(); });
0764 };
0765
0766 const auto& [firstStrategy, secondStrategy] = m_resizeStrategies;
0767
0768 if (m_direction == AxisDirection::AxisZ) {
0769 ACTS_VERBOSE("Stack direction is z");
0770
0771 std::vector<VolumeTuple> volumeTuples;
0772 volumeTuples.reserve(m_volumes.size());
0773 std::ranges::transform(m_volumes, std::back_inserter(volumeTuples),
0774 [this, &gctx](const auto& volume) {
0775 return VolumeTuple{gctx, *volume,
0776 m_groupTransform};
0777 });
0778
0779 ACTS_VERBOSE("*** Initial volume configuration:");
0780 printVolumeSequence(volumeTuples, logger, Acts::Logging::DEBUG);
0781
0782 if (!same(newMinR, oldMinR) || !same(newMaxR, oldMaxR)) {
0783 ACTS_VERBOSE("Resize all volumes to new r bounds");
0784 for (auto& volume : volumeTuples) {
0785 volume.set({
0786 {CylinderVolumeBounds::eMinR, newMinR},
0787 {CylinderVolumeBounds::eMaxR, newMaxR},
0788 });
0789 }
0790 ACTS_VERBOSE("*** Volume configuration after r resizing:");
0791 printVolumeSequence(volumeTuples, logger, Acts::Logging::DEBUG);
0792 } else {
0793 ACTS_VERBOSE("R bounds are the same, no r resize needed");
0794 }
0795
0796 auto printGapDimensions = [&](const VolumeTuple& gap,
0797 const std::string& prefix = "") {
0798 ACTS_VERBOSE(" -> gap" << prefix << ": [ " << gap.minZ() << " <- "
0799 << gap.midZ() << " -> " << gap.maxZ()
0800 << " ], r: [ " << gap.minR() << " <-> "
0801 << gap.maxR() << " ]");
0802 };
0803
0804 if (same(newHlZ, oldHlZ)) {
0805 ACTS_VERBOSE("Halflength z is the same, no z resize needed");
0806 } else {
0807 if (newMinZ < oldMinZ) {
0808 if (firstStrategy == VolumeResizeStrategy::Expand) {
0809 ACTS_VERBOSE("Expanding first volume to new z bounds");
0810
0811 auto& first = volumeTuples.front();
0812 double newMinZFirst = newVolume.minZ();
0813 double newMidZFirst = (newMinZFirst + first.maxZ()) / 2.0;
0814 double newHlZFirst = (first.maxZ() - newMinZFirst) / 2.0;
0815
0816 ACTS_VERBOSE(" -> first z: [ "
0817 << newMinZFirst << " <- " << newMidZFirst << " -> "
0818 << first.maxZ() << " ] (hl: " << newHlZFirst << ")");
0819
0820 first.set({{CylinderVolumeBounds::eHalfLengthZ, newHlZFirst}});
0821 first.setLocalTransform(Transform3{Translation3{0, 0, newMidZFirst}},
0822 m_groupTransform);
0823 } else if (firstStrategy == VolumeResizeStrategy::Gap) {
0824 ACTS_VERBOSE("Creating gap volumes to fill the new z bounds at minZ");
0825
0826 double gap1MinZ = newVolume.minZ();
0827 double gap1MaxZ = oldVolume.minZ();
0828 double gap1HlZ = (gap1MaxZ - gap1MinZ) / 2.0;
0829 double gap1PZ = (gap1MaxZ + gap1MinZ) / 2.0;
0830
0831
0832 auto& candidate = volumeTuples.front();
0833 if (isGap(candidate.volume)) {
0834 ACTS_VERBOSE("~> Reusing existing gap volume at negative z");
0835
0836 gap1HlZ =
0837 candidate.bounds->get(CylinderVolumeBounds::eHalfLengthZ) +
0838 gap1HlZ;
0839 gap1MaxZ = gap1MinZ + gap1HlZ * 2;
0840 gap1PZ = (gap1MaxZ + gap1MinZ) / 2.0;
0841
0842 printGapDimensions(candidate, " before");
0843 auto gap1Bounds = std::make_shared<CylinderVolumeBounds>(
0844 newMinR, newMaxR, gap1HlZ);
0845 auto gap1Transform = m_groupTransform * Translation3{0, 0, gap1PZ};
0846 candidate.volume->update(gctx, std::move(gap1Bounds),
0847 gap1Transform);
0848 candidate = VolumeTuple{gctx, *candidate.volume, m_groupTransform};
0849 ACTS_VERBOSE("After:");
0850 printGapDimensions(candidate, " after ");
0851
0852 } else {
0853 ACTS_VERBOSE("~> Creating new gap volume at negative z");
0854 auto gap1Bounds = std::make_shared<CylinderVolumeBounds>(
0855 newMinR, newMaxR, gap1HlZ);
0856 auto gap1Transform = m_groupTransform * Translation3{0, 0, gap1PZ};
0857 auto gap1 = addGapVolume(gap1Transform, std::move(gap1Bounds));
0858 volumeTuples.insert(volumeTuples.begin(),
0859 VolumeTuple{gctx, *gap1, m_groupTransform});
0860 printGapDimensions(volumeTuples.front());
0861 }
0862 }
0863 }
0864
0865 if (newMaxZ > oldMaxZ) {
0866 if (secondStrategy == VolumeResizeStrategy::Expand) {
0867 ACTS_VERBOSE("Expanding last volume to new z bounds");
0868
0869 auto& last = volumeTuples.back();
0870 double newMaxZLast = newVolume.maxZ();
0871 double newMidZLast = (last.minZ() + newMaxZLast) / 2.0;
0872 double newHlZLast = (newMaxZLast - last.minZ()) / 2.0;
0873
0874 ACTS_VERBOSE(" -> last z: [ " << last.minZ() << " <- " << newMidZLast
0875 << " -> " << newMaxZLast
0876 << " ] (hl: " << newHlZLast << ")");
0877
0878 last.set({{CylinderVolumeBounds::eHalfLengthZ, newHlZLast}});
0879 last.setLocalTransform(Transform3{Translation3{0, 0, newMidZLast}},
0880 m_groupTransform);
0881 } else if (secondStrategy == VolumeResizeStrategy::Gap) {
0882 ACTS_VERBOSE("Creating gap volumes to fill the new z bounds at maxZ");
0883
0884 double gap2MinZ = oldVolume.maxZ();
0885 double gap2MaxZ = newVolume.maxZ();
0886 double gap2HlZ = (gap2MaxZ - gap2MinZ) / 2.0;
0887 double gap2PZ = (gap2MaxZ + gap2MinZ) / 2.0;
0888
0889
0890 auto& candidate = volumeTuples.back();
0891 if (isGap(candidate.volume)) {
0892 ACTS_VERBOSE("~> Reusing existing gap volume at positive z");
0893
0894 gap2HlZ =
0895 candidate.bounds->get(CylinderVolumeBounds::eHalfLengthZ) +
0896 gap2HlZ;
0897 gap2MinZ = newVolume.maxZ() - gap2HlZ * 2;
0898 gap2PZ = (gap2MaxZ + gap2MinZ) / 2.0;
0899
0900 printGapDimensions(candidate, " before");
0901 auto gap2Bounds = std::make_shared<CylinderVolumeBounds>(
0902 newMinR, newMaxR, gap2HlZ);
0903 auto gap2Transform = m_groupTransform * Translation3{0, 0, gap2PZ};
0904
0905 candidate.volume->update(gctx, std::move(gap2Bounds),
0906 gap2Transform);
0907 candidate = VolumeTuple{gctx, *candidate.volume, m_groupTransform};
0908 printGapDimensions(candidate, " after ");
0909 } else {
0910 ACTS_VERBOSE("~> Creating new gap volume at positive z");
0911 auto gap2Bounds = std::make_shared<CylinderVolumeBounds>(
0912 newMinR, newMaxR, gap2HlZ);
0913 auto gap2Transform = m_groupTransform * Translation3{0, 0, gap2PZ};
0914 auto gap2 = addGapVolume(gap2Transform, std::move(gap2Bounds));
0915 volumeTuples.emplace_back(gctx, *gap2, m_groupTransform);
0916 printGapDimensions(volumeTuples.back());
0917 }
0918 }
0919 }
0920
0921 ACTS_VERBOSE("*** Volume configuration after z resizing:");
0922 printVolumeSequence(volumeTuples, logger, Acts::Logging::DEBUG);
0923 }
0924
0925 ACTS_VERBOSE("Commit and update outer vector of volumes");
0926 m_volumes.clear();
0927 for (auto& vt : volumeTuples) {
0928 vt.commit(gctx, logger);
0929 m_volumes.push_back(vt.volume);
0930 }
0931
0932 } else if (m_direction == AxisDirection::AxisR) {
0933 ACTS_VERBOSE("Stack direction is r");
0934
0935 std::vector<VolumeTuple> volumeTuples;
0936 volumeTuples.reserve(m_volumes.size());
0937 std::ranges::transform(m_volumes, std::back_inserter(volumeTuples),
0938 [this, &gctx](const auto& volume) {
0939 return VolumeTuple{gctx, *volume,
0940 m_groupTransform};
0941 });
0942
0943 ACTS_VERBOSE("*** Initial volume configuration:");
0944 printVolumeSequence(volumeTuples, logger, Acts::Logging::DEBUG);
0945
0946 ACTS_VERBOSE("Resize all volumes to new z bounds and update transforms");
0947 for (auto& volume : volumeTuples) {
0948 volume.set({
0949 {CylinderVolumeBounds::eHalfLengthZ, newHlZ},
0950 });
0951 volume.setLocalTransform(newVolume.localTransform, m_groupTransform);
0952 }
0953
0954 ACTS_VERBOSE("*** Volume configuration after z resizing:");
0955 printVolumeSequence(volumeTuples, logger, Acts::Logging::DEBUG);
0956
0957 if (oldMinR == newMinR && oldMaxR == newMaxR) {
0958 ACTS_VERBOSE("Radii are the same, no r resize needed");
0959 } else {
0960 auto printGapDimensions = [&](const VolumeTuple& gap,
0961 const std::string& prefix = "") {
0962 ACTS_VERBOSE(" -> gap" << prefix << ": [ " << gap.minZ() << " <- "
0963 << gap.midZ() << " -> " << gap.maxZ()
0964 << " ], r: [ " << gap.minR() << " <-> "
0965 << gap.maxR() << " ]");
0966 };
0967
0968 if (oldMinR > newMinR) {
0969 if (firstStrategy == VolumeResizeStrategy::Expand) {
0970
0971 auto& first = volumeTuples.front();
0972 first.set({
0973 {CylinderVolumeBounds::eMinR, newMinR},
0974 });
0975 ACTS_VERBOSE(" -> z: [ " << first.minZ() << " <- " << first.midZ()
0976 << " -> " << first.maxZ() << " ], r: [ "
0977 << first.minR() << " <-> " << first.maxR()
0978 << " ]");
0979 } else if (firstStrategy == VolumeResizeStrategy::Gap) {
0980 auto& candidate = volumeTuples.front();
0981 if (isGap(candidate.volume)) {
0982 ACTS_VERBOSE("~> Reusing existing gap volume at inner r");
0983 auto& candidateCylBounds = dynamic_cast<CylinderVolumeBounds&>(
0984 candidate.volume->volumeBounds());
0985 printGapDimensions(candidate, " before");
0986 candidateCylBounds.set(CylinderVolumeBounds::eMinR, newMinR);
0987 candidate = VolumeTuple{gctx, *candidate.volume, m_groupTransform};
0988 printGapDimensions(candidate, " after ");
0989 } else {
0990 ACTS_VERBOSE("~> Creating new gap volume at inner r");
0991 auto gapBounds = std::make_shared<CylinderVolumeBounds>(
0992 newMinR, oldMinR, newHlZ);
0993 auto gapTransform = newVolume.globalTransform;
0994 auto gapVolume = addGapVolume(gapTransform, gapBounds);
0995 volumeTuples.insert(
0996 volumeTuples.begin(),
0997 VolumeTuple{gctx, *gapVolume, m_groupTransform});
0998 auto gap = volumeTuples.front();
0999 printGapDimensions(gap);
1000 }
1001 }
1002 }
1003
1004 if (oldMaxR < newMaxR) {
1005 if (secondStrategy == VolumeResizeStrategy::Expand) {
1006
1007 auto& last = volumeTuples.back();
1008 last.set({
1009 {CylinderVolumeBounds::eMaxR, newMaxR},
1010 });
1011 ACTS_VERBOSE(" -> z: [ " << last.minZ() << " <- " << last.midZ()
1012 << " -> " << last.maxZ() << " ], r: [ "
1013 << last.minR() << " <-> " << last.maxR()
1014 << " ]");
1015 } else if (secondStrategy == VolumeResizeStrategy::Gap) {
1016 auto& candidate = volumeTuples.back();
1017 if (isGap(candidate.volume)) {
1018 ACTS_VERBOSE("~> Reusing existing gap volume at outer r");
1019 auto& candidateCylBounds = dynamic_cast<CylinderVolumeBounds&>(
1020 candidate.volume->volumeBounds());
1021 printGapDimensions(candidate, " before");
1022 candidateCylBounds.set(CylinderVolumeBounds::eMaxR, newMaxR);
1023 candidate = VolumeTuple{gctx, *candidate.volume, m_groupTransform};
1024 printGapDimensions(candidate, " after ");
1025 } else {
1026 ACTS_VERBOSE("~> Creating new gap volume at outer r");
1027 auto gapBounds = std::make_shared<CylinderVolumeBounds>(
1028 oldMaxR, newMaxR, newHlZ);
1029 auto gapTransform = newVolume.globalTransform;
1030 auto gapVolume = addGapVolume(gapTransform, gapBounds);
1031 volumeTuples.emplace_back(gctx, *gapVolume,
1032 newVolume.globalTransform);
1033 auto gap = volumeTuples.back();
1034 printGapDimensions(gap);
1035 }
1036 }
1037 }
1038
1039 ACTS_VERBOSE("*** Volume configuration after r resizing:");
1040 printVolumeSequence(volumeTuples, logger, Acts::Logging::DEBUG);
1041 }
1042
1043 ACTS_VERBOSE("Commit and update outer vector of volumes");
1044 m_volumes.clear();
1045 for (auto& vt : volumeTuples) {
1046 vt.commit(gctx, logger);
1047 m_volumes.push_back(vt.volume);
1048 }
1049 }
1050
1051 Volume::update(gctx, std::move(cylBounds), newVolume.globalTransform, logger);
1052
1053 m_groupTransform = localToGlobalTransform(gctx);
1054 }
1055
1056 void CylinderVolumeStack::checkNoPhiOrBevel(const CylinderVolumeBounds& bounds,
1057 const Logger& logger) {
1058 if (bounds.get(CylinderVolumeBounds::eHalfPhiSector) != std::numbers::pi) {
1059 ACTS_ERROR(
1060 "CylinderVolumeStack requires all volumes to have a full "
1061 "phi sector");
1062 throw std::invalid_argument(
1063 "CylinderVolumeStack requires all volumes to have a full phi sector");
1064 }
1065
1066 if (bounds.get(CylinderVolumeBounds::eAveragePhi) != 0.0) {
1067 ACTS_ERROR(
1068 "CylinderVolumeStack requires all volumes to have an average "
1069 "phi of 0");
1070 throw std::invalid_argument(
1071 "CylinderVolumeStack requires all volumes to have an average phi of "
1072 "0");
1073 }
1074
1075 if (bounds.get(CylinderVolumeBounds::eBevelMinZ) != 0.0) {
1076 ACTS_ERROR(
1077 "CylinderVolumeStack requires all volumes to have a bevel angle of "
1078 "0");
1079 throw std::invalid_argument(
1080 "CylinderVolumeStack requires all volumes to have a bevel angle of "
1081 "0");
1082 }
1083
1084 if (bounds.get(CylinderVolumeBounds::eBevelMaxZ) != 0.0) {
1085 ACTS_ERROR(
1086 "CylinderVolumeStack requires all volumes to have a bevel angle of "
1087 "0");
1088 throw std::invalid_argument(
1089 "CylinderVolumeStack requires all volumes to have a bevel angle of "
1090 "0");
1091 }
1092 }
1093
1094 }