File indexing completed on 2025-01-18 09:11:17
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include "Acts/Detector/detail/CuboidalDetectorHelper.hpp"
0010
0011 #include "Acts/Definitions/Common.hpp"
0012 #include "Acts/Detector/DetectorVolume.hpp"
0013 #include "Acts/Detector/Portal.hpp"
0014 #include "Acts/Detector/detail/DetectorVolumeConsistency.hpp"
0015 #include "Acts/Detector/detail/PortalHelper.hpp"
0016 #include "Acts/Geometry/CuboidVolumeBounds.hpp"
0017 #include "Acts/Surfaces/PlaneSurface.hpp"
0018 #include "Acts/Surfaces/RectangleBounds.hpp"
0019 #include "Acts/Surfaces/Surface.hpp"
0020 #include "Acts/Utilities/BinningData.hpp"
0021 #include "Acts/Utilities/Enumerate.hpp"
0022 #include "Acts/Utilities/Helpers.hpp"
0023 #include "Acts/Utilities/StringHelpers.hpp"
0024
0025 #include <algorithm>
0026
0027 Acts::Experimental::DetectorComponent::PortalContainer
0028 Acts::Experimental::detail::CuboidalDetectorHelper::connect(
0029 const GeometryContext& gctx,
0030 std::vector<std::shared_ptr<Experimental::DetectorVolume>>& volumes,
0031 AxisDirection bValue, const std::vector<unsigned int>& selectedOnly,
0032 Acts::Logging::Level logLevel) {
0033 ACTS_LOCAL_LOGGER(getDefaultLogger("CuboidalDetectorHelper", logLevel));
0034
0035 ACTS_DEBUG("Connect " << volumes.size() << " detector volumes in "
0036 << axisDirectionName(bValue) << ".");
0037
0038
0039 auto centerDistances =
0040 DetectorVolumeConsistency::checkCenterAlignment(gctx, volumes, bValue);
0041
0042
0043 std::array<AxisDirection, 3u> possibleValues = {
0044 AxisDirection::AxisX, AxisDirection::AxisY, AxisDirection::AxisZ};
0045
0046
0047
0048 using PortalSet = std::array<std::size_t, 2u>;
0049 std::vector<PortalSet> portalSets = {
0050 {PortalSet{2, 3}, PortalSet{4, 5}, PortalSet{0, 1}}};
0051
0052
0053 auto [sIndex, fIndex] = portalSets[toUnderlying(bValue)];
0054
0055
0056 std::array<std::vector<double>, 3u> mergeSplits;
0057 std::array<double, 3u> mergeHalfLengths = {
0058 0.,
0059 0.,
0060 0.,
0061 };
0062
0063
0064 auto counterPart = [&](AxisDirection mValue) -> AxisDirection {
0065 for (auto cValue : possibleValues) {
0066 if (cValue != mValue && cValue != bValue) {
0067 return cValue;
0068 }
0069 }
0070 return mValue;
0071 };
0072
0073
0074
0075 using MergeParameters = std::tuple<double, Transform3>;
0076 std::map<std::size_t, MergeParameters> mergeParameters;
0077 auto& firstVolume = volumes.front();
0078 auto& lastVolume = volumes.back();
0079
0080 const auto firstBoundValues = firstVolume->volumeBounds().values();
0081 const auto lastBoundValues = lastVolume->volumeBounds().values();
0082 Vector3 stepDirection =
0083 firstVolume->transform(gctx).rotation().col(toUnderlying(bValue));
0084
0085 for (auto [im, mergeValue] : enumerate(possibleValues)) {
0086
0087 if (mergeValue == bValue) {
0088 continue;
0089 }
0090 for (auto [is, index] : enumerate(portalSets[toUnderlying(mergeValue)])) {
0091
0092 auto rotation = firstVolume->portalPtrs()[index]
0093 ->surface()
0094 .transform(gctx)
0095 .rotation();
0096 double stepDown = firstBoundValues[toUnderlying(bValue)];
0097 double stepUp = lastBoundValues[toUnderlying(bValue)];
0098
0099 auto translationF = firstVolume->portalPtrs()[index]
0100 ->surface()
0101 .transform(gctx)
0102 .translation();
0103
0104 auto translationL = lastVolume->portalPtrs()[index]
0105 ->surface()
0106 .transform(gctx)
0107 .translation();
0108
0109 Vector3 translation = 0.5 * (translationF - stepDown * stepDirection +
0110 translationL + stepUp * stepDirection);
0111
0112 Transform3 portalTransform = Transform3::Identity();
0113 portalTransform.prerotate(rotation);
0114 portalTransform.pretranslate(translation);
0115
0116 double keepHalfLength =
0117 firstBoundValues[toUnderlying(counterPart(mergeValue))];
0118 mergeParameters[index] = MergeParameters(keepHalfLength, portalTransform);
0119 }
0120 }
0121
0122
0123 for (auto [iv, v] : enumerate(volumes)) {
0124
0125 if (v->volumeBounds().type() != VolumeBounds::BoundsType::eCuboid) {
0126 throw std::invalid_argument(
0127 "CuboidalDetectorHelper: volume bounds are not cuboid");
0128 }
0129
0130
0131 if (iv > 0u) {
0132 ACTS_VERBOSE("- fuse portals of volume '"
0133 << volumes[iv - 1]->name() << "' with volume '" << v->name()
0134 << "'.");
0135 ACTS_VERBOSE("-- indices " << fIndex << " of first, " << sIndex
0136 << " of second volume.");
0137
0138 auto fPortal = volumes[iv - 1]->portalPtrs()[fIndex];
0139 auto sPortal = v->portalPtrs()[sIndex];
0140 auto fusedPortal = Portal::fuse(fPortal, sPortal);
0141 volumes[iv - 1]->updatePortal(fusedPortal, fIndex);
0142 v->updatePortal(fusedPortal, sIndex);
0143 }
0144
0145
0146 auto boundValues = v->volumeBounds().values();
0147
0148 for (auto [im, mergeValue] : enumerate(possibleValues)) {
0149
0150 if (mergeValue == bValue) {
0151 continue;
0152 }
0153
0154 mergeSplits[im].push_back(2 * boundValues[toUnderlying(bValue)]);
0155 mergeHalfLengths[im] += boundValues[toUnderlying(bValue)];
0156 }
0157 }
0158
0159
0160 std::vector<PortalReplacement> pReplacements;
0161 for (auto [im, mergeValue] : enumerate(possibleValues)) {
0162
0163 if (mergeValue == bValue) {
0164 continue;
0165 }
0166
0167
0168
0169
0170 bool mergedInX = true;
0171 switch (bValue) {
0172 case AxisDirection::AxisZ: {
0173 mergedInX = (mergeValue == AxisDirection::AxisY);
0174 } break;
0175 case AxisDirection::AxisY: {
0176 mergedInX = (mergeValue == AxisDirection::AxisX);
0177 } break;
0178 case AxisDirection::AxisX: {
0179 mergedInX = (mergeValue == AxisDirection::AxisZ);
0180 } break;
0181 default:
0182 break;
0183 }
0184
0185
0186 std::vector<double> stitchBoundaries;
0187 stitchBoundaries.push_back(-mergeHalfLengths[im]);
0188 for (auto step : mergeSplits[im]) {
0189 stitchBoundaries.push_back(stitchBoundaries.back() + step);
0190 }
0191
0192 for (auto [is, index] : enumerate(portalSets[toUnderlying(mergeValue)])) {
0193
0194 if (!selectedOnly.empty() && !rangeContainsValue(selectedOnly, index)) {
0195 continue;
0196 }
0197
0198 auto [keepHalfLength, portalTransform] = mergeParameters[index];
0199 std::shared_ptr<RectangleBounds> portalBounds =
0200 mergedInX ? std::make_shared<RectangleBounds>(mergeHalfLengths[im],
0201 keepHalfLength)
0202 : std::make_shared<RectangleBounds>(keepHalfLength,
0203 mergeHalfLengths[im]);
0204 auto portalSurface =
0205 Surface::makeShared<PlaneSurface>(portalTransform, portalBounds);
0206 auto portal = std::make_shared<Portal>(portalSurface);
0207
0208
0209
0210 Acts::Direction dir =
0211 (index % 2 == 0) ? Direction::Forward() : Direction::Backward();
0212
0213
0214 pReplacements.push_back(PortalReplacement(
0215 portal, index, dir, stitchBoundaries,
0216 (mergedInX ? AxisDirection::AxisX : AxisDirection::AxisY)));
0217 }
0218 }
0219
0220
0221 PortalHelper::attachExternalNavigationDelegates(gctx, volumes, pReplacements);
0222
0223
0224 DetectorComponent::PortalContainer dShell;
0225
0226
0227
0228 for (auto& iv : volumes) {
0229 ACTS_VERBOSE("- update portals of volume '" << iv->name() << "'.");
0230 for (auto& [p, i, dir, boundaries, binning] : pReplacements) {
0231
0232 dShell[i] = p;
0233 ACTS_VERBOSE("-- update portal with index " << i);
0234 iv->updatePortal(p, i);
0235 }
0236 }
0237
0238
0239 return dShell;
0240 }
0241
0242 Acts::Experimental::DetectorComponent::PortalContainer
0243 Acts::Experimental::detail::CuboidalDetectorHelper::connect(
0244 const GeometryContext& ,
0245 const std::vector<DetectorComponent::PortalContainer>& containers,
0246 AxisDirection bValue, const std::vector<unsigned int>& ,
0247 Acts::Logging::Level logLevel) noexcept(false) {
0248
0249 ACTS_LOCAL_LOGGER(getDefaultLogger("CuboidalDetectorHelper", logLevel));
0250
0251 ACTS_DEBUG("Connect " << containers.size() << " containers in "
0252 << axisDirectionName(bValue) << ".");
0253
0254
0255 DetectorComponent::PortalContainer dShell;
0256
0257
0258 std::array<AxisDirection, 3u> possibleValues = {
0259 AxisDirection::AxisX, AxisDirection::AxisY, AxisDirection::AxisZ};
0260
0261 using PortalSet = std::array<std::size_t, 2u>;
0262 std::vector<PortalSet> portalSets = {
0263 {PortalSet{2, 3}, PortalSet{4, 5}, PortalSet{0, 1}}};
0264
0265
0266 auto [endIndex, startIndex] = portalSets[toUnderlying(bValue)];
0267
0268
0269 for (std::size_t ic = 1; ic < containers.size(); ++ic) {
0270 auto& formerContainer = containers[ic - 1];
0271 auto& currentContainer = containers[ic];
0272
0273 if (!formerContainer.contains(startIndex)) {
0274 throw std::invalid_argument(
0275 "CuboidalDetectorHelper: proto container has no fuse portal at index "
0276 "of former container.");
0277 }
0278 if (!currentContainer.contains(endIndex)) {
0279 throw std::invalid_argument(
0280 "CuboidalDetectorHelper: proto container has no fuse portal at index "
0281 "of current container.");
0282 }
0283
0284 std::shared_ptr<Portal> sPortal = formerContainer.find(startIndex)->second;
0285 auto sAttachedVolumes =
0286 sPortal->attachedDetectorVolumes()[Direction::Backward().index()];
0287
0288 std::shared_ptr<Portal> ePortal = currentContainer.find(endIndex)->second;
0289 auto eAttachedVolumes =
0290 ePortal->attachedDetectorVolumes()[Direction::Forward().index()];
0291
0292 auto fusedPortal = Portal::fuse(sPortal, ePortal);
0293
0294 for (auto& av : sAttachedVolumes) {
0295 ACTS_VERBOSE("Update portal of detector volume '" << av->name() << "'.");
0296 av->updatePortal(fusedPortal, startIndex);
0297 }
0298
0299 for (auto& av : eAttachedVolumes) {
0300 ACTS_VERBOSE("Update portal of detector volume '" << av->name() << "'.");
0301 av->updatePortal(fusedPortal, endIndex);
0302 }
0303 }
0304
0305 dShell[startIndex] = containers.front().find(startIndex)->second;
0306 dShell[endIndex] = containers.back().find(endIndex)->second;
0307
0308
0309 std::vector<unsigned int> sidePortals = {};
0310 for (auto sVals : possibleValues) {
0311 if (sVals != bValue) {
0312 sidePortals.push_back(
0313 static_cast<unsigned int>(portalSets[toUnderlying(sVals)][0]));
0314 sidePortals.push_back(
0315 static_cast<unsigned int>(portalSets[toUnderlying(sVals)][1]));
0316 }
0317 }
0318
0319
0320 return dShell;
0321 }
0322
0323 std::array<std::vector<double>, 3u>
0324 Acts::Experimental::detail::CuboidalDetectorHelper::xyzBoundaries(
0325 [[maybe_unused]] const GeometryContext& gctx,
0326 [[maybe_unused]] const std::vector<
0327 const Acts::Experimental::DetectorVolume*>& volumes,
0328 Acts::Logging::Level logLevel) {
0329
0330 ACTS_LOCAL_LOGGER(getDefaultLogger("CuboidalDetectorHelper", logLevel));
0331
0332
0333 std::array<std::vector<double>, 3u> boundaries;
0334
0335
0336 std::array<std::map<double, std::size_t>, 3u> valueMaps;
0337 auto& xMap = valueMaps[0u];
0338 auto& yMap = valueMaps[1u];
0339 auto& zMap = valueMaps[2u];
0340
0341 auto fillMap = [&](std::map<double, std::size_t>& map,
0342 const std::array<double, 2u>& values) {
0343 for (auto v : values) {
0344
0345 ++map[v];
0346 }
0347 };
0348
0349
0350 for (const auto& v : volumes) {
0351 if (v->volumeBounds().type() == Acts::VolumeBounds::BoundsType::eCuboid) {
0352 auto bValues = v->volumeBounds().values();
0353
0354 double halfX = bValues[CuboidVolumeBounds::BoundValues::eHalfLengthX];
0355 double halfY = bValues[CuboidVolumeBounds::BoundValues::eHalfLengthY];
0356 double halfZ = bValues[CuboidVolumeBounds::BoundValues::eHalfLengthZ];
0357
0358 auto translation = v->transform(gctx).translation();
0359
0360 double xMin = translation.x() - halfX;
0361 double xMax = translation.x() + halfX;
0362 double yMin = translation.y() - halfY;
0363 double yMax = translation.y() + halfY;
0364 double zMin = translation.z() - halfZ;
0365 double zMax = translation.z() + halfZ;
0366
0367 fillMap(xMap, {xMin, xMax});
0368 fillMap(yMap, {yMin, yMax});
0369 fillMap(zMap, {zMin, zMax});
0370 }
0371 }
0372
0373 for (auto [im, map] : enumerate(valueMaps)) {
0374 for (auto [key, _] : map) {
0375 boundaries[im].push_back(key);
0376 }
0377 std::ranges::sort(boundaries[im]);
0378 }
0379
0380 ACTS_VERBOSE("- did yield " << boundaries[0u].size() << " boundaries in X.");
0381 ACTS_VERBOSE("- did yield " << boundaries[1u].size() << " boundaries in Y.");
0382 ACTS_VERBOSE("- did yield " << boundaries[2u].size() << " boundaries in Z.");
0383
0384 return boundaries;
0385 }