File indexing completed on 2025-07-14 08:10:53
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include "Acts/Detector/detail/BlueprintHelper.hpp"
0010
0011 #include "Acts/Definitions/Algebra.hpp"
0012 #include "Acts/Definitions/Tolerance.hpp"
0013 #include "Acts/Geometry/VolumeBounds.hpp"
0014
0015 #include <algorithm>
0016 #include <array>
0017 #include <ranges>
0018
0019 namespace {
0020
0021 std::array<Acts::Vector3, 2u> endPointsXYZ(
0022 const Acts::Experimental::Gen2Blueprint::Node& node,
0023 Acts::AxisDirection bVal) {
0024 unsigned int bIdx = 0;
0025 switch (bVal) {
0026 case Acts::AxisDirection::AxisX:
0027 bIdx = 0;
0028 break;
0029 case Acts::AxisDirection::AxisY:
0030 bIdx = 1;
0031 break;
0032 case Acts::AxisDirection::AxisZ:
0033 bIdx = 2;
0034 break;
0035 default:
0036 break;
0037 }
0038 Acts::Vector3 axis = node.transform.rotation().col(bIdx);
0039 auto halfL = node.boundaryValues[bIdx];
0040 Acts::Vector3 center = node.transform.translation();
0041 Acts::Vector3 p0 = center - halfL * axis;
0042 Acts::Vector3 p1 = center + halfL * axis;
0043 return {p0, p1};
0044 }
0045
0046 }
0047
0048 void Acts::Experimental::detail::BlueprintHelper::sort(
0049 Gen2Blueprint::Node& node, bool recursive) {
0050 if (node.children.size() < 2u) {
0051 return;
0052 }
0053
0054 if (node.binning.size() == 1) {
0055 auto bVal = node.binning.front();
0056
0057 if (bVal == AxisDirection::AxisX || bVal == AxisDirection::AxisY ||
0058 bVal == AxisDirection::AxisZ) {
0059 Vector3 nodeCenter = node.transform.translation();
0060 Vector3 nodeSortAxis = node.transform.rotation().col(toUnderlying(bVal));
0061 std::ranges::sort(node.children, {}, [&](const auto& c) {
0062 return (c->transform.translation() - nodeCenter).dot(nodeSortAxis);
0063 });
0064 } else if (bVal == AxisDirection::AxisR &&
0065 node.boundsType == VolumeBounds::eCylinder) {
0066 std::ranges::sort(node.children, {}, [](const auto& c) {
0067 return c->boundaryValues[0] + c->boundaryValues[1];
0068 });
0069 }
0070 }
0071
0072
0073 if (recursive) {
0074 for (auto& child : node.children) {
0075 sort(*child, true);
0076 }
0077 }
0078 }
0079
0080 void Acts::Experimental::detail::BlueprintHelper::fillGaps(
0081 Gen2Blueprint::Node& node, bool adjustToParent) {
0082
0083 if (node.isLeaf()) {
0084 return;
0085 }
0086 if (node.boundsType == VolumeBounds::eCylinder && node.binning.size() == 1) {
0087 fillGapsCylindrical(node, adjustToParent);
0088 } else if (node.boundsType == VolumeBounds::eCuboid &&
0089 node.binning.size() == 1) {
0090
0091
0092
0093 fillGapsCuboidal(node, adjustToParent);
0094 } else {
0095 throw std::runtime_error(
0096 "BlueprintHelper: gap filling is not implemented for "
0097 "this boundary type");
0098 }
0099 }
0100
0101 void Acts::Experimental::detail::BlueprintHelper::fillGapsCylindrical(
0102 Gen2Blueprint::Node& node, bool adjustToParent) {
0103
0104 sort(node, false);
0105
0106
0107 auto cInnerR = node.boundaryValues[0];
0108 auto cOuterR = node.boundaryValues[1];
0109 auto cHalfZ = node.boundaryValues[2];
0110
0111 std::vector<std::unique_ptr<Gen2Blueprint::Node>> gaps;
0112
0113 if (AxisDirection bVal = node.binning.front(); bVal == AxisDirection::AxisZ) {
0114
0115 if (adjustToParent) {
0116 std::ranges::for_each(node.children, [&](auto& child) {
0117 child->boundaryValues[0] = cInnerR;
0118 child->boundaryValues[1] = cOuterR;
0119 });
0120 }
0121 auto [negC, posC] = endPointsXYZ(node, bVal);
0122
0123 unsigned int igap = 0;
0124 for (auto& child : node.children) {
0125 auto [neg, pos] = endPointsXYZ(*child, bVal);
0126 double gapSpan = (neg - negC).norm();
0127 if (gapSpan > s_onSurfaceTolerance) {
0128
0129 auto gapName = node.name + "_gap_" + std::to_string(igap);
0130 auto gapTransform = Transform3::Identity();
0131 gapTransform.rotate(node.transform.rotation());
0132 gapTransform.pretranslate(0.5 * (neg + negC));
0133 auto gap = std::make_unique<Gen2Blueprint::Node>(
0134 gapName, gapTransform, VolumeBounds::eCylinder,
0135 std::vector<double>{cInnerR, cOuterR, 0.5 * gapSpan});
0136 gaps.push_back(std::move(gap));
0137 ++igap;
0138 }
0139
0140 negC = pos;
0141 }
0142
0143 double gapSpan = (negC - posC).norm();
0144 if (gapSpan > s_onSurfaceTolerance) {
0145
0146 auto gapName = node.name + "_gap_" + std::to_string(igap);
0147 auto gapTransform = Transform3::Identity();
0148 gapTransform.rotate(node.transform.rotation());
0149 gapTransform.pretranslate(0.5 * (negC + posC));
0150 auto gap = std::make_unique<Gen2Blueprint::Node>(
0151 gapName, gapTransform, VolumeBounds::eCylinder,
0152 std::vector<double>{cInnerR, cOuterR, 0.5 * gapSpan});
0153 gaps.push_back(std::move(gap));
0154 }
0155
0156 } else if (bVal == AxisDirection::AxisR) {
0157
0158 if (adjustToParent) {
0159 std::ranges::for_each(node.children, [&](auto& child) {
0160 child->transform = node.transform;
0161 child->boundaryValues[2] = cHalfZ;
0162 });
0163 }
0164
0165 unsigned int igap = 0;
0166 double lastR = cInnerR;
0167 for (auto& child : node.children) {
0168 double iR = child->boundaryValues[0];
0169 if (std::abs(iR - lastR) > s_onSurfaceTolerance) {
0170 auto gap = std::make_unique<Gen2Blueprint::Node>(
0171 node.name + "_gap_" + std::to_string(igap), node.transform,
0172 VolumeBounds::eCylinder, std::vector<double>{lastR, iR, cHalfZ});
0173 gaps.push_back(std::move(gap));
0174 ++igap;
0175 }
0176
0177 lastR = child->boundaryValues[1];
0178 }
0179
0180 if (std::abs(lastR - cOuterR) > s_onSurfaceTolerance) {
0181 auto gap = std::make_unique<Gen2Blueprint::Node>(
0182 node.name + "_gap_" + std::to_string(igap), node.transform,
0183 VolumeBounds::eCylinder, std::vector<double>{lastR, cOuterR, cHalfZ});
0184 gaps.push_back(std::move(gap));
0185 }
0186 } else {
0187 throw std::runtime_error(
0188 "BlueprintHelper: gap filling not implemented for "
0189 "cylinder and this binning type.");
0190 }
0191
0192
0193 for (auto& gap : gaps) {
0194 node.add(std::move(gap));
0195 }
0196
0197
0198 sort(node, false);
0199
0200 for (auto& child : node.children) {
0201 fillGaps(*child, adjustToParent);
0202 }
0203 }
0204
0205 void Acts::Experimental::detail::BlueprintHelper::fillGapsCuboidal(
0206 Gen2Blueprint::Node& node, bool adjustToParent) {
0207
0208 sort(node, false);
0209
0210
0211 std::array<Acts::AxisDirection, 3u> allowedBinVals = {
0212 AxisDirection::AxisX, AxisDirection::AxisY, AxisDirection::AxisZ};
0213
0214 std::vector<std::unique_ptr<Gen2Blueprint::Node>> gaps;
0215 auto binVal = node.binning.front();
0216
0217
0218 if (adjustToParent) {
0219 std::ranges::for_each(node.children, [&](auto& child) {
0220 for (auto bv : allowedBinVals) {
0221 if (bv != binVal) {
0222
0223
0224 child->boundaryValues[toUnderlying(bv)] =
0225 node.boundaryValues[toUnderlying(bv)];
0226 child->transform.translation()[toUnderlying(bv)] =
0227 node.transform.translation()[toUnderlying(bv)];
0228 }
0229 }
0230 });
0231 }
0232 auto [negC, posC] = endPointsXYZ(node, binVal);
0233
0234
0235 unsigned int igap = 0;
0236 for (auto& child : node.children) {
0237 auto [neg, pos] = endPointsXYZ(*child, binVal);
0238 double gapSpan = (neg - negC).norm();
0239 if (gapSpan > s_onSurfaceTolerance) {
0240
0241 auto gapName = node.name + "_gap_" + std::to_string(igap);
0242 auto gapTransform = Transform3::Identity();
0243 gapTransform.rotate(node.transform.rotation());
0244 gapTransform.pretranslate(0.5 * (neg + negC));
0245 std::vector<double> gapBounds{0, 0, 0};
0246 gapBounds[toUnderlying(binVal)] = 0.5 * gapSpan;
0247 for (auto bv : allowedBinVals) {
0248 if (bv != binVal) {
0249 gapBounds[toUnderlying(bv)] = node.boundaryValues[toUnderlying(bv)];
0250 }
0251 }
0252 auto gap = std::make_unique<Gen2Blueprint::Node>(
0253 gapName, gapTransform, VolumeBounds::eCuboid, gapBounds);
0254 gaps.push_back(std::move(gap));
0255 ++igap;
0256 }
0257
0258 negC = pos;
0259 }
0260
0261 double gapSpan = (negC - posC).norm();
0262 if (gapSpan > s_onSurfaceTolerance) {
0263
0264 auto gapName = node.name + "_gap_" + std::to_string(igap);
0265 auto gapTransform = Transform3::Identity();
0266 gapTransform.rotate(node.transform.rotation());
0267 gapTransform.pretranslate(0.5 * (negC + posC));
0268 std::vector<double> gapBounds{0, 0, 0};
0269 gapBounds[toUnderlying(binVal)] = 0.5 * gapSpan;
0270 for (auto bv : allowedBinVals) {
0271 if (bv != binVal) {
0272 gapBounds[toUnderlying(bv)] = node.boundaryValues[toUnderlying(bv)];
0273 }
0274 }
0275 auto gap = std::make_unique<Gen2Blueprint::Node>(
0276 gapName, gapTransform, VolumeBounds::eCuboid, gapBounds);
0277 gaps.push_back(std::move(gap));
0278 }
0279
0280
0281 for (auto& gap : gaps) {
0282 node.add(std::move(gap));
0283 }
0284
0285
0286 sort(node, false);
0287
0288 for (auto& child : node.children) {
0289 fillGaps(*child, adjustToParent);
0290 }
0291 }