From 635df4be8ee52182caa95c6f4c99384bde9a3a5d Mon Sep 17 00:00:00 2001 From: Andreas Salzburger Date: Fri, 21 Jul 2023 11:13:26 +0200 Subject: [PATCH 01/21] core examples tests --- Examples/Python/python/acts/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Examples/Python/python/acts/__init__.py b/Examples/Python/python/acts/__init__.py index 98c8af5929f..85d487de6f9 100644 --- a/Examples/Python/python/acts/__init__.py +++ b/Examples/Python/python/acts/__init__.py @@ -26,7 +26,6 @@ else: warnings.warn(error + "\nThe compile-time threshold will be used in this case!") - def Propagator(stepper, navigator): for prefix in ("Eigen", "Atlas", "StraightLine"): _stepper = getattr(ActsPythonBindings, f"{prefix}Stepper") From dac7644d60d45a857ab9ffebd692c3c7a63582e9 Mon Sep 17 00:00:00 2001 From: Andreas Salzburger Date: Fri, 21 Jul 2023 11:16:49 +0200 Subject: [PATCH 02/21] python formatting --- Examples/Python/python/acts/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Examples/Python/python/acts/__init__.py b/Examples/Python/python/acts/__init__.py index 85d487de6f9..98c8af5929f 100644 --- a/Examples/Python/python/acts/__init__.py +++ b/Examples/Python/python/acts/__init__.py @@ -26,6 +26,7 @@ else: warnings.warn(error + "\nThe compile-time threshold will be used in this case!") + def Propagator(stepper, navigator): for prefix in ("Eigen", "Atlas", "StraightLine"): _stepper = getattr(ActsPythonBindings, f"{prefix}Stepper") From 008df9c8b3c297259adcb0aebf7ed6b4bb0f0613 Mon Sep 17 00:00:00 2001 From: Andreas Salzburger Date: Thu, 30 Nov 2023 11:27:29 +0100 Subject: [PATCH 03/21] setting the fused portals also to the original keep volume --- .../detail/CylindricalDetectorHelper.cpp | 193 +++++++++++------- 1 file changed, 123 insertions(+), 70 deletions(-) diff --git a/Core/src/Detector/detail/CylindricalDetectorHelper.cpp b/Core/src/Detector/detail/CylindricalDetectorHelper.cpp index 99d675ec60e..6a5f6d97e4f 100644 --- a/Core/src/Detector/detail/CylindricalDetectorHelper.cpp +++ b/Core/src/Detector/detail/CylindricalDetectorHelper.cpp @@ -413,7 +413,7 @@ Acts::Experimental::detail::CylindricalDetectorHelper::connectInR( refValues[CylinderVolumeBounds::BoundValues::eHalfPhiSector]; ActsScalar avgPhi = refValues[CylinderVolumeBounds::BoundValues::eAveragePhi]; - // Fuse the cylinders - portals can be reused for this operation + // Fuse the cylinders for (unsigned int iv = 1; iv < volumes.size(); ++iv) { refValues = volumes[iv]->volumeBounds().values(); // Keep on collecting the outside maximum r for the overall r boundaries @@ -423,15 +423,12 @@ Acts::Experimental::detail::CylindricalDetectorHelper::connectInR( ACTS_VERBOSE("Connect volume '" << volumes[iv - 1]->name() << "' to " << volumes[iv]->name() << "'."); - // When fusing volumes at a cylinder boundary, we *keep* one - // portal and transfer the portal link information from the other - // - // In this case the outer cylinder portal of the inner volume is kept, - // the inner cylinder of the outer portal goes to waste - auto& keepCylinder = volumes[iv - 1]->portalPtrs()[2u]; - auto& wasteCylinder = volumes[iv]->portalPtrs()[3u]; - keepCylinder = Portal::fuse(keepCylinder, wasteCylinder); - volumes[iv]->updatePortal(keepCylinder, 3u); + // Fusing cylinders from inner and outer volume + auto innerCylinder = volumes[iv - 1]->portalPtrs()[2u]; + auto outerCylinder = volumes[iv]->portalPtrs()[3u]; + auto fusedCylinder = Portal::fuse(innerCylinder, outerCylinder); + volumes[iv - 1]->updatePortal(fusedCylinder, 2u); + volumes[iv]->updatePortal(fusedCylinder, 3u); } } @@ -583,30 +580,26 @@ Acts::Experimental::detail::CylindricalDetectorHelper::connectInZ( if (connectZ) { ACTS_VERBOSE("Connect volume '" << volumes[iv - 1]->name() << "' to " << volumes[iv]->name() << "'."); - // When fusing, one portal survives (keep) and gets the - // portal linking from the waste transferred - // - // In this case we keep the disc at positive z of the volume - // at lower relative z, and trash the disc at negative z of the - // following volume - auto& keepDisc = volumes[iv - 1]->portalPtrs()[1u]; - auto& wasteDisc = volumes[iv]->portalPtrs()[0u]; + // Fusing the discs: positive at lower z, negative at hgiher z + auto& pDisc = volumes[iv - 1]->portalPtrs()[1u]; + auto& nDisc = volumes[iv]->portalPtrs()[0u]; // Throw an exception if the discs are not at the same position - Vector3 keepPosition = keepDisc->surface().center(gctx); - Vector3 wastePosition = wasteDisc->surface().center(gctx); - if (!keepPosition.isApprox(wastePosition)) { + Vector3 pPosition = pDisc->surface().center(gctx); + Vector3 nPosition = nDisc->surface().center(gctx); + if (!pPosition.isApprox(nPosition)) { std::string message = "CylindricalDetectorHelper: '"; message += volumes[iv - 1]->name(); message += "' does not attach to '"; message += volumes[iv]->name(); message += "'\n"; message += " - along z with values "; - message += Acts::toString(keepPosition); - message += " / " + Acts::toString(wastePosition); + message += Acts::toString(pPosition); + message += " / " + Acts::toString(nPosition); throw std::runtime_error(message.c_str()); } - keepDisc = Portal::fuse(keepDisc, wasteDisc); - volumes[iv]->updatePortal(keepDisc, 0u); + auto fusedDisc = Portal::fuse(pDisc, nDisc); + volumes[iv - 1]->updatePortal(fusedDisc, 1u); + volumes[iv]->updatePortal(fusedDisc, 0u); } } @@ -758,16 +751,17 @@ Acts::Experimental::detail::CylindricalDetectorHelper::connectInPhi( phiBoundaries.push_back( refValues[CylinderVolumeBounds::BoundValues::eAveragePhi] + refValues[CylinderVolumeBounds::BoundValues::eHalfPhiSector]); - // Fuse the sectors - portals can be reused for this operation + // Fuse the sectors for (unsigned int iv = 1; iv < volumes.size(); ++iv) { ACTS_VERBOSE("Connect volume '" << volumes[iv - 1]->name() << "' to " << volumes[iv]->name() << "'."); - // Fuse and swap - auto& keepSector = volumes[iv - 1]->portalPtrs()[iSecOffset + 1u]; - auto& wasteSector = volumes[iv]->portalPtrs()[iSecOffset]; - keepSector = Portal::fuse(keepSector, wasteSector); - volumes[iv]->updatePortal(keepSector, iSecOffset); + // Fuse sector surfaces r handed at lower index, l handed at higher index + auto& rSector = volumes[iv - 1]->portalPtrs()[iSecOffset + 1u]; + auto& lSector = volumes[iv]->portalPtrs()[iSecOffset]; + auto fusedSector = Portal::fuse(rSector, lSector); + volumes[iv - 1]->updatePortal(fusedSector, iSecOffset + 1u); + volumes[iv]->updatePortal(fusedSector, iSecOffset); // The current values auto curValues = volumes[iv]->volumeBounds().values(); // Bail out if they do not match @@ -875,22 +869,25 @@ Acts::Experimental::detail::CylindricalDetectorHelper::wrapInZR( dShell[2u] = volumes[1u]->portalPtrs()[2u]; // Fuse outer cover of first with inner cylinder of wrapping volume - auto& keepCover = volumes[0u]->portalPtrs()[2u]; - auto& wasteCover = volumes[1u]->portalPtrs()[3u]; - keepCover = Portal::fuse(keepCover, wasteCover); - volumes[1u]->updatePortal(keepCover, 3u); + auto& outerCover = volumes[0u]->portalPtrs()[2u]; + auto& innerCover = volumes[1u]->portalPtrs()[3u]; + auto fusedCover = Portal::fuse(outerCover, innerCover); + volumes[0u]->updatePortal(fusedCover, 2u); + volumes[1u]->updatePortal(fusedCover, 3u); // Stitch sides - negative - auto& keepDiscN = volumes[1u]->portalPtrs()[4u]; - auto& wasteDiscN = volumes[0u]->portalPtrs()[0u]; - keepDiscN = Portal::fuse(keepDiscN, wasteDiscN); - volumes[0u]->updatePortal(keepDiscN, 0u); + auto& firstDiscN = volumes[1u]->portalPtrs()[4u]; + auto& secondDiscN = volumes[0u]->portalPtrs()[0u]; + auto fusedDiscN = Portal::fuse(firstDiscN, secondDiscN); + volumes[1u]->updatePortal(fusedDiscN, 4u); + volumes[0u]->updatePortal(fusedDiscN, 0u); // Stich sides - positive - auto& keepDiscP = volumes[0u]->portalPtrs()[1u]; - auto& wasteDiscP = volumes[1u]->portalPtrs()[5u]; - keepDiscP = Portal::fuse(keepDiscP, wasteDiscP); - volumes[1u]->updatePortal(keepDiscP, 5u); + auto& firstDiscP = volumes[0u]->portalPtrs()[1u]; + auto& secondDiscP = volumes[1u]->portalPtrs()[5u]; + auto fusedDiscP = Portal::fuse(firstDiscP, secondDiscP); + volumes[0u]->updatePortal(fusedDiscP, 1u); + volumes[1u]->updatePortal(fusedDiscP, 5u); // If needed, insert new cylinder if (volumes[0u]->portalPtrs().size() == 4u && @@ -960,13 +957,27 @@ Acts::Experimental::detail::CylindricalDetectorHelper::connectInR( "not be connected in R"); } - // Fuse and swap - std::shared_ptr keepCylinder = containers[ic - 1].find(2u)->second; - std::shared_ptr wasteCylinder = containers[ic].find(3u)->second; - keepCylinder = Portal::fuse(keepCylinder, wasteCylinder); - for (auto& av : wasteCylinder->attachedDetectorVolumes()[1u]) { - av->updatePortal(keepCylinder, 3u); - } + // Fuse containers, and update the attached volumes + std::shared_ptr innerCylinder = containers[ic - 1].find(2u)->second; + // Direction is explicitely up + auto innerAttachedVolumes = + innerCylinder + ->attachedDetectorVolumes()[Direction(Direction::Backward).index()]; + std::shared_ptr outerCylinder = containers[ic].find(3u)->second; + auto outerAttachedVolume = + outerCylinder + ->attachedDetectorVolumes()[Direction(Direction::Forward).index()]; + auto fusedCylinder = Portal::fuse(innerCylinder, outerCylinder); + + // Update the attached volumes with the new portal + std::for_each(innerAttachedVolumes.begin(), innerAttachedVolumes.end(), + [&](std::shared_ptr& av) { + av->updatePortal(fusedCylinder, 2u); + }); + std::for_each(outerAttachedVolume.begin(), outerAttachedVolume.end(), + [&](std::shared_ptr& av) { + av->updatePortal(fusedCylinder, 3u); + }); } // Proto container refurbishment @@ -1017,13 +1028,26 @@ Acts::Experimental::detail::CylindricalDetectorHelper::connectInZ( "CylindricalDetectorHelper: proto container has no positive disc, " "can not be connected in Z"); } - std::shared_ptr keepDisc = formerContainer.find(1u)->second; - std::shared_ptr wasteDisc = currentContainer.find(0u)->second; - keepDisc = Portal::fuse(keepDisc, wasteDisc); - for (auto& av : wasteDisc->attachedDetectorVolumes()[1u]) { - ACTS_VERBOSE("Update portal of detector volume '" << av->name() << "'."); - av->updatePortal(keepDisc, 0u); - } + // Container attachment positive Disc of lower, negative Disc at higher + std::shared_ptr pDisc = formerContainer.find(1u)->second; + auto pAttachedVolumes = + pDisc + ->attachedDetectorVolumes()[Direction(Direction::Backward).index()]; + + std::shared_ptr nDisc = currentContainer.find(0u)->second; + auto nAttachedVolumes = + nDisc->attachedDetectorVolumes()[Direction(Direction::Forward).index()]; + + auto fusedDisc = Portal::fuse(pDisc, nDisc); + + std::for_each(pAttachedVolumes.begin(), pAttachedVolumes.end(), + [&](std::shared_ptr& av) { + av->updatePortal(fusedDisc, 1u); + }); + std::for_each(nAttachedVolumes.begin(), nAttachedVolumes.end(), + [&](std::shared_ptr& av) { + av->updatePortal(fusedDisc, 0u); + }); } // Proto container refurbishment @@ -1119,22 +1143,51 @@ Acts::Experimental::detail::CylindricalDetectorHelper::wrapInZR( dShell[2u] = wrappingVolume->portalPtrs()[2u]; // Fuse outer cover of first with inner cylinder of wrapping volume - auto& keepCover = innerContainer[2u]; - auto& wasteCover = wrappingVolume->portalPtrs()[3u]; - keepCover = Portal::fuse(keepCover, wasteCover); - wrappingVolume->updatePortal(keepCover, 3u); + auto& innerCover = innerContainer[2u]; + auto innerAttachedVolumes = + innerCover + ->attachedDetectorVolumes()[Direction(Direction::Backward).index()]; + auto& innerTube = wrappingVolume->portalPtrs()[3u]; + auto fusedCover = Portal::fuse(innerCover, innerTube); + + std::for_each(innerAttachedVolumes.begin(), innerAttachedVolumes.end(), + [&](std::shared_ptr& av) { + av->updatePortal(fusedCover, 2u); + }); + wrappingVolume->updatePortal(fusedCover, 3u); // Stitch sides - negative - auto& keepDiscN = innerContainer[0u]; - auto& wasteDiscN = wrappingVolume->portalPtrs()[4u]; - keepDiscN = Portal::fuse(keepDiscN, wasteDiscN); - wrappingVolume->updatePortal(keepDiscN, 4u); + // positive disc of lower , negative disc of higher + auto& firstDiscN = innerContainer[0u]; - // Stich sides - positive - auto& keepDiscP = innerContainer[1u]; - auto& wasteDiscP = wrappingVolume->portalPtrs()[5u]; - keepDiscP = Portal::fuse(keepDiscP, wasteDiscP); - wrappingVolume->updatePortal(keepDiscP, 5u); + auto firstNAttachedVolumes = + firstDiscN + ->attachedDetectorVolumes()[Direction(Direction::Forward).index()]; + + auto& secondDiscN = wrappingVolume->portalPtrs()[4u]; + auto fusedDiscN = Portal::fuse(firstDiscN, secondDiscN); + + std::for_each(firstNAttachedVolumes.begin(), firstNAttachedVolumes.end(), + [&](std::shared_ptr& av) { + av->updatePortal(fusedDiscN, 0u); + }); + wrappingVolume->updatePortal(fusedDiscN, 4u); + + // Stich sides - positiv + auto& firstDiscP = innerContainer[1u]; + auto firstPAttachedVolumes = + firstDiscP + ->attachedDetectorVolumes()[Direction(Direction::Backward).index()]; + + auto& secondDiscP = wrappingVolume->portalPtrs()[5u]; + auto fusedDiscP = Portal::fuse(firstDiscP, secondDiscP); + + std::for_each(firstPAttachedVolumes.begin(), firstPAttachedVolumes.end(), + [&](std::shared_ptr& av) { + av->updatePortal(fusedDiscP, 1u); + }); + + wrappingVolume->updatePortal(fusedDiscP, 5u); // If inner stitching is necessary if (innerContainer.size() == 4u && From 8cc3adc89ab89a638a60e6b0271330e80ab455e9 Mon Sep 17 00:00:00 2001 From: Andreas Salzburger Date: Thu, 30 Nov 2023 11:35:39 +0100 Subject: [PATCH 04/21] fix spelling --- Core/src/Detector/detail/CylindricalDetectorHelper.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/src/Detector/detail/CylindricalDetectorHelper.cpp b/Core/src/Detector/detail/CylindricalDetectorHelper.cpp index 6a5f6d97e4f..4158d3e1a05 100644 --- a/Core/src/Detector/detail/CylindricalDetectorHelper.cpp +++ b/Core/src/Detector/detail/CylindricalDetectorHelper.cpp @@ -1173,7 +1173,7 @@ Acts::Experimental::detail::CylindricalDetectorHelper::wrapInZR( }); wrappingVolume->updatePortal(fusedDiscN, 4u); - // Stich sides - positiv + // Stich sides - positive auto& firstDiscP = innerContainer[1u]; auto firstPAttachedVolumes = firstDiscP From 64ca36005161a30641beac41688a774ad71f3bd5 Mon Sep 17 00:00:00 2001 From: Andreas Salzburger Date: Thu, 30 Nov 2023 13:24:22 +0100 Subject: [PATCH 05/21] adding infrastructure for cuboidal detectors --- .../detail/CuboidalDetectorHelper.hpp | 87 ++++ .../detail/DetectorVolumeConsistency.hpp | 58 +++ .../Acts/Detector/detail/PortalHelper.hpp | 19 + Core/src/Detector/CMakeLists.txt | 2 + .../detail/CuboidalDetectorHelper.cpp | 389 ++++++++++++++++++ .../detail/CylindricalDetectorHelper.cpp | 96 +---- .../detail/DetectorVolumeConsistency.cpp | 72 ++++ Core/src/Detector/detail/PortalHelper.cpp | 44 ++ Tests/UnitTests/Core/Detector/CMakeLists.txt | 1 + .../Detector/CuboidalDetectorHelperTests.cpp | 173 ++++++++ 10 files changed, 854 insertions(+), 87 deletions(-) create mode 100644 Core/include/Acts/Detector/detail/CuboidalDetectorHelper.hpp create mode 100644 Core/include/Acts/Detector/detail/DetectorVolumeConsistency.hpp create mode 100644 Core/src/Detector/detail/CuboidalDetectorHelper.cpp create mode 100644 Core/src/Detector/detail/DetectorVolumeConsistency.cpp create mode 100644 Tests/UnitTests/Core/Detector/CuboidalDetectorHelperTests.cpp diff --git a/Core/include/Acts/Detector/detail/CuboidalDetectorHelper.hpp b/Core/include/Acts/Detector/detail/CuboidalDetectorHelper.hpp new file mode 100644 index 00000000000..ec8e8e9d99f --- /dev/null +++ b/Core/include/Acts/Detector/detail/CuboidalDetectorHelper.hpp @@ -0,0 +1,87 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/Definitions/Algebra.hpp" +#include "Acts/Definitions/Common.hpp" +#include "Acts/Detector/DetectorComponents.hpp" +#include "Acts/Detector/Portal.hpp" +#include "Acts/Geometry/GeometryContext.hpp" +#include "Acts/Utilities/BinningData.hpp" +#include "Acts/Utilities/Logger.hpp" + +#include +#include +#include +#include +#include + +namespace Acts { + +namespace Experimental { + +class DetectorVolume; +class Portal; + +namespace detail { +namespace CuboidalDetectorHelper { + +/// @brief Connect detector volumes given a binning value +/// +/// @param gctx The geometry context +/// @param volumes the volumes +/// @param bValue the binning value (allowed are binX, binY, binZ) +/// @param selectedOnly switch only selected boundaries +/// @param logLevel is the screen logging level +/// +/// @note a fair amount of consistency checking is done, +/// and exceptions are thrown if any of the tests fail +/// +/// @return a proto container with the outside portals +DetectorComponent::PortalContainer connect( + const GeometryContext& gctx, + std::vector>& volumes, BinningValue bValue, + const std::vector& selectedOnly = {}, + Acts::Logging::Level logLevel = Acts::Logging::INFO); + +/// @brief Connect containers given a binning value +/// +/// @param gctx The geometry context +/// @param containers the containers +/// @param bValue the binning value (allowed are binX, binY, binZ) +/// @param selectedOnly switch only selected boundaries +/// @param logLevel is the screen logging level +/// +/// @note not much checking is done anymore, as the DetectorComponent::PortalContainer +/// are assumed to come properly formed from the prior methods +/// +/// @return a proto container with the outside portals +DetectorComponent::PortalContainer connect( + const GeometryContext& gctx, + const std::vector& containers, + BinningValue bValue, const std::vector& selectedOnly = {}, + Acts::Logging::Level logLevel = Acts::Logging::INFO); + +/// @brief Helper method to extract r,z,phi boundaries for +/// eventual grid volume search +/// +/// @param gctx the geometry context of the call +/// @param volumes the volumes at input +/// @param logLevel is the screen logging level +/// +/// @return extracted boundary values +std::array, 3u> xyzBoundaries( + const GeometryContext& gctx, + const std::vector& volumes, + Acts::Logging::Level logLevel = Acts::Logging::INFO); + +} // namespace CuboidalDetectorHelper +} // namespace detail +} // namespace Experimental +} // namespace Acts \ No newline at end of file diff --git a/Core/include/Acts/Detector/detail/DetectorVolumeConsistency.hpp b/Core/include/Acts/Detector/detail/DetectorVolumeConsistency.hpp new file mode 100644 index 00000000000..89264d9d955 --- /dev/null +++ b/Core/include/Acts/Detector/detail/DetectorVolumeConsistency.hpp @@ -0,0 +1,58 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/Definitions/Common.hpp" +#include "Acts/Geometry/GeometryContext.hpp" +#include "Acts/Utilities/BinningData.hpp" + +#include +#include + +namespace Acts { +namespace Experimental { + +class DetectorVolume; + +namespace detail { +namespace DetectorVolumeConsistency { + +/// @brief Helper method to check alignment of the volumes, this method checks +/// if the rotational part of the transform is identical +/// +/// @param gctx the geometry context +/// @param volumes the input volumes to be checked +/// +/// @note this is a strict matching that requires the rotation to be identical +/// +/// @note throws exception if any of checks fails +void checkRotationAlignment( + const GeometryContext& gctx, + const std::vector>& volumes); + +/// @brief Helper method to check whether a set of volumes is lined up on +/// a given common axis definition +/// +/// @param gctx the geometry context +/// @param volumes the input volumes to be checked +/// @param axisValue the alignment axist +/// +/// @note this will call checkRotation Alginemtn first +/// @note throws exception if the volumes are not ordered +/// +/// @return a vector with position differences (ordered) +std::vector checkCenterAlignment( + const GeometryContext& gctx, + const std::vector>& volumes, + BinningValue axisValue); + +} // namespace DetectorVolumeConsistency +} // namespace detail +} // namespace Experimental +} // namespace Acts diff --git a/Core/include/Acts/Detector/detail/PortalHelper.hpp b/Core/include/Acts/Detector/detail/PortalHelper.hpp index 45a9c2a4013..16f26064d2a 100644 --- a/Core/include/Acts/Detector/detail/PortalHelper.hpp +++ b/Core/include/Acts/Detector/detail/PortalHelper.hpp @@ -15,6 +15,7 @@ #include "Acts/Geometry/GeometryContext.hpp" #include "Acts/Utilities/BinningType.hpp" +#include #include #include #include @@ -89,6 +90,24 @@ void attachDetectorVolumeUpdaters( std::vector> attachedDetectorVolumes( Portal& portal) noexcept(false); +/// @brief Method that strips out attached volumes from portals and +/// provides them back to the caller. +/// +/// @param pContainers the portal containers to be resolved +/// @param sides the sides to be handled +/// @param selectedOnly the selected only volumes, e.g. for complex containers +/// to chose only outisde skins, +/// @param logLevel the logging level +/// +std::map>> +stripSideVolumes( + const std::vector>> + pContainers, + const std::vector& sides, + const std::vector& selectedOnly = {}, + Acts::Logging::Level logLevel = Acts::Logging::INFO); + } // namespace PortalHelper } // namespace detail } // namespace Experimental diff --git a/Core/src/Detector/CMakeLists.txt b/Core/src/Detector/CMakeLists.txt index 3a381dccb9f..649339268e9 100644 --- a/Core/src/Detector/CMakeLists.txt +++ b/Core/src/Detector/CMakeLists.txt @@ -3,7 +3,9 @@ target_sources( PRIVATE detail/BlueprintHelper.cpp detail/BlueprintDrawer.cpp + detail/CuboidalDetectorHelper.cpp detail/CylindricalDetectorHelper.cpp + detail/DetectorVolumeConsistency.cpp detail/PortalHelper.cpp detail/SupportHelper.cpp detail/IndexedGridFiller.cpp diff --git a/Core/src/Detector/detail/CuboidalDetectorHelper.cpp b/Core/src/Detector/detail/CuboidalDetectorHelper.cpp new file mode 100644 index 00000000000..244f0065d44 --- /dev/null +++ b/Core/src/Detector/detail/CuboidalDetectorHelper.cpp @@ -0,0 +1,389 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "Acts/Detector/detail/CuboidalDetectorHelper.hpp" + +#include "Acts/Definitions/Common.hpp" +#include "Acts/Detector/DetectorVolume.hpp" +#include "Acts/Detector/Portal.hpp" +#include "Acts/Detector/detail/DetectorVolumeConsistency.hpp" +#include "Acts/Detector/detail/PortalHelper.hpp" +#include "Acts/Geometry/CuboidVolumeBounds.hpp" +#include "Acts/Surfaces/PlaneSurface.hpp" +#include "Acts/Surfaces/RectangleBounds.hpp" +#include "Acts/Surfaces/Surface.hpp" +#include "Acts/Utilities/BinningData.hpp" +#include "Acts/Utilities/Enumerate.hpp" +#include "Acts/Utilities/StringHelpers.hpp" + +#include + +Acts::Experimental::DetectorComponent::PortalContainer +Acts::Experimental::detail::CuboidalDetectorHelper::connect( + const GeometryContext& gctx, + std::vector>& volumes, + BinningValue bValue, const std::vector& selectedOnly, + Acts::Logging::Level logLevel) { + ACTS_LOCAL_LOGGER(getDefaultLogger("CuboidalDetectorHelper", logLevel)); + + ACTS_DEBUG("Connect " << volumes.size() << " detector volumes in " + << binningValueNames()[bValue] << "."); + + // Check transform for consistency + auto centerDistances = + DetectorVolumeConsistency::checkCenterAlignment(gctx, volumes, bValue); + + // Assign the portal indices according to the volume bounds definition + std::array possibleValues = {binX, binY, binZ}; + // 1 -> [ 2,3 ] for binX connection (cylclic one step) + // 2 -> [ 4,5 ] for binY connection (cylclic two steps) + // 0 -> [ 0,1 ] for binZ connection (to be in line with cylinder covnention) + using PortalSet = std::array; + std::vector portalSets = { + {PortalSet{2, 3}, PortalSet{4, 5}, PortalSet{0, 1}}}; + + // This is the picked set for fusing + auto [sIndex, fIndex] = portalSets[bValue]; + + // Log the merge splits, i.e. the boundaries of the volumes + std::array, 3u> mergeSplits; + std::array mergeHalfLengths = { + 0., + 0., + 0., + }; + + // Pick the counter part value + auto counterPart = [&](BinningValue mValue) -> BinningValue { + for (auto cValue : possibleValues) { + if (cValue != mValue and cValue != bValue) { + return cValue; + } + } + return mValue; + }; + + // Things that can be done without a loop be first/last check + // Estimate the merge parameters: the scalar and the transform + using MergeParameters = std::tuple; + std::map mergeParameters; + auto& firstVolume = volumes.front(); + auto& lastVolume = volumes.back(); + // Values + const auto firstBoundValues = firstVolume->volumeBounds().values(); + const auto lastBoundValues = lastVolume->volumeBounds().values(); + Vector3 stepDirection = firstVolume->transform(gctx).rotation().col(bValue); + + for (auto [im, mergeValue] : enumerate(possibleValues)) { + // Skip the bin value itself, fusing will took care of that + if (mergeValue == bValue) { + continue; + } + for (auto [is, index] : enumerate(portalSets[mergeValue])) { + // Take rotation from first volume + auto rotation = firstVolume->portalPtrs()[index] + ->surface() + .transform(gctx) + .rotation(); + ActsScalar stepDown = firstBoundValues[bValue]; + ActsScalar stepUp = lastBoundValues[bValue]; + // Take translation from first and last volume + auto translationF = firstVolume->portalPtrs()[index] + ->surface() + .transform(gctx) + .translation(); + + auto translationL = lastVolume->portalPtrs()[index] + ->surface() + .transform(gctx) + .translation(); + + Vector3 translation = 0.5 * (translationF - stepDown * stepDirection + + translationL + stepUp * stepDirection); + + Transform3 portalTransform = Transform3::Identity(); + portalTransform.prerotate(rotation); + portalTransform.pretranslate(translation); + // The half length to be kept + ActsScalar keepHalfLength = firstBoundValues[counterPart(mergeValue)]; + mergeParameters[index] = MergeParameters(keepHalfLength, portalTransform); + } + } + + // Loop over the volumes and fuse the portals, collect the merge information + for (auto [iv, v] : enumerate(volumes)) { + // So far works only in a cubioid setup + if (v->volumeBounds().type() != VolumeBounds::BoundsType::eCuboid) { + throw std::invalid_argument( + "CuboidalDetectorHelper: volume bounds are not cuboid"); + } + + // Loop to fuse the portals along the connection direction (bValue) + if (iv > 0u) { + ACTS_VERBOSE("- fuse portals of volume '" + << volumes[iv - 1]->name() << "' with volume '" << v->name() + << "'."); + ACTS_VERBOSE("-- indices " << fIndex << " of first, " << sIndex + << " of second volume."); + // Fusing the portals of the current volume with the previous one + auto fPortal = volumes[iv - 1]->portalPtrs()[fIndex]; + auto sPortal = v->portalPtrs()[sIndex]; + auto fusedPortal = Portal::fuse(fPortal, sPortal); + volumes[iv - 1]->updatePortal(fusedPortal, fIndex); + v->updatePortal(fusedPortal, sIndex); + } + + // Get the bound values + auto boundValues = v->volumeBounds().values(); + // Loop to determine the merge bounds, the new transform + for (auto [im, mergeValue] : enumerate(possibleValues)) { + // Skip the bin value itself, fusing will took care of that + if (mergeValue == bValue) { + continue; + } + // Record the merge splits + mergeSplits[im].push_back(2 * boundValues[bValue]); + mergeHalfLengths[im] += boundValues[bValue]; + } + } + + // Loop to create the new portals as portal replacements + std::vector pReplacements; + for (auto [im, mergeValue] : enumerate(possibleValues)) { + // Skip the bin value itself, fusing took care of that + if (mergeValue == bValue) { + continue; + } + + // Create the new RecangleBounds + // - there are conventions involved, regarding the bounds orientation + // - This is an anticyclic swap + bool mergedInX = true; + switch (bValue) { + case binZ: { + mergedInX = (mergeValue == binY); + } break; + case binY: { + mergedInX = (mergeValue == binX); + } break; + case binX: { + mergedInX = (mergeValue == binZ); + } break; + default: + break; + } + + // The stitch boundarieS for portal pointing + std::vector stitchBoundaries; + stitchBoundaries.push_back(-mergeHalfLengths[im]); + for (auto step : mergeSplits[im]) { + stitchBoundaries.push_back(stitchBoundaries.back() + step); + } + + for (auto [is, index] : enumerate(portalSets[mergeValue])) { + // Check if you need to skip due to selections + if (not selectedOnly.empty() and + std::find(selectedOnly.begin(), selectedOnly.end(), index) == + selectedOnly.end()) { + continue; + } + + auto [keepHalfLength, portalTransform] = mergeParameters[index]; + std::shared_ptr portalBounds = + mergedInX ? std::make_shared(mergeHalfLengths[im], + keepHalfLength) + : std::make_shared(keepHalfLength, + mergeHalfLengths[im]); + auto portalSurface = + Surface::makeShared(portalTransform, portalBounds); + auto portal = std::make_shared(portalSurface); + // Make the stitch boundaries + pReplacements.push_back( + PortalReplacement(portal, index, Direction::Backward, + stitchBoundaries, (mergedInX ? binX : binY))); + } + } + // Return proto container + DetectorComponent::PortalContainer dShell; + + // Update the portals of all volumes + // Exchange the portals of the volumes + for (auto& iv : volumes) { + ACTS_VERBOSE("- update portals of volume '" << iv->name() << "'."); + for (auto& [p, i, dir, boundaries, binning] : pReplacements) { + // Fill the map + dShell[i] = p; + ACTS_VERBOSE("-- update portal with index " << i); + iv->updatePortal(p, i); + } + } + // Done. + + return dShell; +} + +Acts::Experimental::DetectorComponent::PortalContainer +Acts::Experimental::detail::CuboidalDetectorHelper::connect( + const GeometryContext& gctx, + const std::vector& containers, + BinningValue bValue, const std::vector& selectedOnly, + Acts::Logging::Level logLevel) noexcept(false) { + // The local logger + ACTS_LOCAL_LOGGER(getDefaultLogger("CuboidalDetectorHelper", logLevel)); + + ACTS_DEBUG("Connect " << containers.size() << " containers in " + << binningValueNames()[bValue] << "."); + + // Return the new container + DetectorComponent::PortalContainer dShell; + + // The possible bin values + std::array possibleValues = {binX, binY, binZ}; + // And their associated portal sets, see above + using PortalSet = std::array; + std::vector portalSets = { + {PortalSet{2, 3}, PortalSet{4, 5}, PortalSet{0, 1}}}; + + // This is the picked set for refubishing + auto [endIndex, startIndex] = portalSets[bValue]; + + // Fusing along the connection direction (bValue) + for (std::size_t ic = 1; ic < containers.size(); ++ic) { + auto& formerContainer = containers[ic - 1]; + auto& currentContainer = containers[ic]; + // Check and throw exception + if (formerContainer.find(startIndex) == formerContainer.end()) { + throw std::invalid_argument( + "CuboidalDetectorHelper: proto container has no fuse portal at index " + "of former container."); + } + if (currentContainer.find(endIndex) == currentContainer.end()) { + throw std::invalid_argument( + "CuboidalDetectorHelper: proto container has no fuse portal at index " + "of current container."); + } + + std::shared_ptr sPortal = formerContainer.find(startIndex)->second; + auto sAttachedVolumes = + sPortal + ->attachedDetectorVolumes()[Direction(Direction::Backward).index()]; + + std::shared_ptr ePortal = currentContainer.find(endIndex)->second; + auto eAttachedVolumes = + ePortal + ->attachedDetectorVolumes()[Direction(Direction::Forward).index()]; + + auto fusedPortal = Portal::fuse(sPortal, ePortal); + + for (auto& av : sAttachedVolumes) { + ACTS_VERBOSE("Update portal of detector volume '" << av->name() << "'."); + av->updatePortal(fusedPortal, startIndex); + } + + for (auto& av : eAttachedVolumes) { + ACTS_VERBOSE("Update portal of detector volume '" << av->name() << "'."); + av->updatePortal(fusedPortal, endIndex); + } + } + // Proto container refurbishment - outside + dShell[startIndex] = containers.front().find(startIndex)->second; + dShell[endIndex] = containers.back().find(endIndex)->second; + + // Create remaining outside shells now + std::vector sidePortals = {}; + for (auto sVals : possibleValues) { + if (sVals != bValue) { + sidePortals.push_back(portalSets[sVals][0]); + sidePortals.push_back(portalSets[sVals][1]); + } + } + + // Strip the side volumes + auto sideVolumes = + PortalHelper::stripSideVolumes(containers, sidePortals, selectedOnly); + + ACTS_VERBOSE("There remain " << sideVolumes.size() + << " side volume packs to be connected"); + for (auto [s, volumes] : sideVolumes) { + ACTS_VERBOSE(" - connect " << volumes.size() << " at selected side " << s); + auto pR = connect(gctx, volumes, bValue, {s}, logLevel); + if (pR.find(s) != pR.end()) { + dShell[s] = pR.find(s)->second; + } + } + + // Done. + return dShell; +} + +std::array, 3u> +Acts::Experimental::detail::CuboidalDetectorHelper::xyzBoundaries( + [[maybe_unused]] const GeometryContext& gctx, + [[maybe_unused]] const std::vector< + const Acts::Experimental::DetectorVolume*>& volumes, + Acts::Logging::Level logLevel) { + // The local logger + ACTS_LOCAL_LOGGER(getDefaultLogger("CuboidalDetectorHelper", logLevel)); + + // The return boundaries + std::array, 3u> boundaries; + + // The map for collecting + std::array, 3u> valueMaps; + auto& xMap = valueMaps[0u]; + auto& yMap = valueMaps[1u]; + auto& zMap = valueMaps[2u]; + + auto fillMap = [&](std::map& map, + const std::array& values) { + for (auto v : values) { + if (map.find(v) != map.end()) { + ++map[v]; + } else { + map[v] = 1u; + } + } + }; + + // Loop over the volumes and collect boundaries + for (const auto& v : volumes) { + if (v->volumeBounds().type() == Acts::VolumeBounds::BoundsType::eCuboid) { + auto bValues = v->volumeBounds().values(); + // The min/max values + ActsScalar halfX = bValues[CuboidVolumeBounds::BoundValues::eHalfLengthX]; + ActsScalar halfY = bValues[CuboidVolumeBounds::BoundValues::eHalfLengthY]; + ActsScalar halfZ = bValues[CuboidVolumeBounds::BoundValues::eHalfLengthZ]; + // Get the transform @todo use a center of gravity of the detector + auto translation = v->transform(gctx).translation(); + // The min/max values + ActsScalar xMin = translation.x() - halfX; + ActsScalar xMax = translation.x() + halfX; + ActsScalar yMin = translation.y() - halfY; + ActsScalar yMax = translation.y() + halfY; + ActsScalar zMin = translation.z() - halfZ; + ActsScalar zMax = translation.z() + halfZ; + // Fill the maps + fillMap(xMap, {xMin, xMax}); + fillMap(yMap, {yMin, yMax}); + fillMap(zMap, {zMin, zMax}); + } + } + + for (auto [im, map] : enumerate(valueMaps)) { + for (auto [key, value] : map) { + boundaries[im].push_back(key); + } + std::sort(boundaries[im].begin(), boundaries[im].end()); + } + + ACTS_VERBOSE("- did yield " << boundaries[0u].size() << " boundaries in X."); + ACTS_VERBOSE("- did yield " << boundaries[1u].size() << " boundaries in Y."); + ACTS_VERBOSE("- did yield " << boundaries[2u].size() << " boundaries in Z."); + + return boundaries; +} \ No newline at end of file diff --git a/Core/src/Detector/detail/CylindricalDetectorHelper.cpp b/Core/src/Detector/detail/CylindricalDetectorHelper.cpp index 4158d3e1a05..8d56cd04b9a 100644 --- a/Core/src/Detector/detail/CylindricalDetectorHelper.cpp +++ b/Core/src/Detector/detail/CylindricalDetectorHelper.cpp @@ -12,6 +12,7 @@ #include "Acts/Definitions/Tolerance.hpp" #include "Acts/Detector/DetectorVolume.hpp" #include "Acts/Detector/Portal.hpp" +#include "Acts/Detector/detail/DetectorVolumeConsistency.hpp" #include "Acts/Detector/detail/PortalHelper.hpp" #include "Acts/Geometry/CutoutCylinderVolumeBounds.hpp" #include "Acts/Surfaces/CylinderBounds.hpp" @@ -205,87 +206,6 @@ Acts::Experimental::PortalReplacement createSectorReplacement( return pRep; } -/// @brief Helper method to strip side volumes from containers -/// -/// @param containers the list of container -/// @param sides the sides -/// @param selectedOnly the selection restriction -/// -/// @return a map of stripped out container -std::map>> -stripSideVolumes( - const std::vector& - containers, - const std::vector& sides, - const std::vector& selectedOnly = {}, - Acts::Logging::Level logLevel = Acts::Logging::INFO) { - ACTS_LOCAL_LOGGER(Acts::getDefaultLogger("::stripSideVolumes", logLevel)); - - // These are the stripped off outside volumes - std::map>> - sideVolumes; - - // Principle sides and selected sides, make an intersection - std::vector selectedSides; - if (!selectedOnly.empty()) { - std::set_intersection(sides.begin(), sides.end(), selectedOnly.begin(), - selectedOnly.end(), - std::back_inserter(selectedSides)); - } else { - selectedSides = sides; - } - - // Loop through the containers - for (const auto& pc : containers) { - // Loop through the selected sides and check if they are contained - for (const auto& s : selectedSides) { - auto cSide = pc.find(s); - if (cSide != pc.end()) { - auto p = cSide->second; - auto& sVolumes = sideVolumes[s]; - auto aVolumes = - Acts::Experimental::detail::PortalHelper::attachedDetectorVolumes( - *p); - sVolumes.insert(sVolumes.end(), aVolumes.begin(), aVolumes.end()); - } - } - } - // return them - return sideVolumes; -} - -/// @brief Helper method to check alignment of the volumes, this method checks -/// if the z-axes are aligned and throws an exception if not, it assumes that -/// the detector volume content has been checked already -/// -/// @param gctx the geometry context -/// @param volumes the input volumes to be checked -/// -/// @note this is a strict matching that requires the rotation to be identical -/// -/// @note throws exception if any of checks fails -void checkAlignment( - const Acts::GeometryContext& gctx, - const std::vector>& - volumes) { - // Take first transform as reference transform - auto refRotation = volumes[0u]->transform(gctx).rotation(); - // Loop over rest and recursively test - for (auto [iv, v] : Acts::enumerate(volumes)) { - if (iv > 0) { - auto curRotation = v->transform(gctx).rotation(); - if (!curRotation.isApprox(refRotation)) { - std::string message = "CylindricalDetectorHelper: rotation of volume "; - message += std::to_string(iv); - message += std::string(" is not aligned with previous volume"); - throw std::invalid_argument(message.c_str()); - } - } - } -} - /// @brief Helper method to check the volumes in general and throw and exception if fails /// /// @param gctx the geometry context @@ -322,7 +242,8 @@ void checkVolumes( } } // Check the alignment of the volumes - checkAlignment(gctx, volumes); + Acts::Experimental::detail::DetectorVolumeConsistency::checkRotationAlignment( + gctx, volumes); } /// @brief Helper method to check the volume bounds @@ -986,8 +907,8 @@ Acts::Experimental::detail::CylindricalDetectorHelper::connectInR( } dShell[2u] = containers[containers.size() - 1u].find(2u)->second; - auto sideVolumes = - stripSideVolumes(containers, {0u, 1u, 4u, 5u}, selectedOnly, logLevel); + auto sideVolumes = PortalHelper::stripSideVolumes( + containers, {0u, 1u, 4u, 5u}, selectedOnly, logLevel); for (auto [s, volumes] : sideVolumes) { auto pR = connectInR(gctx, volumes, {s}); @@ -1061,8 +982,8 @@ Acts::Experimental::detail::CylindricalDetectorHelper::connectInZ( } // Strip the side volumes - auto sideVolumes = - stripSideVolumes(containers, nominalSides, selectedOnly, logLevel); + auto sideVolumes = PortalHelper::stripSideVolumes(containers, nominalSides, + selectedOnly, logLevel); ACTS_VERBOSE("There remain " << sideVolumes.size() << " side volume packs to be connected"); @@ -1207,7 +1128,8 @@ Acts::Experimental::detail::CylindricalDetectorHelper::wrapInZR( ActsScalar pHalfLengthZ = pValues[CylinderBounds::BoundValues::eHalfLengthZ]; - auto sideVolumes = stripSideVolumes({innerContainer}, {3u}, {3u}, logLevel); + auto sideVolumes = + PortalHelper::stripSideVolumes({innerContainer}, {3u}, {3u}, logLevel); // First the left volume sector std::vector> innerVolumes = { diff --git a/Core/src/Detector/detail/DetectorVolumeConsistency.cpp b/Core/src/Detector/detail/DetectorVolumeConsistency.cpp new file mode 100644 index 00000000000..8c53cc893c5 --- /dev/null +++ b/Core/src/Detector/detail/DetectorVolumeConsistency.cpp @@ -0,0 +1,72 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "Acts/Detector/detail/DetectorVolumeConsistency.hpp" + +#include "Acts/Definitions/Algebra.hpp" +#include "Acts/Detector/DetectorVolume.hpp" + +#include + +void Acts::Experimental::detail::DetectorVolumeConsistency:: + checkRotationAlignment( + const GeometryContext& gctx, + const std::vector>& + volumes) { + // Take first transform as reference transform + auto refRotation = volumes[0u]->transform(gctx).rotation(); + // Loop over rest and recursively test + for (auto [iv, v] : Acts::enumerate(volumes)) { + if (iv > 0) { + auto curRotation = v->transform(gctx).rotation(); + if (not curRotation.isApprox(refRotation)) { + std::string message = "ConsitencyChecker: rotation of volume "; + message += std::to_string(iv); + message += std::string(" is not aligned with previous volume"); + throw std::invalid_argument(message.c_str()); + } + } + } +} + +std::vector +Acts::Experimental::detail::DetectorVolumeConsistency::checkCenterAlignment( + const GeometryContext& gctx, + const std::vector>& volumes, + BinningValue axisValue) { + std::vector distances = {}; + // First it needs to surfive the rotation check + checkRotationAlignment(gctx, volumes); + + // Get the reference axis + auto refAxis = volumes[0u]->transform(gctx).rotation().col(axisValue); + + for (auto [iv, v] : Acts::enumerate(volumes)) { + if (iv > 0) { + Vector3 lastCenter = volumes[iv - 1]->transform(gctx).translation(); + Vector3 curCenter = v->transform(gctx).translation(); + Vector3 diff = curCenter - lastCenter; + // Check if the difference is aligned with the reference axis + if (not diff.normalized().isApprox(refAxis)) { + std::string message = "ConsitencyChecker: center of volume "; + message += std::to_string(iv); + message += std::string(" is not aligned with previous volume"); + throw std::invalid_argument(message.c_str()); + } + // Check if the projection is positive + if (diff.dot(refAxis) < 0) { + std::string message = "ConsitencyChecker: center of volume "; + message += std::to_string(iv); + message += std::string(" is not ordered with previous volume"); + throw std::invalid_argument(message.c_str()); + } + distances.push_back(diff.norm()); + } + } + return distances; +} \ No newline at end of file diff --git a/Core/src/Detector/detail/PortalHelper.cpp b/Core/src/Detector/detail/PortalHelper.cpp index d6e4c951b03..f0a860b2de8 100644 --- a/Core/src/Detector/detail/PortalHelper.cpp +++ b/Core/src/Detector/detail/PortalHelper.cpp @@ -83,3 +83,47 @@ Acts::Experimental::detail::PortalHelper::attachedDetectorVolumes( unsigned int iu = attachedVolumes[0u].empty() ? 1u : 0u; return attachedVolumes[iu]; } + +std::map>> +Acts::Experimental::detail::PortalHelper::stripSideVolumes( + const std::vector>> + pContainers, + const std::vector& sides, + const std::vector& selectedOnly, + Acts::Logging::Level logLevel) { + ACTS_LOCAL_LOGGER(Acts::getDefaultLogger("::stripSideVolumes", logLevel)); + + // These are the stripped off outside volumes + std::map>> + sideVolumes; + + // Principle sides and selected sides, make an intersection + std::vector selectedSides; + if (!selectedOnly.empty()) { + std::set_intersection(sides.begin(), sides.end(), selectedOnly.begin(), + selectedOnly.end(), + std::back_inserter(selectedSides)); + } else { + selectedSides = sides; + } + + // Loop through the containers + for (const auto& pc : pContainers) { + // Loop through the selected sides and check if they are contained + for (const auto& s : selectedSides) { + auto cSide = pc.find(s); + if (cSide != pc.end()) { + auto p = cSide->second; + auto& sVolumes = sideVolumes[s]; + auto aVolumes = + Acts::Experimental::detail::PortalHelper::attachedDetectorVolumes( + *p); + sVolumes.insert(sVolumes.end(), aVolumes.begin(), aVolumes.end()); + } + } + } + // return them + return sideVolumes; +} diff --git a/Tests/UnitTests/Core/Detector/CMakeLists.txt b/Tests/UnitTests/Core/Detector/CMakeLists.txt index b5733893b67..9a6060cbaa4 100644 --- a/Tests/UnitTests/Core/Detector/CMakeLists.txt +++ b/Tests/UnitTests/Core/Detector/CMakeLists.txt @@ -2,6 +2,7 @@ add_unittest(Blueprint BlueprintTests.cpp) add_unittest(BlueprintHelper BlueprintHelperTests.cpp) add_unittest(CylindricalContainerBuilder CylindricalContainerBuilderTests.cpp) add_unittest(CylindricalDetectorFromBlueprint CylindricalDetectorFromBlueprintTests.cpp) +add_unittest(CuboidalDetectorHelper CuboidalDetectorHelperTests.cpp) add_unittest(CylindricalDetectorHelper CylindricalDetectorHelperTests.cpp) add_unittest(GridAxisGenerators GridAxisGeneratorsTests.cpp) add_unittest(Detector DetectorTests.cpp) diff --git a/Tests/UnitTests/Core/Detector/CuboidalDetectorHelperTests.cpp b/Tests/UnitTests/Core/Detector/CuboidalDetectorHelperTests.cpp new file mode 100644 index 00000000000..6cdc60a47cc --- /dev/null +++ b/Tests/UnitTests/Core/Detector/CuboidalDetectorHelperTests.cpp @@ -0,0 +1,173 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2022 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include + +#include "Acts/Definitions/Algebra.hpp" +#include "Acts/Detector/DetectorVolume.hpp" +#include "Acts/Detector/PortalGenerators.hpp" +#include "Acts/Detector/detail/CuboidalDetectorHelper.hpp" +#include "Acts/Geometry/CuboidVolumeBounds.hpp" +#include "Acts/Geometry/GeometryContext.hpp" +#include "Acts/Navigation/SurfaceCandidatesUpdaters.hpp" +#include "Acts/Utilities/BinningData.hpp" +#include "Acts/Utilities/StringHelpers.hpp" +#include "Acts/Visualization/GeometryView3D.hpp" +#include "Acts/Visualization/ObjVisualization3D.hpp" + +#include +#include +#include + +auto portalGenerator = Acts::Experimental::defaultPortalGenerator(); +auto tContext = Acts::GeometryContext(); + +BOOST_AUTO_TEST_SUITE(Experimental) + +BOOST_AUTO_TEST_CASE(CubicVolumeExceptions) { + // A perfect box shape + auto box = std::make_shared(10, 10, 10); + + // Create volume A + auto volumeA = Acts::Experimental::DetectorVolumeFactory::construct( + portalGenerator, tContext, "VolumeA", Acts::Transform3::Identity(), box, + Acts::Experimental::tryAllPortals()); + + // Create volume B + auto transformB = Acts::Transform3::Identity(); + transformB.prerotate(Acts::AngleAxis3(0.234, Acts::Vector3::UnitZ())); + + auto volumeB = Acts::Experimental::DetectorVolumeFactory::construct( + portalGenerator, tContext, "volumeB", transformB, box, + Acts::Experimental::tryAllPortals()); + // Build the container + std::vector> volumes = { + volumeA, volumeB}; + + BOOST_CHECK_THROW( + Acts::Experimental::detail::CuboidalDetectorHelper::connect( + tContext, volumes, Acts::binX, {}, Acts::Logging::VERBOSE), + std::invalid_argument); +} + +BOOST_AUTO_TEST_CASE(SimpleBoxConnection) { + std::array binningValues = {Acts::binX, Acts::binY, + Acts::binZ}; + for (auto bVal : binningValues) { + // A perfect box shape + auto box = std::make_shared(10, 10, 10); + + // Create volume A + auto volumeA = Acts::Experimental::DetectorVolumeFactory::construct( + portalGenerator, tContext, "VolumeA", Acts::Transform3::Identity(), box, + Acts::Experimental::tryAllPortals()); + + // Move it into the bval direction + auto transformB = Acts::Transform3::Identity(); + + Acts::Vector3 translation = Acts::Vector3::Zero(); + translation[bVal] = 20; + transformB.pretranslate(translation); + // Create volume B + auto volumeB = Acts::Experimental::DetectorVolumeFactory::construct( + portalGenerator, tContext, "VolumeB", transformB, box, + Acts::Experimental::tryAllPortals()); + // Build the container + std::vector> volumes = { + volumeA, volumeB}; + auto container = + Acts::Experimental::detail::CuboidalDetectorHelper::connect( + tContext, volumes, bVal, {}, Acts::Logging::VERBOSE); + // Check the container + + Acts::ObjVisualization3D obj; + Acts::GeometryView3D::drawDetectorVolume(obj, *volumeA, tContext); + Acts::GeometryView3D::drawDetectorVolume(obj, *volumeB, tContext); + obj.write("ConnectectBoxesRegular_" + Acts::binningValueNames()[bVal] + + ".obj"); + } +} + +BOOST_AUTO_TEST_CASE(IrregularBoxConnectionInZ) { + std::vector binningValues = {Acts::binX, Acts::binY, + Acts::binZ}; + + using HlPos = std::array; + using VolHlPos = std::array; + using VolSetup = std::array; + + VolHlPos cPA = {{{10., 0.}, {10., 0.}, {10., 0.}}}; + VolHlPos cPB = {{{20., 0.}, {20., 0.}, {20., 0.}}}; + VolHlPos sP = {{{10., -30.}, {30., 10.}, {90., 130.}}}; + + std::array volSetups = { + {{sP, cPA, cPB}, {cPB, sP, cPA}, {cPA, cPB, sP}}}; + + std::array transforms = { + Acts::Transform3::Identity(), + Acts::Transform3(Acts::Transform3::Identity()) + .prerotate( + Acts::AngleAxis3(0.34, Acts::Vector3(1., 1., 1.).normalized()))}; + + // Try with arbitrary rotations + for (auto [it, t] : Acts::enumerate(transforms)) { + std::string trstr = it == 0 ? "" : "_rotated"; + auto rotation = t.rotation(); + // Try for all binning values + for (auto bVal : binningValues) { + auto [vsA, vsB, vsC] = volSetups[bVal]; + + // Three box shares with different length in Z + auto boxA = std::make_shared( + vsA[0][0], vsB[0][0], vsC[0][0]); + auto boxB = std::make_shared( + vsA[1][0], vsB[1][0], vsC[1][0]); + auto boxC = std::make_shared( + vsA[2][0], vsB[2][0], vsC[2][0]); + + auto transformA = Acts::Transform3::Identity(); + auto transformB = Acts::Transform3::Identity(); + auto transformC = Acts::Transform3::Identity(); + + transformA.pretranslate(Acts::Vector3(vsA[0][1], vsB[0][1], vsC[0][1])); + transformB.pretranslate(Acts::Vector3(vsA[1][1], vsB[1][1], vsC[1][1])); + transformC.pretranslate(Acts::Vector3(vsA[2][1], vsB[2][1], vsC[2][1])); + + transformA.prerotate(rotation); + transformB.prerotate(rotation); + transformC.prerotate(rotation); + + // Create volume A, B, C + auto volumeA = Acts::Experimental::DetectorVolumeFactory::construct( + portalGenerator, tContext, "VolumeA", transformA, boxA, + Acts::Experimental::tryAllPortals()); + auto volumeB = Acts::Experimental::DetectorVolumeFactory::construct( + portalGenerator, tContext, "VolumeB", transformB, boxB, + Acts::Experimental::tryAllPortals()); + auto volumeC = Acts::Experimental::DetectorVolumeFactory::construct( + portalGenerator, tContext, "VolumeC", transformC, boxC, + Acts::Experimental::tryAllPortals()); + + // Build the container + std::vector> volumes = + {volumeA, volumeB, volumeC}; + auto container = + Acts::Experimental::detail::CuboidalDetectorHelper::connect( + tContext, volumes, bVal, {}, Acts::Logging::VERBOSE); + + Acts::ObjVisualization3D obj; + Acts::GeometryView3D::drawDetectorVolume(obj, *volumeA, tContext); + Acts::GeometryView3D::drawDetectorVolume(obj, *volumeB, tContext); + Acts::GeometryView3D::drawDetectorVolume(obj, *volumeC, tContext); + obj.write("ConnectectBoxesIrregular_" + Acts::binningValueNames()[bVal] + + trstr + ".obj"); + } + } +} + +BOOST_AUTO_TEST_SUITE_END() From 33f80dfc5d01bd972f6d5c82e88f1e5f50f25e99 Mon Sep 17 00:00:00 2001 From: Andreas Salzburger Date: Thu, 30 Nov 2023 13:42:07 +0100 Subject: [PATCH 06/21] fixing low level check scripts --- .../include/Acts/Detector/detail/CuboidalDetectorHelper.hpp | 2 +- Core/include/Acts/Detector/detail/PortalHelper.hpp | 2 +- Core/src/Detector/detail/CuboidalDetectorHelper.cpp | 6 +++--- Core/src/Detector/detail/CylindricalDetectorHelper.cpp | 2 +- Core/src/Detector/detail/DetectorVolumeConsistency.cpp | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Core/include/Acts/Detector/detail/CuboidalDetectorHelper.hpp b/Core/include/Acts/Detector/detail/CuboidalDetectorHelper.hpp index ec8e8e9d99f..c264d645d7e 100644 --- a/Core/include/Acts/Detector/detail/CuboidalDetectorHelper.hpp +++ b/Core/include/Acts/Detector/detail/CuboidalDetectorHelper.hpp @@ -84,4 +84,4 @@ std::array, 3u> xyzBoundaries( } // namespace CuboidalDetectorHelper } // namespace detail } // namespace Experimental -} // namespace Acts \ No newline at end of file +} // namespace Acts diff --git a/Core/include/Acts/Detector/detail/PortalHelper.hpp b/Core/include/Acts/Detector/detail/PortalHelper.hpp index 16f26064d2a..8a0bd9ccac4 100644 --- a/Core/include/Acts/Detector/detail/PortalHelper.hpp +++ b/Core/include/Acts/Detector/detail/PortalHelper.hpp @@ -96,7 +96,7 @@ std::vector> attachedDetectorVolumes( /// @param pContainers the portal containers to be resolved /// @param sides the sides to be handled /// @param selectedOnly the selected only volumes, e.g. for complex containers -/// to chose only outisde skins, +/// to chose only outside skins, /// @param logLevel the logging level /// std::map, 3u> boundaries; // The map for collecting - std::array, 3u> valueMaps; + std::array, 3u> valueMaps; auto& xMap = valueMaps[0u]; auto& yMap = valueMaps[1u]; auto& zMap = valueMaps[2u]; - auto fillMap = [&](std::map& map, + auto fillMap = [&](std::map& map, const std::array& values) { for (auto v : values) { if (map.find(v) != map.end()) { @@ -386,4 +386,4 @@ Acts::Experimental::detail::CuboidalDetectorHelper::xyzBoundaries( ACTS_VERBOSE("- did yield " << boundaries[2u].size() << " boundaries in Z."); return boundaries; -} \ No newline at end of file +} diff --git a/Core/src/Detector/detail/CylindricalDetectorHelper.cpp b/Core/src/Detector/detail/CylindricalDetectorHelper.cpp index 8d56cd04b9a..01330ad8a0c 100644 --- a/Core/src/Detector/detail/CylindricalDetectorHelper.cpp +++ b/Core/src/Detector/detail/CylindricalDetectorHelper.cpp @@ -880,7 +880,7 @@ Acts::Experimental::detail::CylindricalDetectorHelper::connectInR( // Fuse containers, and update the attached volumes std::shared_ptr innerCylinder = containers[ic - 1].find(2u)->second; - // Direction is explicitely up + // Direction is explicitly addressed with a direction index auto innerAttachedVolumes = innerCylinder ->attachedDetectorVolumes()[Direction(Direction::Backward).index()]; diff --git a/Core/src/Detector/detail/DetectorVolumeConsistency.cpp b/Core/src/Detector/detail/DetectorVolumeConsistency.cpp index 8c53cc893c5..f0c9ac2cb21 100644 --- a/Core/src/Detector/detail/DetectorVolumeConsistency.cpp +++ b/Core/src/Detector/detail/DetectorVolumeConsistency.cpp @@ -69,4 +69,4 @@ Acts::Experimental::detail::DetectorVolumeConsistency::checkCenterAlignment( } } return distances; -} \ No newline at end of file +} From dcff6a2a09c44ead174325b94b79696a56b96022 Mon Sep 17 00:00:00 2001 From: Andreas Salzburger Date: Thu, 30 Nov 2023 14:28:22 +0100 Subject: [PATCH 07/21] more explicit exception message --- .../detail/DetectorVolumeConsistency.cpp | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/Core/src/Detector/detail/DetectorVolumeConsistency.cpp b/Core/src/Detector/detail/DetectorVolumeConsistency.cpp index f0c9ac2cb21..35036a30f42 100644 --- a/Core/src/Detector/detail/DetectorVolumeConsistency.cpp +++ b/Core/src/Detector/detail/DetectorVolumeConsistency.cpp @@ -10,6 +10,7 @@ #include "Acts/Definitions/Algebra.hpp" #include "Acts/Detector/DetectorVolume.hpp" +#include "Acts/Utilities/StringHelpers.hpp" #include @@ -50,12 +51,20 @@ Acts::Experimental::detail::DetectorVolumeConsistency::checkCenterAlignment( if (iv > 0) { Vector3 lastCenter = volumes[iv - 1]->transform(gctx).translation(); Vector3 curCenter = v->transform(gctx).translation(); - Vector3 diff = curCenter - lastCenter; + Vector3 diff(curCenter - lastCenter); // Check if the difference is aligned with the reference axis - if (not diff.normalized().isApprox(refAxis)) { - std::string message = "ConsitencyChecker: center of volume "; + if (!diff.normalized().isApprox(refAxis)) { + std::string message = "ConsitencyChecker: center "; + message += toString(curCenter); + message += " of volume "; message += std::to_string(iv); - message += std::string(" is not aligned with previous volume"); + message += " is not aligned with center "; + message += toString(lastCenter); + message += " of previous volume."; + message += " Axis mismatch: "; + message += toString(refAxis); + message += " vs. "; + message += toString(diff.normalized()); throw std::invalid_argument(message.c_str()); } // Check if the projection is positive From 4a5288095c953f481445cb7ffd1747dc1d766966 Mon Sep 17 00:00:00 2001 From: Andreas Salzburger Date: Thu, 30 Nov 2023 16:39:50 +0100 Subject: [PATCH 08/21] force Vector3 evaluation --- Core/src/Detector/detail/DetectorVolumeConsistency.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Core/src/Detector/detail/DetectorVolumeConsistency.cpp b/Core/src/Detector/detail/DetectorVolumeConsistency.cpp index 35036a30f42..df44fb3e143 100644 --- a/Core/src/Detector/detail/DetectorVolumeConsistency.cpp +++ b/Core/src/Detector/detail/DetectorVolumeConsistency.cpp @@ -40,14 +40,14 @@ Acts::Experimental::detail::DetectorVolumeConsistency::checkCenterAlignment( const GeometryContext& gctx, const std::vector>& volumes, BinningValue axisValue) { - std::vector distances = {}; + std::vector distances = {}; // First it needs to surfive the rotation check checkRotationAlignment(gctx, volumes); // Get the reference axis - auto refAxis = volumes[0u]->transform(gctx).rotation().col(axisValue); + Vector3 refAxis = volumes[0u]->transform(gctx).rotation().col(axisValue); - for (auto [iv, v] : Acts::enumerate(volumes)) { + for (auto [iv, v] : enumerate(volumes)) { if (iv > 0) { Vector3 lastCenter = volumes[iv - 1]->transform(gctx).translation(); Vector3 curCenter = v->transform(gctx).translation(); From 703ff49b36afdc7206ec01a142f7a01c3319b1ba Mon Sep 17 00:00:00 2001 From: Andreas Salzburger Date: Thu, 30 Nov 2023 19:10:57 +0100 Subject: [PATCH 09/21] make clang-tidy happy/happier --- Core/include/Acts/Detector/detail/PortalHelper.hpp | 2 +- Core/src/Detector/detail/CuboidalDetectorHelper.cpp | 4 ++-- Core/src/Detector/detail/DetectorVolumeConsistency.cpp | 4 ++-- Core/src/Detector/detail/PortalHelper.cpp | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Core/include/Acts/Detector/detail/PortalHelper.hpp b/Core/include/Acts/Detector/detail/PortalHelper.hpp index 8a0bd9ccac4..cf178c84968 100644 --- a/Core/include/Acts/Detector/detail/PortalHelper.hpp +++ b/Core/include/Acts/Detector/detail/PortalHelper.hpp @@ -102,7 +102,7 @@ std::vector> attachedDetectorVolumes( std::map>> stripSideVolumes( - const std::vector>> + const std::vector>>& pContainers, const std::vector& sides, const std::vector& selectedOnly = {}, diff --git a/Core/src/Detector/detail/CuboidalDetectorHelper.cpp b/Core/src/Detector/detail/CuboidalDetectorHelper.cpp index 3025198d72f..42076ef89e1 100644 --- a/Core/src/Detector/detail/CuboidalDetectorHelper.cpp +++ b/Core/src/Detector/detail/CuboidalDetectorHelper.cpp @@ -61,7 +61,7 @@ Acts::Experimental::detail::CuboidalDetectorHelper::connect( // Pick the counter part value auto counterPart = [&](BinningValue mValue) -> BinningValue { for (auto cValue : possibleValues) { - if (cValue != mValue and cValue != bValue) { + if (cValue != mValue && cValue != bValue) { return cValue; } } @@ -187,7 +187,7 @@ Acts::Experimental::detail::CuboidalDetectorHelper::connect( for (auto [is, index] : enumerate(portalSets[mergeValue])) { // Check if you need to skip due to selections - if (not selectedOnly.empty() and + if (!selectedOnly.empty() && std::find(selectedOnly.begin(), selectedOnly.end(), index) == selectedOnly.end()) { continue; diff --git a/Core/src/Detector/detail/DetectorVolumeConsistency.cpp b/Core/src/Detector/detail/DetectorVolumeConsistency.cpp index df44fb3e143..3e1c54aaa87 100644 --- a/Core/src/Detector/detail/DetectorVolumeConsistency.cpp +++ b/Core/src/Detector/detail/DetectorVolumeConsistency.cpp @@ -25,7 +25,7 @@ void Acts::Experimental::detail::DetectorVolumeConsistency:: for (auto [iv, v] : Acts::enumerate(volumes)) { if (iv > 0) { auto curRotation = v->transform(gctx).rotation(); - if (not curRotation.isApprox(refRotation)) { + if (!curRotation.isApprox(refRotation)) { std::string message = "ConsitencyChecker: rotation of volume "; message += std::to_string(iv); message += std::string(" is not aligned with previous volume"); @@ -68,7 +68,7 @@ Acts::Experimental::detail::DetectorVolumeConsistency::checkCenterAlignment( throw std::invalid_argument(message.c_str()); } // Check if the projection is positive - if (diff.dot(refAxis) < 0) { + if (diff.dot(refAxis) < 0.) { std::string message = "ConsitencyChecker: center of volume "; message += std::to_string(iv); message += std::string(" is not ordered with previous volume"); diff --git a/Core/src/Detector/detail/PortalHelper.cpp b/Core/src/Detector/detail/PortalHelper.cpp index f0a860b2de8..4db3b278189 100644 --- a/Core/src/Detector/detail/PortalHelper.cpp +++ b/Core/src/Detector/detail/PortalHelper.cpp @@ -87,7 +87,7 @@ Acts::Experimental::detail::PortalHelper::attachedDetectorVolumes( std::map>> Acts::Experimental::detail::PortalHelper::stripSideVolumes( - const std::vector>> + const std::vector>>& pContainers, const std::vector& sides, const std::vector& selectedOnly, From 64bffbef20da9f8fef8c025023e35c5e1e2af3ea Mon Sep 17 00:00:00 2001 From: Andreas Salzburger Date: Tue, 12 Dec 2023 17:39:33 +0100 Subject: [PATCH 10/21] Update Core/include/Acts/Detector/detail/DetectorVolumeConsistency.hpp Co-authored-by: Alexander J. Pfleger <70842573+AJPfleger@users.noreply.github.com> --- Core/include/Acts/Detector/detail/DetectorVolumeConsistency.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/include/Acts/Detector/detail/DetectorVolumeConsistency.hpp b/Core/include/Acts/Detector/detail/DetectorVolumeConsistency.hpp index 89264d9d955..5d3c2d2d2ea 100644 --- a/Core/include/Acts/Detector/detail/DetectorVolumeConsistency.hpp +++ b/Core/include/Acts/Detector/detail/DetectorVolumeConsistency.hpp @@ -43,7 +43,7 @@ void checkRotationAlignment( /// @param volumes the input volumes to be checked /// @param axisValue the alignment axist /// -/// @note this will call checkRotation Alginemtn first +/// @note this will call checkRotationAlignment first /// @note throws exception if the volumes are not ordered /// /// @return a vector with position differences (ordered) From c2d75c56087e59afbe1d4e97f9e4dd6f57089aaf Mon Sep 17 00:00:00 2001 From: Andreas Salzburger Date: Tue, 12 Dec 2023 17:39:44 +0100 Subject: [PATCH 11/21] Update Core/src/Detector/detail/CuboidalDetectorHelper.cpp Co-authored-by: Alexander J. Pfleger <70842573+AJPfleger@users.noreply.github.com> --- Core/src/Detector/detail/CuboidalDetectorHelper.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/src/Detector/detail/CuboidalDetectorHelper.cpp b/Core/src/Detector/detail/CuboidalDetectorHelper.cpp index 42076ef89e1..0555342ee3b 100644 --- a/Core/src/Detector/detail/CuboidalDetectorHelper.cpp +++ b/Core/src/Detector/detail/CuboidalDetectorHelper.cpp @@ -160,7 +160,7 @@ Acts::Experimental::detail::CuboidalDetectorHelper::connect( continue; } - // Create the new RecangleBounds + // Create the new RectangleBounds // - there are conventions involved, regarding the bounds orientation // - This is an anticyclic swap bool mergedInX = true; From 35526255e7c7f83932461847e9024ec8cacacd4c Mon Sep 17 00:00:00 2001 From: Andreas Salzburger Date: Tue, 12 Dec 2023 17:39:56 +0100 Subject: [PATCH 12/21] Update Core/src/Detector/detail/CuboidalDetectorHelper.cpp Co-authored-by: Alexander J. Pfleger <70842573+AJPfleger@users.noreply.github.com> --- Core/src/Detector/detail/CuboidalDetectorHelper.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/src/Detector/detail/CuboidalDetectorHelper.cpp b/Core/src/Detector/detail/CuboidalDetectorHelper.cpp index 0555342ee3b..71a2c529ab0 100644 --- a/Core/src/Detector/detail/CuboidalDetectorHelper.cpp +++ b/Core/src/Detector/detail/CuboidalDetectorHelper.cpp @@ -178,7 +178,7 @@ Acts::Experimental::detail::CuboidalDetectorHelper::connect( break; } - // The stitch boundarieS for portal pointing + // The stitch boundaries for portal pointing std::vector stitchBoundaries; stitchBoundaries.push_back(-mergeHalfLengths[im]); for (auto step : mergeSplits[im]) { From a9a45c7215b4303d2b464145252ef36987cf973e Mon Sep 17 00:00:00 2001 From: Andreas Salzburger Date: Tue, 12 Dec 2023 17:40:31 +0100 Subject: [PATCH 13/21] Update Core/src/Detector/detail/CuboidalDetectorHelper.cpp Co-authored-by: Alexander J. Pfleger <70842573+AJPfleger@users.noreply.github.com> --- Core/src/Detector/detail/CuboidalDetectorHelper.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Core/src/Detector/detail/CuboidalDetectorHelper.cpp b/Core/src/Detector/detail/CuboidalDetectorHelper.cpp index 71a2c529ab0..ead65a46c86 100644 --- a/Core/src/Detector/detail/CuboidalDetectorHelper.cpp +++ b/Core/src/Detector/detail/CuboidalDetectorHelper.cpp @@ -216,6 +216,10 @@ Acts::Experimental::detail::CuboidalDetectorHelper::connect( for (auto& iv : volumes) { ACTS_VERBOSE("- update portals of volume '" << iv->name() << "'."); for (auto& [p, i, dir, boundaries, binning] : pReplacements) { + (void)dir; + (void)boundaries; + (void)binning; + // Fill the map dShell[i] = p; ACTS_VERBOSE("-- update portal with index " << i); From e0acd960a67871a4b81c5705d526b756ae5e03cc Mon Sep 17 00:00:00 2001 From: Andreas Salzburger Date: Tue, 12 Dec 2023 17:40:56 +0100 Subject: [PATCH 14/21] Update Tests/UnitTests/Core/Detector/CuboidalDetectorHelperTests.cpp Co-authored-by: Alexander J. Pfleger <70842573+AJPfleger@users.noreply.github.com> --- Tests/UnitTests/Core/Detector/CuboidalDetectorHelperTests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/UnitTests/Core/Detector/CuboidalDetectorHelperTests.cpp b/Tests/UnitTests/Core/Detector/CuboidalDetectorHelperTests.cpp index 6cdc60a47cc..ba7637bfb7b 100644 --- a/Tests/UnitTests/Core/Detector/CuboidalDetectorHelperTests.cpp +++ b/Tests/UnitTests/Core/Detector/CuboidalDetectorHelperTests.cpp @@ -1,6 +1,6 @@ // This file is part of the Acts project. // -// Copyright (C) 2022 CERN for the benefit of the Acts project +// Copyright (C) 2023 CERN for the benefit of the Acts project // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this From 34e73e1e429a36fe766ff303f25d4f43541b7d91 Mon Sep 17 00:00:00 2001 From: Andreas Salzburger Date: Mon, 18 Dec 2023 14:33:43 +0100 Subject: [PATCH 15/21] test for container connection --- Core/src/Detector/Portal.cpp | 46 ++++++----- .../detail/CuboidalDetectorHelper.cpp | 26 ++----- .../Detector/CuboidalDetectorHelperTests.cpp | 77 ++++++++++++++++++- 3 files changed, 108 insertions(+), 41 deletions(-) diff --git a/Core/src/Detector/Portal.cpp b/Core/src/Detector/Portal.cpp index 2e1123d03e6..930a48b4f4c 100644 --- a/Core/src/Detector/Portal.cpp +++ b/Core/src/Detector/Portal.cpp @@ -69,47 +69,53 @@ std::shared_ptr Portal::fuse(std::shared_ptr& aPortal, if (noneConnected(*aPortal) || noneConnected(*bPortal)) { throw std::invalid_argument( - "Portal: trying to fuse two portals where at least on has no links."); + "Portal: trying to fuse two portals where at least one has no links."); } - // We checked they're not both empty, so one of them must be connected - Direction aDir = (aPortal->m_volumeUpdaters[0].connected()) + // @TODO: There's no safety against fusing portals with different surfaces + // We model the fused portal after the portal a + std::shared_ptr fused = std::make_shared(aPortal->m_surface); + + // Get the connection directions + Direction getA = (aPortal->m_volumeUpdaters[0].connected()) + ? Direction::fromIndex(0) + : Direction::fromIndex(1); + Direction getB = (bPortal->m_volumeUpdaters[0].connected()) ? Direction::fromIndex(0) : Direction::fromIndex(1); - Direction bDir = aDir.invert(); - // And now check other direction - if (!bPortal->m_volumeUpdaters[bDir.index()].connected()) { - throw std::runtime_error( - "Portal: trying to fuse portal (discard) with no links."); - } + // Modelling the fused portal after the aPortal, leaves B as inverted + Direction setA = getA; + Direction setB = setA.invert(); + // Checkif material is associated const auto& aSurface = aPortal->surface(); const auto& bSurface = bPortal->surface(); - // @TODO: There's no safety against fusing portals with different surfaces - std::shared_ptr fused = std::make_shared(aPortal->m_surface); - if (aSurface.surfaceMaterial() != nullptr && bSurface.surfaceMaterial() != nullptr) { throw std::runtime_error( "Portal: both surfaces have surface material, fusing will lead to " "information loss."); } else if (aSurface.surfaceMaterial() != nullptr) { + // We keep portal A modelling fused->m_surface = aPortal->m_surface; } else if (bSurface.surfaceMaterial() != nullptr) { fused->m_surface = bPortal->m_surface; + // Remodel after the bPortal + setB = getB; + setA = setB.invert(); } - fused->m_volumeUpdaters[aDir.index()] = - std::move(aPortal->m_volumeUpdaters[aDir.index()]); - fused->m_attachedVolumes[aDir.index()] = - std::move(aPortal->m_attachedVolumes[aDir.index()]); + fused->m_volumeUpdaters[setA.index()] = + std::move(aPortal->m_volumeUpdaters[getA.index()]); + fused->m_attachedVolumes[setA.index()] = + std::move(aPortal->m_attachedVolumes[getA.index()]); - fused->m_volumeUpdaters[bDir.index()] = - std::move(bPortal->m_volumeUpdaters[bDir.index()]); - fused->m_attachedVolumes[bDir.index()] = - std::move(bPortal->m_attachedVolumes[bDir.index()]); + fused->m_volumeUpdaters[setB.index()] = + std::move(bPortal->m_volumeUpdaters[getB.index()]); + fused->m_attachedVolumes[setB.index()] = + std::move(bPortal->m_attachedVolumes[getB.index()]); return fused; } diff --git a/Core/src/Detector/detail/CuboidalDetectorHelper.cpp b/Core/src/Detector/detail/CuboidalDetectorHelper.cpp index ead65a46c86..9daedbea0f5 100644 --- a/Core/src/Detector/detail/CuboidalDetectorHelper.cpp +++ b/Core/src/Detector/detail/CuboidalDetectorHelper.cpp @@ -162,7 +162,7 @@ Acts::Experimental::detail::CuboidalDetectorHelper::connect( // Create the new RectangleBounds // - there are conventions involved, regarding the bounds orientation - // - This is an anticyclic swap + // - this is an anticyclic swap bool mergedInX = true; switch (bValue) { case binZ: { @@ -208,6 +208,10 @@ Acts::Experimental::detail::CuboidalDetectorHelper::connect( stitchBoundaries, (mergedInX ? binX : binY))); } } + + // Attach the new volume updaters + PortalHelper::attachDetectorVolumeUpdaters(gctx, volumes, pReplacements); + // Return proto container DetectorComponent::PortalContainer dShell; @@ -216,10 +220,6 @@ Acts::Experimental::detail::CuboidalDetectorHelper::connect( for (auto& iv : volumes) { ACTS_VERBOSE("- update portals of volume '" << iv->name() << "'."); for (auto& [p, i, dir, boundaries, binning] : pReplacements) { - (void)dir; - (void)boundaries; - (void)binning; - // Fill the map dShell[i] = p; ACTS_VERBOSE("-- update portal with index " << i); @@ -235,7 +235,7 @@ Acts::Experimental::DetectorComponent::PortalContainer Acts::Experimental::detail::CuboidalDetectorHelper::connect( const GeometryContext& gctx, const std::vector& containers, - BinningValue bValue, const std::vector& selectedOnly, + BinningValue bValue, const std::vector& /*unused */, Acts::Logging::Level logLevel) noexcept(false) { // The local logger ACTS_LOCAL_LOGGER(getDefaultLogger("CuboidalDetectorHelper", logLevel)); @@ -307,20 +307,6 @@ Acts::Experimental::detail::CuboidalDetectorHelper::connect( } } - // Strip the side volumes - auto sideVolumes = - PortalHelper::stripSideVolumes(containers, sidePortals, selectedOnly); - - ACTS_VERBOSE("There remain " << sideVolumes.size() - << " side volume packs to be connected"); - for (auto [s, volumes] : sideVolumes) { - ACTS_VERBOSE(" - connect " << volumes.size() << " at selected side " << s); - auto pR = connect(gctx, volumes, bValue, {s}, logLevel); - if (pR.find(s) != pR.end()) { - dShell[s] = pR.find(s)->second; - } - } - // Done. return dShell; } diff --git a/Tests/UnitTests/Core/Detector/CuboidalDetectorHelperTests.cpp b/Tests/UnitTests/Core/Detector/CuboidalDetectorHelperTests.cpp index ba7637bfb7b..ad71c77d015 100644 --- a/Tests/UnitTests/Core/Detector/CuboidalDetectorHelperTests.cpp +++ b/Tests/UnitTests/Core/Detector/CuboidalDetectorHelperTests.cpp @@ -83,7 +83,9 @@ BOOST_AUTO_TEST_CASE(SimpleBoxConnection) { auto container = Acts::Experimental::detail::CuboidalDetectorHelper::connect( tContext, volumes, bVal, {}, Acts::Logging::VERBOSE); - // Check the container + + // Check the container is constructed + BOOST_CHECK(!container.empty()); Acts::ObjVisualization3D obj; Acts::GeometryView3D::drawDetectorVolume(obj, *volumeA, tContext); @@ -160,6 +162,9 @@ BOOST_AUTO_TEST_CASE(IrregularBoxConnectionInZ) { Acts::Experimental::detail::CuboidalDetectorHelper::connect( tContext, volumes, bVal, {}, Acts::Logging::VERBOSE); + // Check the container is constructed + BOOST_CHECK(!container.empty()); + Acts::ObjVisualization3D obj; Acts::GeometryView3D::drawDetectorVolume(obj, *volumeA, tContext); Acts::GeometryView3D::drawDetectorVolume(obj, *volumeB, tContext); @@ -170,4 +175,74 @@ BOOST_AUTO_TEST_CASE(IrregularBoxConnectionInZ) { } } +BOOST_AUTO_TEST_CASE(ContainerConnection) { + // A perfect box shape + auto box = std::make_shared(10, 10, 10); + + // Create an AB container + + // Create volume A + auto volumeA = Acts::Experimental::DetectorVolumeFactory::construct( + portalGenerator, tContext, "VolumeA", Acts::Transform3::Identity(), box, + Acts::Experimental::tryAllPortals()); + + // Move it into the bval direction + auto transformB = Acts::Transform3::Identity(); + Acts::Vector3 translationB = Acts::Vector3::Zero(); + translationB[Acts::binX] = 20; + transformB.pretranslate(translationB); + // Create volume B + auto volumeB = Acts::Experimental::DetectorVolumeFactory::construct( + portalGenerator, tContext, "VolumeB", transformB, box, + Acts::Experimental::tryAllPortals()); + // Build the container + std::vector> volumes = { + volumeA, volumeB}; + auto containerAB = + Acts::Experimental::detail::CuboidalDetectorHelper::connect( + tContext, volumes, Acts::binX, {}, Acts::Logging::VERBOSE); + + // Create a CD container + + auto transformC = Acts::Transform3::Identity(); + Acts::Vector3 translationC = Acts::Vector3::Zero(); + translationC[Acts::binY] = 20; + transformC.pretranslate(translationC); + + auto volumeC = Acts::Experimental::DetectorVolumeFactory::construct( + portalGenerator, tContext, "VolumeC", transformC, box, + Acts::Experimental::tryAllPortals()); + + auto transformD = Acts::Transform3::Identity(); + Acts::Vector3 translationD = Acts::Vector3::Zero(); + translationD[Acts::binX] = 20; + translationD[Acts::binY] = 20; + transformD.pretranslate(translationD); + + auto volumeD = Acts::Experimental::DetectorVolumeFactory::construct( + portalGenerator, tContext, "VolumeD", transformD, box, + Acts::Experimental::tryAllPortals()); + + volumes = {volumeC, volumeD}; + auto containerCD = + Acts::Experimental::detail::CuboidalDetectorHelper::connect( + tContext, volumes, Acts::binX, {}, Acts::Logging::VERBOSE); + + auto containerABCD = + Acts::Experimental::detail::CuboidalDetectorHelper::connect( + tContext, {containerAB, containerCD}, Acts::binY, {}, + Acts::Logging::VERBOSE); + + // Check the container is constructed + BOOST_CHECK(!containerABCD.empty()); + + Acts::ObjVisualization3D obj; + Acts::GeometryView3D::drawDetectorVolume(obj, *volumeA, tContext); + Acts::GeometryView3D::drawDetectorVolume(obj, *volumeB, tContext); + Acts::GeometryView3D::drawDetectorVolume(obj, *volumeC, tContext); + Acts::GeometryView3D::drawDetectorVolume(obj, *volumeD, tContext); + + obj.write("ConnectContainers_binX.obj"); +} + BOOST_AUTO_TEST_SUITE_END() From 565cd89d87e1f773a3a7ba587f4442f6fedc9827 Mon Sep 17 00:00:00 2001 From: Andreas Salzburger Date: Mon, 18 Dec 2023 14:41:17 +0100 Subject: [PATCH 16/21] fix warning --- Core/src/Detector/detail/CuboidalDetectorHelper.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/src/Detector/detail/CuboidalDetectorHelper.cpp b/Core/src/Detector/detail/CuboidalDetectorHelper.cpp index 9daedbea0f5..4ce2237566b 100644 --- a/Core/src/Detector/detail/CuboidalDetectorHelper.cpp +++ b/Core/src/Detector/detail/CuboidalDetectorHelper.cpp @@ -233,7 +233,7 @@ Acts::Experimental::detail::CuboidalDetectorHelper::connect( Acts::Experimental::DetectorComponent::PortalContainer Acts::Experimental::detail::CuboidalDetectorHelper::connect( - const GeometryContext& gctx, + const GeometryContext& /*gctx*/, const std::vector& containers, BinningValue bValue, const std::vector& /*unused */, Acts::Logging::Level logLevel) noexcept(false) { From 2361f98824da2d19f7dab744fc2dabba68dfebd1 Mon Sep 17 00:00:00 2001 From: Andreas Salzburger Date: Mon, 18 Dec 2023 14:47:46 +0100 Subject: [PATCH 17/21] allowing same facing portals --- Tests/UnitTests/Core/Detector/PortalTests.cpp | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/Tests/UnitTests/Core/Detector/PortalTests.cpp b/Tests/UnitTests/Core/Detector/PortalTests.cpp index c2e89b7dacf..232d3aa980b 100644 --- a/Tests/UnitTests/Core/Detector/PortalTests.cpp +++ b/Tests/UnitTests/Core/Detector/PortalTests.cpp @@ -144,25 +144,6 @@ BOOST_AUTO_TEST_CASE(PortalTest) { // Portal A retains identical position to B BOOST_CHECK_EQUAL(portalA->surface().center(gctx), portalB->surface().center(gctx)); - - // An invalid fusing setup - auto linkToAIImpl = std::make_unique(volumeA); - auto linkToBIImpl = std::make_unique(volumeB); - - auto portalAI = std::make_shared(surface); - DetectorVolumeUpdater linkToAI; - linkToAI.connect<&LinkToVolumeImpl::link>(std::move(linkToAIImpl)); - portalAI->assignDetectorVolumeUpdater(Acts::Direction::Positive, - std::move(linkToAI), {volumeA}); - - auto portalBI = std::make_shared(surface); - DetectorVolumeUpdater linkToBI; - linkToBI.connect<&LinkToVolumeImpl::link>(std::move(linkToBIImpl)); - portalBI->assignDetectorVolumeUpdater(Acts::Direction::Positive, - std::move(linkToBI), {volumeB}); - - BOOST_CHECK_THROW(Portal::fuse(portalAI, portalBI), std::runtime_error); - BOOST_CHECK_THROW(Portal::fuse(portalBI, portalAI), std::runtime_error); } BOOST_AUTO_TEST_CASE(PortalMaterialTest) { From 3b80930282512ff207051581dfe1e532b3a2b28d Mon Sep 17 00:00:00 2001 From: Andreas Salzburger Date: Tue, 19 Dec 2023 06:10:57 +0100 Subject: [PATCH 18/21] Update Core/src/Detector/Portal.cpp Co-authored-by: Alexander J. Pfleger <70842573+AJPfleger@users.noreply.github.com> --- Core/src/Detector/Portal.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/src/Detector/Portal.cpp b/Core/src/Detector/Portal.cpp index 930a48b4f4c..412b68fa91f 100644 --- a/Core/src/Detector/Portal.cpp +++ b/Core/src/Detector/Portal.cpp @@ -73,7 +73,7 @@ std::shared_ptr Portal::fuse(std::shared_ptr& aPortal, } // @TODO: There's no safety against fusing portals with different surfaces - // We model the fused portal after the portal a + // We model the fused portal after the aPortal std::shared_ptr fused = std::make_shared(aPortal->m_surface); // Get the connection directions From 3b02ec9e32f8d1e073018140e0df438e658d8ba8 Mon Sep 17 00:00:00 2001 From: Andreas Salzburger Date: Tue, 19 Dec 2023 06:11:04 +0100 Subject: [PATCH 19/21] Update Core/src/Detector/Portal.cpp Co-authored-by: Alexander J. Pfleger <70842573+AJPfleger@users.noreply.github.com> --- Core/src/Detector/Portal.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/src/Detector/Portal.cpp b/Core/src/Detector/Portal.cpp index 412b68fa91f..d5df24e8320 100644 --- a/Core/src/Detector/Portal.cpp +++ b/Core/src/Detector/Portal.cpp @@ -88,7 +88,7 @@ std::shared_ptr Portal::fuse(std::shared_ptr& aPortal, Direction setA = getA; Direction setB = setA.invert(); - // Checkif material is associated + // Check if material is associated const auto& aSurface = aPortal->surface(); const auto& bSurface = bPortal->surface(); From 98cbc76659ad9c7574d6ed7e46e231e402b36f6c Mon Sep 17 00:00:00 2001 From: Andreas Salzburger Date: Tue, 19 Dec 2023 06:11:17 +0100 Subject: [PATCH 20/21] Update Core/src/Detector/Portal.cpp Co-authored-by: Alexander J. Pfleger <70842573+AJPfleger@users.noreply.github.com> --- Core/src/Detector/Portal.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/src/Detector/Portal.cpp b/Core/src/Detector/Portal.cpp index d5df24e8320..4e556d7aaa8 100644 --- a/Core/src/Detector/Portal.cpp +++ b/Core/src/Detector/Portal.cpp @@ -98,7 +98,7 @@ std::shared_ptr Portal::fuse(std::shared_ptr& aPortal, "Portal: both surfaces have surface material, fusing will lead to " "information loss."); } else if (aSurface.surfaceMaterial() != nullptr) { - // We keep portal A modelling + // We keep the aPortal modelling fused->m_surface = aPortal->m_surface; } else if (bSurface.surfaceMaterial() != nullptr) { fused->m_surface = bPortal->m_surface; From 11ae30f443e2721514920745a9f1331cd356fd20 Mon Sep 17 00:00:00 2001 From: Andreas Salzburger Date: Tue, 19 Dec 2023 11:55:56 +0100 Subject: [PATCH 21/21] adding consistency unit test --- Tests/UnitTests/Core/Detector/CMakeLists.txt | 1 + .../DetectorVolumeConsistencyTests.cpp | 80 +++++++++++++++++++ 2 files changed, 81 insertions(+) create mode 100644 Tests/UnitTests/Core/Detector/DetectorVolumeConsistencyTests.cpp diff --git a/Tests/UnitTests/Core/Detector/CMakeLists.txt b/Tests/UnitTests/Core/Detector/CMakeLists.txt index 9a6060cbaa4..7940cc28c5f 100644 --- a/Tests/UnitTests/Core/Detector/CMakeLists.txt +++ b/Tests/UnitTests/Core/Detector/CMakeLists.txt @@ -8,6 +8,7 @@ add_unittest(GridAxisGenerators GridAxisGeneratorsTests.cpp) add_unittest(Detector DetectorTests.cpp) add_unittest(DetectorBuilder DetectorBuilderTests.cpp) add_unittest(DetectorVolume DetectorVolumeTests.cpp) +add_unittest(DetectorVolumeConsistency DetectorVolumeConsistencyTests.cpp) add_unittest(DetectorVolumeBuilder DetectorVolumeBuilderTests.cpp) add_unittest(GeometryIdGenerator GeometryIdGeneratorTests.cpp) add_unittest(IndexedRootVolumeFinderBuilder IndexedRootVolumeFinderBuilderTests.cpp) diff --git a/Tests/UnitTests/Core/Detector/DetectorVolumeConsistencyTests.cpp b/Tests/UnitTests/Core/Detector/DetectorVolumeConsistencyTests.cpp new file mode 100644 index 00000000000..1619b4891e8 --- /dev/null +++ b/Tests/UnitTests/Core/Detector/DetectorVolumeConsistencyTests.cpp @@ -0,0 +1,80 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include + +#include "Acts/Definitions/Algebra.hpp" +#include "Acts/Detector/DetectorVolume.hpp" +#include "Acts/Detector/PortalGenerators.hpp" +#include "Acts/Detector/detail/DetectorVolumeConsistency.hpp" +#include "Acts/Geometry/CuboidVolumeBounds.hpp" +#include "Acts/Geometry/GeometryContext.hpp" +#include "Acts/Navigation/SurfaceCandidatesUpdaters.hpp" + +auto portalGenerator = Acts::Experimental::defaultPortalGenerator(); +auto tContext = Acts::GeometryContext(); + +BOOST_AUTO_TEST_SUITE(Detector) + +BOOST_AUTO_TEST_CASE(DetectorVolumeConsistencyFail) { + // A perfect box shape + auto box = std::make_shared(10, 10, 10); + + // Create volume A + auto volumeA = Acts::Experimental::DetectorVolumeFactory::construct( + portalGenerator, tContext, "VolumeA", Acts::Transform3::Identity(), box, + Acts::Experimental::tryAllPortals()); + + // Move it into the bval direction + auto transformB = Acts::Transform3::Identity(); + Acts::Vector3 translationB = Acts::Vector3::Zero(); + translationB[Acts::binX] = 20; + translationB[Acts::binY] = 5; + transformB.pretranslate(translationB); + // Create volume B + auto volumeB = Acts::Experimental::DetectorVolumeFactory::construct( + portalGenerator, tContext, "VolumeB", transformB, box, + Acts::Experimental::tryAllPortals()); + // Build the container + std::vector> volumes = { + volumeA, volumeB}; + + BOOST_CHECK_THROW( + Acts::Experimental::detail::DetectorVolumeConsistency:: + checkCenterAlignment(tContext, {volumeA, volumeB}, Acts::binX), + std::invalid_argument); +} + +BOOST_AUTO_TEST_CASE(DetectorVolumeConsistencyPass) { + // A perfect box shape + auto box = std::make_shared(10, 10, 10); + + // Create volume A + auto volumeA = Acts::Experimental::DetectorVolumeFactory::construct( + portalGenerator, tContext, "VolumeA", Acts::Transform3::Identity(), box, + Acts::Experimental::tryAllPortals()); + + // Move it into the bval direction + auto transformB = Acts::Transform3::Identity(); + Acts::Vector3 translationB = Acts::Vector3::Zero(); + translationB[Acts::binX] = 20; + transformB.pretranslate(translationB); + // Create volume B + auto volumeB = Acts::Experimental::DetectorVolumeFactory::construct( + portalGenerator, tContext, "VolumeB", transformB, box, + Acts::Experimental::tryAllPortals()); + // Build the container + std::vector> volumes = { + volumeA, volumeB}; + + BOOST_CHECK_NO_THROW( + Acts::Experimental::detail::DetectorVolumeConsistency:: + checkCenterAlignment(tContext, {volumeA, volumeB}, Acts::binX)); +} + +BOOST_AUTO_TEST_SUITE_END()