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