File indexing completed on 2026-09-26 08:01:39
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include "Acts/Geometry/CylinderVolumeBounds.hpp"
0010
0011 #include "Acts/Definitions/Algebra.hpp"
0012 #include "Acts/Definitions/Direction.hpp"
0013 #include "Acts/Definitions/Tolerance.hpp"
0014 #include "Acts/Surfaces/CylinderBounds.hpp"
0015 #include "Acts/Surfaces/CylinderSurface.hpp"
0016 #include "Acts/Surfaces/DiscSurface.hpp"
0017 #include "Acts/Surfaces/PlaneSurface.hpp"
0018 #include "Acts/Surfaces/RadialBounds.hpp"
0019 #include "Acts/Surfaces/RectangleBounds.hpp"
0020 #include "Acts/Surfaces/Surface.hpp"
0021 #include "Acts/Utilities/BoundingBox.hpp"
0022 #include "Acts/Utilities/detail/OstreamStateGuard.hpp"
0023 #include "Acts/Utilities/detail/periodic.hpp"
0024
0025 #include <cmath>
0026 #include <iomanip>
0027 #include <numbers>
0028 #include <utility>
0029
0030 namespace Acts {
0031
0032 CylinderVolumeBounds::CylinderVolumeBounds(double rmin, double rmax,
0033 double halfz, double halfphi,
0034 double avgphi)
0035 : m_values() {
0036 m_values[eMinR] = rmin;
0037 m_values[eMaxR] = rmax;
0038 m_values[eHalfLengthZ] = halfz;
0039 m_values[eHalfPhiSector] = halfphi;
0040 m_values[eAveragePhi] = avgphi;
0041 checkConsistency();
0042 buildSurfaceBounds();
0043 }
0044
0045 CylinderVolumeBounds::CylinderVolumeBounds(
0046 const std::array<double, eSize>& values)
0047 : m_values(values) {
0048 checkConsistency();
0049 buildSurfaceBounds();
0050 }
0051
0052 CylinderVolumeBounds::CylinderVolumeBounds(const CylinderBounds& cBounds,
0053 double thickness)
0054 : VolumeBounds() {
0055 double cR = cBounds.get(CylinderBounds::eR);
0056 if (thickness <= 0. || (cR - 0.5 * thickness) < 0.) {
0057 throw(std::invalid_argument(
0058 "CylinderVolumeBounds: invalid extrusion thickness."));
0059 }
0060 m_values[eMinR] = cR - 0.5 * thickness;
0061 m_values[eMaxR] = cR + 0.5 * thickness;
0062 m_values[eHalfLengthZ] = cBounds.get(CylinderBounds::eHalfLengthZ);
0063 m_values[eHalfPhiSector] = cBounds.get(CylinderBounds::eHalfPhiSector);
0064 m_values[eAveragePhi] = cBounds.get(CylinderBounds::eAveragePhi);
0065 buildSurfaceBounds();
0066 }
0067
0068 CylinderVolumeBounds::CylinderVolumeBounds(const RadialBounds& rBounds,
0069 double thickness)
0070 : VolumeBounds() {
0071 if (thickness <= 0.) {
0072 throw(std::invalid_argument(
0073 "CylinderVolumeBounds: invalid extrusion thickness."));
0074 }
0075 m_values[eMinR] = rBounds.get(RadialBounds::eMinR);
0076 m_values[eMaxR] = rBounds.get(RadialBounds::eMaxR);
0077 m_values[eHalfLengthZ] = 0.5 * thickness;
0078 m_values[eHalfPhiSector] = rBounds.get(RadialBounds::eHalfPhiSector);
0079 m_values[eAveragePhi] = rBounds.get(RadialBounds::eAveragePhi);
0080 buildSurfaceBounds();
0081 }
0082
0083 std::vector<OrientedSurface> CylinderVolumeBounds::orientedSurfaces(
0084 const Transform3& transform) const {
0085 std::vector<OrientedSurface> oSurfaces;
0086 oSurfaces.reserve(6);
0087
0088 const Transform3 transMinZ =
0089 transform * Translation3(0., 0., -get(eHalfLengthZ));
0090 const Transform3 transMaxZ =
0091 transform * Translation3(0., 0., get(eHalfLengthZ));
0092
0093 auto dSurface = Surface::makeShared<DiscSurface>(transMinZ, m_discBounds);
0094 oSurfaces.emplace_back(std::move(dSurface), Direction::AlongNormal());
0095
0096 dSurface = Surface::makeShared<DiscSurface>(transMaxZ, m_discBounds);
0097 oSurfaces.emplace_back(std::move(dSurface), Direction::OppositeNormal());
0098
0099
0100 auto cSurface =
0101 Surface::makeShared<CylinderSurface>(transform, m_outerCylinderBounds);
0102 oSurfaces.emplace_back(std::move(cSurface), Direction::OppositeNormal());
0103
0104
0105 if (m_innerCylinderBounds != nullptr) {
0106 cSurface =
0107 Surface::makeShared<CylinderSurface>(transform, m_innerCylinderBounds);
0108 oSurfaces.emplace_back(std::move(cSurface), Direction::AlongNormal());
0109 }
0110
0111
0112 if (m_sectorPlaneBounds != nullptr) {
0113
0114 const Transform3 sp1Transform =
0115 Transform3(transform *
0116 AngleAxis3(get(eAveragePhi) - get(eHalfPhiSector),
0117 Vector3(0., 0., 1.)) *
0118 Translation3(0.5 * (get(eMinR) + get(eMaxR)), 0., 0.) *
0119 AngleAxis3(std::numbers::pi / 2, Vector3(1., 0., 0.)));
0120 auto pSurface =
0121 Surface::makeShared<PlaneSurface>(sp1Transform, m_sectorPlaneBounds);
0122 oSurfaces.emplace_back(std::move(pSurface), Direction::AlongNormal());
0123
0124 const Transform3 sp2Transform =
0125 Transform3(transform *
0126 AngleAxis3(get(eAveragePhi) + get(eHalfPhiSector),
0127 Vector3(0., 0., 1.)) *
0128 Translation3(0.5 * (get(eMinR) + get(eMaxR)), 0., 0.) *
0129 AngleAxis3(-std::numbers::pi / 2, Vector3(1., 0., 0.)));
0130 pSurface =
0131 Surface::makeShared<PlaneSurface>(sp2Transform, m_sectorPlaneBounds);
0132 oSurfaces.emplace_back(std::move(pSurface), Direction::OppositeNormal());
0133 }
0134 return oSurfaces;
0135 }
0136
0137 void CylinderVolumeBounds::buildSurfaceBounds() {
0138 if (get(eMinR) > s_epsilon) {
0139 m_innerCylinderBounds = std::make_shared<const CylinderBounds>(
0140 get(eMinR), get(eHalfLengthZ), get(eHalfPhiSector), get(eAveragePhi));
0141 }
0142 m_outerCylinderBounds = std::make_shared<const CylinderBounds>(
0143 get(eMaxR), get(eHalfLengthZ), get(eHalfPhiSector), get(eAveragePhi));
0144 m_discBounds = std::make_shared<const RadialBounds>(
0145 get(eMinR), get(eMaxR), get(eHalfPhiSector), get(eAveragePhi));
0146
0147 if (std::abs(get(eHalfPhiSector) - std::numbers::pi) > s_epsilon) {
0148 m_sectorPlaneBounds = std::make_shared<const RectangleBounds>(
0149 0.5 * (get(eMaxR) - get(eMinR)), get(eHalfLengthZ));
0150 }
0151 }
0152
0153 std::ostream& CylinderVolumeBounds::toStream(std::ostream& os) const {
0154 detail::OstreamStateGuard guard{os};
0155 os << std::fixed << std::setprecision(5);
0156 os << "CylinderVolumeBounds: (rMin, rMax, halfZ, halfPhi, "
0157 "averagePhi) = ";
0158 os << get(eMinR) << ", " << get(eMaxR) << ", " << get(eHalfLengthZ) << ", "
0159 << get(eHalfPhiSector) << ", " << get(eAveragePhi);
0160 return os;
0161 }
0162
0163 Volume::BoundingBox CylinderVolumeBounds::boundingBox(
0164 const Transform3* trf, const Vector3& envelope,
0165 const Volume* entity) const {
0166 double xmax = 0, xmin = 0, ymax = 0, ymin = 0;
0167 xmax = get(eMaxR);
0168
0169 if (get(eHalfPhiSector) > std::numbers::pi / 2.) {
0170
0171 ymax = xmax;
0172 ymin = -xmax;
0173 xmin = xmax * std::cos(get(eHalfPhiSector));
0174 } else {
0175
0176 ymax = get(eMaxR) * std::sin(get(eHalfPhiSector));
0177 ymin = -ymax;
0178
0179 xmin = get(eMinR) * std::cos(get(eHalfPhiSector));
0180 }
0181
0182 Vector3 vmin(xmin, ymin, -get(eHalfLengthZ));
0183 Vector3 vmax(xmax, ymax, get(eHalfLengthZ));
0184
0185
0186 Volume::BoundingBox box{entity, vmin - envelope, vmax + envelope};
0187 return trf == nullptr ? box : box.transformed(*trf);
0188 }
0189
0190 bool CylinderVolumeBounds::inside(const Vector3& pos, double tol) const {
0191 using VectorHelpers::perp;
0192 using VectorHelpers::phi;
0193 double ros = perp(pos);
0194 bool insidePhi = std::cos(phi(pos)) >= std::cos(get(eHalfPhiSector)) - tol;
0195 bool insideR = insidePhi
0196 ? ((ros >= get(eMinR) - tol) && (ros <= get(eMaxR) + tol))
0197 : false;
0198 bool insideZ =
0199 insideR ? (std::abs(pos.z()) <= get(eHalfLengthZ) + tol) : false;
0200 return (insideZ && insideR && insidePhi);
0201 }
0202
0203 Vector3 CylinderVolumeBounds::referenceOffset(AxisDirection aDir)
0204 const {
0205 if (aDir == Acts::AxisDirection::AxisR ||
0206 aDir == Acts::AxisDirection::AxisRPhi) {
0207 return Vector3(0.5 * (get(eMinR) + get(eMaxR)), 0., 0.);
0208 }
0209 return VolumeBounds::referenceOffset(aDir);
0210 }
0211
0212 double CylinderVolumeBounds::referenceBorder(AxisDirection aDir) const {
0213 if (aDir == Acts::AxisDirection::AxisR) {
0214 return 0.5 * (get(eMaxR) - get(eMinR));
0215 }
0216 if (aDir == Acts::AxisDirection::AxisZ) {
0217 return get(eHalfLengthZ);
0218 }
0219 return VolumeBounds::referenceBorder(aDir);
0220 }
0221
0222 std::vector<double> CylinderVolumeBounds::values() const {
0223 return {m_values.begin(), m_values.end()};
0224 }
0225
0226 void CylinderVolumeBounds::checkConsistency() {
0227 if (get(eMinR) < 0. || get(eMaxR) <= 0.) {
0228 throw std::invalid_argument(
0229 "CylinderVolumeBounds: invalid radial input: minR (" +
0230 std::to_string(get(eMinR)) + ") < 0 or maxR (" +
0231 std::to_string(get(eMaxR)) + ") <= 0");
0232 }
0233 if (get(eMinR) >= get(eMaxR)) {
0234 throw std::invalid_argument(
0235 "CylinderVolumeBounds: invalid radial input: minR (" +
0236 std::to_string(get(eMinR)) + ") >= (" + std::to_string(get(eMaxR)) +
0237 ")");
0238 }
0239 if (get(eHalfLengthZ) <= 0) {
0240 throw std::invalid_argument(
0241 "CylinderVolumeBounds: invalid longitudinal input: hlZ (" +
0242 std::to_string(get(eHalfLengthZ)) + ") <= 0");
0243 }
0244 if (get(eHalfPhiSector) < 0. || get(eHalfPhiSector) > std::numbers::pi) {
0245 throw std::invalid_argument(
0246 "CylinderVolumeBounds: invalid phi sector setup.");
0247 }
0248 if (get(eAveragePhi) != detail::radian_sym(get(eAveragePhi))) {
0249 throw std::invalid_argument(
0250 "CylinderVolumeBounds: invalid phi positioning.");
0251 }
0252 }
0253
0254 void CylinderVolumeBounds::set(BoundValues bValue, double value) {
0255 set({{bValue, value}});
0256 }
0257
0258 void CylinderVolumeBounds::set(
0259 std::initializer_list<std::pair<BoundValues, double>> keyValues) {
0260 std::array<double, eSize> previous = m_values;
0261 for (const auto& [key, value] : keyValues) {
0262 m_values[key] = value;
0263 }
0264 try {
0265 checkConsistency();
0266 buildSurfaceBounds();
0267 } catch (std::invalid_argument& e) {
0268 m_values = previous;
0269 throw e;
0270 }
0271 }
0272
0273 CylinderVolumeBounds::CylinderVolumeBounds(const CylinderVolumeBounds& cylbo) =
0274 default;
0275
0276 }