Back to home page

sPhenix code displayed by LXR

 
 

    


File indexing completed on 2025-08-06 08:10:22

0001 // This file is part of the Acts project.
0002 //
0003 // Copyright (C) 2023 CERN for the benefit of the Acts project
0004 //
0005 // This Source Code Form is subject to the terms of the Mozilla Public
0006 // License, v. 2.0. If a copy of the MPL was not distributed with this
0007 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
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   // Check transform for consistency
0038   auto centerDistances =
0039       DetectorVolumeConsistency::checkCenterAlignment(gctx, volumes, bValue);
0040 
0041   // Assign the portal indices according to the volume bounds definition
0042   std::array<BinningValue, 3u> possibleValues = {binX, binY, binZ};
0043   // 1 -> [ 2,3 ] for binX connection (cylclic one step)
0044   // 2 -> [ 4,5 ] for binY connection (cylclic two steps)
0045   // 0 -> [ 0,1 ] for binZ connection (to be in line with cylinder covnention)
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   // This is the picked set for fusing
0051   auto [sIndex, fIndex] = portalSets[bValue];
0052 
0053   // Log the merge splits, i.e. the boundaries of the volumes
0054   std::array<std::vector<ActsScalar>, 3u> mergeSplits;
0055   std::array<ActsScalar, 3u> mergeHalfLengths = {
0056       0.,
0057       0.,
0058       0.,
0059   };
0060 
0061   // Pick the counter part value
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   // Things that can be done without a loop be first/last check
0072   // Estimate the merge parameters: the scalar and the transform
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   // Values
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     // Skip the bin value itself, fusing will took care of that
0084     if (mergeValue == bValue) {
0085       continue;
0086     }
0087     for (auto [is, index] : enumerate(portalSets[mergeValue])) {
0088       // Take rotation from first volume
0089       auto rotation = firstVolume->portalPtrs()[index]
0090                           ->surface()
0091                           .transform(gctx)
0092                           .rotation();
0093       ActsScalar stepDown = firstBoundValues[bValue];
0094       ActsScalar stepUp = lastBoundValues[bValue];
0095       // Take translation from first and last volume
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       // The half length to be kept
0113       ActsScalar keepHalfLength = firstBoundValues[counterPart(mergeValue)];
0114       mergeParameters[index] = MergeParameters(keepHalfLength, portalTransform);
0115     }
0116   }
0117 
0118   // Loop over the volumes and fuse the portals, collect the merge information
0119   for (auto [iv, v] : enumerate(volumes)) {
0120     // So far works only in a cubioid setup
0121     if (v->volumeBounds().type() != VolumeBounds::BoundsType::eCuboid) {
0122       throw std::invalid_argument(
0123           "CuboidalDetectorHelper: volume bounds are not cuboid");
0124     }
0125 
0126     // Loop to fuse the portals along the connection direction (bValue)
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       // Fusing the portals of the current volume with the previous one
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     // Get the bound values
0142     auto boundValues = v->volumeBounds().values();
0143     // Loop to determine the merge bounds, the new transform
0144     for (auto [im, mergeValue] : enumerate(possibleValues)) {
0145       // Skip the bin value itself, fusing will took care of that
0146       if (mergeValue == bValue) {
0147         continue;
0148       }
0149       // Record the merge splits
0150       mergeSplits[im].push_back(2 * boundValues[bValue]);
0151       mergeHalfLengths[im] += boundValues[bValue];
0152     }
0153   }
0154 
0155   // Loop to create the new portals as portal replacements
0156   std::vector<PortalReplacement> pReplacements;
0157   for (auto [im, mergeValue] : enumerate(possibleValues)) {
0158     // Skip the bin value itself, fusing took care of that
0159     if (mergeValue == bValue) {
0160       continue;
0161     }
0162 
0163     // Create the new RectangleBounds
0164     // - there are conventions involved, regarding the bounds orientation
0165     // - this is an anticyclic swap
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     // The stitch boundaries for portal pointing
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       // Check if you need to skip due to selections
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       // Assign the portal direction
0207       // in a consistent way
0208       Acts::Direction dir =
0209           (index % 2 == 0) ? Direction::Forward : Direction::Backward;
0210 
0211       // Make the stitch boundaries
0212       pReplacements.push_back(PortalReplacement(
0213           portal, index, dir, stitchBoundaries, (mergedInX ? binX : binY)));
0214     }
0215   }
0216 
0217   // Attach the new volume updaters
0218   PortalHelper::attachDetectorVolumeUpdaters(gctx, volumes, pReplacements);
0219 
0220   // Return proto container
0221   DetectorComponent::PortalContainer dShell;
0222 
0223   // Update the portals of all volumes
0224   // Exchange the portals of the volumes
0225   for (auto& iv : volumes) {
0226     ACTS_VERBOSE("- update portals of volume '" << iv->name() << "'.");
0227     for (auto& [p, i, dir, boundaries, binning] : pReplacements) {
0228       // Fill the map
0229       dShell[i] = p;
0230       ACTS_VERBOSE("-- update portal with index " << i);
0231       iv->updatePortal(p, i);
0232     }
0233   }
0234   // Done.
0235 
0236   return dShell;
0237 }
0238 
0239 Acts::Experimental::DetectorComponent::PortalContainer
0240 Acts::Experimental::detail::CuboidalDetectorHelper::connect(
0241     const GeometryContext& /*gctx*/,
0242     const std::vector<DetectorComponent::PortalContainer>& containers,
0243     BinningValue bValue, const std::vector<unsigned int>& /*unused */,
0244     Acts::Logging::Level logLevel) noexcept(false) {
0245   // The local logger
0246   ACTS_LOCAL_LOGGER(getDefaultLogger("CuboidalDetectorHelper", logLevel));
0247 
0248   ACTS_DEBUG("Connect " << containers.size() << " containers in "
0249                         << binningValueNames()[bValue] << ".");
0250 
0251   // Return the new container
0252   DetectorComponent::PortalContainer dShell;
0253 
0254   // The possible bin values
0255   std::array<BinningValue, 3u> possibleValues = {binX, binY, binZ};
0256   // And their associated portal sets, see above
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   // This is the picked set for refubishing
0262   auto [endIndex, startIndex] = portalSets[bValue];
0263 
0264   // Fusing along the connection direction (bValue)
0265   for (std::size_t ic = 1; ic < containers.size(); ++ic) {
0266     auto& formerContainer = containers[ic - 1];
0267     auto& currentContainer = containers[ic];
0268     // Check and throw exception
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   // Proto container refurbishment - outside
0303   dShell[startIndex] = containers.front().find(startIndex)->second;
0304   dShell[endIndex] = containers.back().find(endIndex)->second;
0305 
0306   // Create remaining outside shells now
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   // Done.
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   // The local logger
0326   ACTS_LOCAL_LOGGER(getDefaultLogger("CuboidalDetectorHelper", logLevel));
0327 
0328   // The return boundaries
0329   std::array<std::vector<Acts::ActsScalar>, 3u> boundaries;
0330 
0331   // The map for collecting
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   // Loop over the volumes and collect boundaries
0349   for (const auto& v : volumes) {
0350     if (v->volumeBounds().type() == Acts::VolumeBounds::BoundsType::eCuboid) {
0351       auto bValues = v->volumeBounds().values();
0352       // The min/max values
0353       ActsScalar halfX = bValues[CuboidVolumeBounds::BoundValues::eHalfLengthX];
0354       ActsScalar halfY = bValues[CuboidVolumeBounds::BoundValues::eHalfLengthY];
0355       ActsScalar halfZ = bValues[CuboidVolumeBounds::BoundValues::eHalfLengthZ];
0356       // Get the transform @todo use a center of gravity of the detector
0357       auto translation = v->transform(gctx).translation();
0358       // The min/max values
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       // Fill the maps
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 }