Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:11:17

0001 // This file is part of the ACTS project.
0002 //
0003 // Copyright (C) 2016 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 https://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/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   // Check transform for consistency
0039   auto centerDistances =
0040       DetectorVolumeConsistency::checkCenterAlignment(gctx, volumes, bValue);
0041 
0042   // Assign the portal indices according to the volume bounds definition
0043   std::array<AxisDirection, 3u> possibleValues = {
0044       AxisDirection::AxisX, AxisDirection::AxisY, AxisDirection::AxisZ};
0045   // 1 -> [ 2,3 ] for AxisX connection (cylclic one step)
0046   // 2 -> [ 4,5 ] for AxisY connection (cylclic two steps)
0047   // 0 -> [ 0,1 ] for AxisZ connection (to be in line with cylinder covnention)
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   // This is the picked set for fusing
0053   auto [sIndex, fIndex] = portalSets[toUnderlying(bValue)];
0054 
0055   // Log the merge splits, i.e. the boundaries of the volumes
0056   std::array<std::vector<double>, 3u> mergeSplits;
0057   std::array<double, 3u> mergeHalfLengths = {
0058       0.,
0059       0.,
0060       0.,
0061   };
0062 
0063   // Pick the counter part value
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   // Things that can be done without a loop be first/last check
0074   // Estimate the merge parameters: the scalar and the transform
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   // Values
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     // Skip the bin value itself, fusing will took care of that
0087     if (mergeValue == bValue) {
0088       continue;
0089     }
0090     for (auto [is, index] : enumerate(portalSets[toUnderlying(mergeValue)])) {
0091       // Take rotation from first volume
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       // Take translation from first and last volume
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       // The half length to be kept
0116       double keepHalfLength =
0117           firstBoundValues[toUnderlying(counterPart(mergeValue))];
0118       mergeParameters[index] = MergeParameters(keepHalfLength, portalTransform);
0119     }
0120   }
0121 
0122   // Loop over the volumes and fuse the portals, collect the merge information
0123   for (auto [iv, v] : enumerate(volumes)) {
0124     // So far works only in a cubioid setup
0125     if (v->volumeBounds().type() != VolumeBounds::BoundsType::eCuboid) {
0126       throw std::invalid_argument(
0127           "CuboidalDetectorHelper: volume bounds are not cuboid");
0128     }
0129 
0130     // Loop to fuse the portals along the connection direction (bValue)
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       // Fusing the portals of the current volume with the previous one
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     // Get the bound values
0146     auto boundValues = v->volumeBounds().values();
0147     // Loop to determine the merge bounds, the new transform
0148     for (auto [im, mergeValue] : enumerate(possibleValues)) {
0149       // Skip the bin value itself, fusing will took care of that
0150       if (mergeValue == bValue) {
0151         continue;
0152       }
0153       // Record the merge splits
0154       mergeSplits[im].push_back(2 * boundValues[toUnderlying(bValue)]);
0155       mergeHalfLengths[im] += boundValues[toUnderlying(bValue)];
0156     }
0157   }
0158 
0159   // Loop to create the new portals as portal replacements
0160   std::vector<PortalReplacement> pReplacements;
0161   for (auto [im, mergeValue] : enumerate(possibleValues)) {
0162     // Skip the bin value itself, fusing took care of that
0163     if (mergeValue == bValue) {
0164       continue;
0165     }
0166 
0167     // Create the new RectangleBounds
0168     // - there are conventions involved, regarding the bounds orientation
0169     // - this is an anticyclic swap
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     // The stitch boundaries for portal pointing
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       // Check if you need to skip due to selections
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       // Assign the portal direction
0209       // in a consistent way
0210       Acts::Direction dir =
0211           (index % 2 == 0) ? Direction::Forward() : Direction::Backward();
0212 
0213       // Make the stitch boundaries
0214       pReplacements.push_back(PortalReplacement(
0215           portal, index, dir, stitchBoundaries,
0216           (mergedInX ? AxisDirection::AxisX : AxisDirection::AxisY)));
0217     }
0218   }
0219 
0220   // Attach the new volume updaters
0221   PortalHelper::attachExternalNavigationDelegates(gctx, volumes, pReplacements);
0222 
0223   // Return proto container
0224   DetectorComponent::PortalContainer dShell;
0225 
0226   // Update the portals of all volumes
0227   // Exchange the portals of the volumes
0228   for (auto& iv : volumes) {
0229     ACTS_VERBOSE("- update portals of volume '" << iv->name() << "'.");
0230     for (auto& [p, i, dir, boundaries, binning] : pReplacements) {
0231       // Fill the map
0232       dShell[i] = p;
0233       ACTS_VERBOSE("-- update portal with index " << i);
0234       iv->updatePortal(p, i);
0235     }
0236   }
0237   // Done.
0238 
0239   return dShell;
0240 }
0241 
0242 Acts::Experimental::DetectorComponent::PortalContainer
0243 Acts::Experimental::detail::CuboidalDetectorHelper::connect(
0244     const GeometryContext& /*gctx*/,
0245     const std::vector<DetectorComponent::PortalContainer>& containers,
0246     AxisDirection bValue, const std::vector<unsigned int>& /*unused */,
0247     Acts::Logging::Level logLevel) noexcept(false) {
0248   // The local logger
0249   ACTS_LOCAL_LOGGER(getDefaultLogger("CuboidalDetectorHelper", logLevel));
0250 
0251   ACTS_DEBUG("Connect " << containers.size() << " containers in "
0252                         << axisDirectionName(bValue) << ".");
0253 
0254   // Return the new container
0255   DetectorComponent::PortalContainer dShell;
0256 
0257   // The possible bin values
0258   std::array<AxisDirection, 3u> possibleValues = {
0259       AxisDirection::AxisX, AxisDirection::AxisY, AxisDirection::AxisZ};
0260   // And their associated portal sets, see above
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   // This is the picked set for refubishing
0266   auto [endIndex, startIndex] = portalSets[toUnderlying(bValue)];
0267 
0268   // Fusing along the connection direction (bValue)
0269   for (std::size_t ic = 1; ic < containers.size(); ++ic) {
0270     auto& formerContainer = containers[ic - 1];
0271     auto& currentContainer = containers[ic];
0272     // Check and throw exception
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   // Proto container refurbishment - outside
0305   dShell[startIndex] = containers.front().find(startIndex)->second;
0306   dShell[endIndex] = containers.back().find(endIndex)->second;
0307 
0308   // Create remaining outside shells now
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   // Done.
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   // The local logger
0330   ACTS_LOCAL_LOGGER(getDefaultLogger("CuboidalDetectorHelper", logLevel));
0331 
0332   // The return boundaries
0333   std::array<std::vector<double>, 3u> boundaries;
0334 
0335   // The map for collecting
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       // This will insert v with a value of 0 if it doesn't exist
0345       ++map[v];
0346     }
0347   };
0348 
0349   // Loop over the volumes and collect boundaries
0350   for (const auto& v : volumes) {
0351     if (v->volumeBounds().type() == Acts::VolumeBounds::BoundsType::eCuboid) {
0352       auto bValues = v->volumeBounds().values();
0353       // The min/max values
0354       double halfX = bValues[CuboidVolumeBounds::BoundValues::eHalfLengthX];
0355       double halfY = bValues[CuboidVolumeBounds::BoundValues::eHalfLengthY];
0356       double halfZ = bValues[CuboidVolumeBounds::BoundValues::eHalfLengthZ];
0357       // Get the transform @todo use a center of gravity of the detector
0358       auto translation = v->transform(gctx).translation();
0359       // The min/max values
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       // Fill the maps
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 }