From 56ebe13133c8bc4d7cb54058896249867a8d9b0f Mon Sep 17 00:00:00 2001 From: "Alexander J. Pfleger" <70842573+AJPfleger@users.noreply.github.com> Date: Fri, 14 Jun 2024 20:31:28 +0200 Subject: [PATCH 1/5] refactor(gx2f): make constant TrackStatePropMask (#3293) --- .../TrackFitting/GlobalChiSquareFitter.hpp | 33 ++++++++----------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/Core/include/Acts/TrackFitting/GlobalChiSquareFitter.hpp b/Core/include/Acts/TrackFitting/GlobalChiSquareFitter.hpp index d0cb780efeea..6b0e10dc86eb 100644 --- a/Core/include/Acts/TrackFitting/GlobalChiSquareFitter.hpp +++ b/Core/include/Acts/TrackFitting/GlobalChiSquareFitter.hpp @@ -49,6 +49,11 @@ namespace Acts::Experimental { namespace Gx2fConstants { constexpr std::string_view gx2fnUpdateColumn = "Gx2fnUpdateColumn"; + +// Mask for the track states. We don't need Smoothed and Filtered +constexpr TrackStatePropMask trackStateMask = TrackStatePropMask::Predicted | + TrackStatePropMask::Jacobian | + TrackStatePropMask::Calibrated; } // namespace Gx2fConstants /// Extension struct which holds delegates to customize the KF behavior @@ -463,17 +468,11 @@ class Gx2Fitter { // TODO generalize the update of the currentTrackIndex auto& fittedStates = *result.fittedStates; - // Mask for the track states. We don't need Smoothed and Filtered - TrackStatePropMask mask = TrackStatePropMask::Predicted | - TrackStatePropMask::Jacobian | - TrackStatePropMask::Calibrated; - - ACTS_VERBOSE(" processSurface: addTrackState"); - - // Add a TrackState entry multi trajectory. This allocates - // storage for all components, which we will set later. + // Add a TrackState entry multi trajectory. This + // allocates storage for all components, which we will set later. typename traj_t::TrackStateProxy trackStateProxy = - fittedStates.makeTrackState(mask, result.lastTrackIndex); + fittedStates.makeTrackState(Gx2fConstants::trackStateMask, + result.lastTrackIndex); std::size_t currentTrackIndex = trackStateProxy.index(); // Set the trackStateProxy components with the state from the ongoing @@ -552,17 +551,11 @@ class Gx2Fitter { auto& fittedStates = *result.fittedStates; - // Mask for the track states. We don't need Smoothed and Filtered - TrackStatePropMask mask = TrackStatePropMask::Predicted | - TrackStatePropMask::Jacobian | - TrackStatePropMask::Calibrated; - - ACTS_VERBOSE(" processSurface: addTrackState"); - - // Add a TrackState entry multi trajectory. This allocates - // storage for all components, which we will set later. + // Add a TrackState entry multi trajectory. This + // allocates storage for all components, which we will set later. typename traj_t::TrackStateProxy trackStateProxy = - fittedStates.makeTrackState(mask, result.lastTrackIndex); + fittedStates.makeTrackState(Gx2fConstants::trackStateMask, + result.lastTrackIndex); std::size_t currentTrackIndex = trackStateProxy.index(); { // Set the trackStateProxy components with the state from the From e1a76b33e75817513e1233252d2e160522cec082 Mon Sep 17 00:00:00 2001 From: Paul Gessinger Date: Fri, 14 Jun 2024 22:26:36 +0200 Subject: [PATCH 2/5] feat: Cylinder and Disc surface merging (#3288) This PR allows merging of two cylinder and disc surfaces. Merging is allowed in the $z$ and $r\times\phi$ direction for cylinders, and in the $r$ and $\phi$ direction for discs. The surfaces need to be consistent, i.e. their bounds dimensions and transforms need to line up. - For **cylinders**: - $z$ merging: $z$ edges must be aligned, no relative $xy$ shift or any rotation is allowed. - $r\times\phi$ merging: $r\times\phi$ edges must be aligned, no $xyz$ shift or any rotation is allowed - For **discs**: - $r$ merging: $r$ edges must be aligned, no $xyz$ shift or rotation is allowed - $\phi$ merging: $\phi$ edges must be aligned, no $xyz$ shift or rotation is allowed. ![Cylinder_Merging](https://github.com/acts-project/acts/assets/1058585/ed7b2a9d-764b-473a-9c5a-675651232786) ![Disc_Merging](https://github.com/acts-project/acts/assets/1058585/3fe97e7e-94ee-4d1a-8277-8cd01104b06a) Blocked by: - #3290 --- Core/include/Acts/Surfaces/CylinderBounds.hpp | 21 -- .../include/Acts/Surfaces/CylinderSurface.hpp | 14 ++ Core/include/Acts/Surfaces/DiscSurface.hpp | 13 ++ .../Acts/Surfaces/detail/MergeHelper.hpp | 31 +++ Core/src/Surfaces/CMakeLists.txt | 1 + Core/src/Surfaces/CylinderBounds.cpp | 22 ++ Core/src/Surfaces/CylinderSurface.cpp | 143 ++++++++++++ Core/src/Surfaces/DiscSurface.cpp | 122 ++++++++++ Core/src/Surfaces/detail/MergeHelper.cpp | 65 ++++++ .../Core/Surfaces/CylinderSurfaceTests.cpp | 218 +++++++++++++++++ .../Core/Surfaces/DiscSurfaceTests.cpp | 220 ++++++++++++++++++ docs/core/geometry/surfaces.md | 24 +- docs/figures/doxygen/Cylinder_Merging.svg | 93 ++++++++ docs/figures/doxygen/Disc_Merging.svg | 48 ++++ 14 files changed, 1007 insertions(+), 28 deletions(-) create mode 100644 Core/include/Acts/Surfaces/detail/MergeHelper.hpp create mode 100644 Core/src/Surfaces/detail/MergeHelper.cpp create mode 100644 docs/figures/doxygen/Cylinder_Merging.svg create mode 100644 docs/figures/doxygen/Disc_Merging.svg diff --git a/Core/include/Acts/Surfaces/CylinderBounds.hpp b/Core/include/Acts/Surfaces/CylinderBounds.hpp index 94e8098fa9d7..0964c0916328 100644 --- a/Core/include/Acts/Surfaces/CylinderBounds.hpp +++ b/Core/include/Acts/Surfaces/CylinderBounds.hpp @@ -159,25 +159,4 @@ inline bool CylinderBounds::coversFullAzimuth() const { return m_closed; } -inline void CylinderBounds::checkConsistency() noexcept(false) { - if (get(eR) <= 0.) { - throw std::invalid_argument("CylinderBounds: invalid radial setup."); - } - if (get(eHalfLengthZ) <= 0.) { - throw std::invalid_argument("CylinderBounds: invalid length setup."); - } - if (get(eHalfPhiSector) <= 0. || get(eHalfPhiSector) > M_PI) { - throw std::invalid_argument("CylinderBounds: invalid phi sector setup."); - } - if (get(eAveragePhi) != detail::radian_sym(get(eAveragePhi))) { - throw std::invalid_argument("CylinderBounds: invalid phi positioning."); - } - if (get(eBevelMinZ) != detail::radian_sym(get(eBevelMinZ))) { - throw std::invalid_argument("CylinderBounds: invalid bevel at min Z."); - } - if (get(eBevelMaxZ) != detail::radian_sym(get(eBevelMaxZ))) { - throw std::invalid_argument("CylinderBounds: invalid bevel at max Z."); - } -} - } // namespace Acts diff --git a/Core/include/Acts/Surfaces/CylinderSurface.hpp b/Core/include/Acts/Surfaces/CylinderSurface.hpp index 7986263859d2..23e687d564e9 100644 --- a/Core/include/Acts/Surfaces/CylinderSurface.hpp +++ b/Core/include/Acts/Surfaces/CylinderSurface.hpp @@ -21,6 +21,7 @@ #include "Acts/Surfaces/SurfaceConcept.hpp" #include "Acts/Utilities/BinningType.hpp" #include "Acts/Utilities/Concepts.hpp" +#include "Acts/Utilities/Logger.hpp" #include "Acts/Utilities/Result.hpp" #include "Acts/Utilities/detail/RealQuadraticEquation.hpp" @@ -247,6 +248,19 @@ class CylinderSurface : public RegularSurface { ActsMatrix<2, 3> localCartesianToBoundLocalDerivative( const GeometryContext& gctx, const Vector3& position) const final; + /// Merge two cylinder surfaces into a single one. + /// @image html Cylinder_Merging.svg + /// @note The surfaces need to be *compatible*, i.e. have cylinder bounds + /// that align, and have the same radius + /// @param gctx The current geometry context object, e.g. alignment + /// @param other The other cylinder surface to merge with + /// @param direction The binning direction: either @c binZ or @c binRPhi + /// @param logger The logger to use + /// @return The merged cylinder surface + std::shared_ptr mergedWith( + const GeometryContext& gctx, const CylinderSurface& other, + BinningValue direction, const Logger& logger = getDummyLogger()) const; + protected: std::shared_ptr m_bounds; //!< bounds (shared) diff --git a/Core/include/Acts/Surfaces/DiscSurface.hpp b/Core/include/Acts/Surfaces/DiscSurface.hpp index bb28d5f9a538..172868447632 100644 --- a/Core/include/Acts/Surfaces/DiscSurface.hpp +++ b/Core/include/Acts/Surfaces/DiscSurface.hpp @@ -331,6 +331,19 @@ class DiscSurface : public RegularSurface { ActsMatrix<2, 3> localCartesianToBoundLocalDerivative( const GeometryContext& gctx, const Vector3& position) const final; + /// Merge two disc surfaces into a single one. + /// @image html Disc_Merging.svg + /// @note The surfaces need to be *compatible*, i.e. have disc bounds + /// that align + /// @param gctx The current geometry context object, e.g. alignment + /// @param other The other disc surface to merge with + /// @param direction The binning direction: either @c binR or @c binPhi + /// @param logger The logger to use + /// @return The merged disc surface + std::shared_ptr mergedWith( + const GeometryContext& gctx, const DiscSurface& other, + BinningValue direction, const Logger& logger = getDummyLogger()) const; + protected: std::shared_ptr m_bounds; ///< bounds (shared) }; diff --git a/Core/include/Acts/Surfaces/detail/MergeHelper.hpp b/Core/include/Acts/Surfaces/detail/MergeHelper.hpp new file mode 100644 index 000000000000..08bbd915148d --- /dev/null +++ b/Core/include/Acts/Surfaces/detail/MergeHelper.hpp @@ -0,0 +1,31 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2024 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/Tolerance.hpp" +#include "Acts/Definitions/Units.hpp" +#include "Acts/Utilities/Logger.hpp" +#include "Acts/Utilities/detail/periodic.hpp" + +#include + +namespace Acts::detail { + +/// This helper function calculates the combination +/// of two phi sectors, defined by a phi half-length + +/// a half phi sector in the range [0,pi). The two +/// ranges need to line up, i.e. that one of the sector +/// ends exactly where the other one starts. +std::pair mergedPhiSector( + ActsScalar hlPhi1, ActsScalar avgPhi1, ActsScalar hlPhi2, + ActsScalar avgPhi2, const Logger& logger = getDummyLogger(), + ActsScalar tolerance = s_onSurfaceTolerance); + +} // namespace Acts::detail diff --git a/Core/src/Surfaces/CMakeLists.txt b/Core/src/Surfaces/CMakeLists.txt index ed12621bc1e1..d1c9a0336b0a 100644 --- a/Core/src/Surfaces/CMakeLists.txt +++ b/Core/src/Surfaces/CMakeLists.txt @@ -28,4 +28,5 @@ target_sources( CurvilinearSurface.cpp detail/AlignmentHelper.cpp detail/AnnulusBoundsHelper.cpp + detail/MergeHelper.cpp ) diff --git a/Core/src/Surfaces/CylinderBounds.cpp b/Core/src/Surfaces/CylinderBounds.cpp index 0ae077594414..83a0831f8511 100644 --- a/Core/src/Surfaces/CylinderBounds.cpp +++ b/Core/src/Surfaces/CylinderBounds.cpp @@ -191,3 +191,25 @@ std::vector Acts::CylinderBounds::createCircles( } return vertices; } + +void Acts::CylinderBounds::checkConsistency() noexcept(false) { + if (get(eR) <= 0.) { + throw std::invalid_argument("CylinderBounds: invalid radial setup."); + } + if (get(eHalfLengthZ) <= 0.) { + throw std::invalid_argument("CylinderBounds: invalid length setup."); + } + if (get(eHalfPhiSector) <= 0. || get(eHalfPhiSector) > M_PI) { + throw std::invalid_argument("CylinderBounds: invalid phi sector setup."); + } + if (get(eAveragePhi) != detail::radian_sym(get(eAveragePhi)) && + std::abs(std::abs(get(eAveragePhi)) - M_PI) > s_epsilon) { + throw std::invalid_argument("CylinderBounds: invalid phi positioning."); + } + if (get(eBevelMinZ) != detail::radian_sym(get(eBevelMinZ))) { + throw std::invalid_argument("CylinderBounds: invalid bevel at min Z."); + } + if (get(eBevelMaxZ) != detail::radian_sym(get(eBevelMaxZ))) { + throw std::invalid_argument("CylinderBounds: invalid bevel at max Z."); + } +} diff --git a/Core/src/Surfaces/CylinderSurface.cpp b/Core/src/Surfaces/CylinderSurface.cpp index 269ee9002521..ac88ee667d83 100644 --- a/Core/src/Surfaces/CylinderSurface.cpp +++ b/Core/src/Surfaces/CylinderSurface.cpp @@ -8,17 +8,26 @@ #include "Acts/Surfaces/CylinderSurface.hpp" +#include "Acts/Definitions/Algebra.hpp" +#include "Acts/Definitions/Tolerance.hpp" +#include "Acts/Definitions/Units.hpp" #include "Acts/Geometry/GeometryObject.hpp" +#include "Acts/Surfaces/CylinderBounds.hpp" #include "Acts/Surfaces/SurfaceError.hpp" #include "Acts/Surfaces/detail/AlignmentHelper.hpp" #include "Acts/Surfaces/detail/FacesHelper.hpp" +#include "Acts/Surfaces/detail/MergeHelper.hpp" +#include "Acts/Utilities/BinningType.hpp" #include "Acts/Utilities/Helpers.hpp" #include "Acts/Utilities/Intersection.hpp" #include "Acts/Utilities/ThrowAssert.hpp" +#include "Acts/Utilities/detail/periodic.hpp" #include #include #include +#include +#include #include #include @@ -353,3 +362,137 @@ Acts::CylinderSurface::localCartesianToBoundLocalDerivative( return loc3DToLocBound; } + +std::shared_ptr Acts::CylinderSurface::mergedWith( + const GeometryContext& gctx, const CylinderSurface& other, + BinningValue direction, const Logger& logger) const { + using namespace Acts::UnitLiterals; + + ACTS_DEBUG("Merging cylinder surfaces in " << binningValueNames()[direction] + << " direction") + + Transform3 otherLocal = transform(gctx).inverse() * other.transform(gctx); + + constexpr auto tolerance = s_onSurfaceTolerance; + + // surface cannot have any relative rotation + if (!otherLocal.linear().isApprox(RotationMatrix3::Identity())) { + ACTS_ERROR("CylinderSurface::merge: surfaces have relative rotation"); + throw std::invalid_argument( + "CylinderSurface::merge: surfaces have relative rotation"); + } + + auto checkNoBevel = [&logger](const auto& bounds) { + if (bounds.get(CylinderBounds::eBevelMinZ) != 0.0) { + ACTS_ERROR( + "CylinderVolumeStack requires all volumes to have a bevel angle of " + "0"); + throw std::invalid_argument( + "CylinderVolumeStack requires all volumes to have a bevel angle of " + "0"); + } + + if (bounds.get(CylinderBounds::eBevelMaxZ) != 0.0) { + ACTS_ERROR( + "CylinderVolumeStack requires all volumes to have a bevel angle of " + "0"); + throw std::invalid_argument( + "CylinderVolumeStack requires all volumes to have a bevel angle of " + "0"); + } + }; + + checkNoBevel(bounds()); + checkNoBevel(other.bounds()); + + // radii need to be identical + if (std::abs(bounds().get(CylinderBounds::eR) - + other.bounds().get(CylinderBounds::eR)) > tolerance) { + ACTS_ERROR("CylinderSurface::merge: surfaces have different radii"); + throw std::invalid_argument( + "CylinderSurface::merge: surfaces have different radii"); + } + + ActsScalar r = bounds().get(CylinderBounds::eR); + + // no translation in x/z is allowed + Vector3 translation = otherLocal.translation(); + + if (std::abs(translation[0]) > tolerance || + std::abs(translation[1]) > tolerance) { + ACTS_ERROR( + "CylinderSurface::merge: surfaces have relative translation in x/y"); + throw std::invalid_argument( + "CylinderSurface::merge: surfaces have relative translation in x/y"); + } + + if (direction == Acts::binZ) { + // z shift must match the bounds + + ActsScalar hlZ = bounds().get(CylinderBounds::eHalfLengthZ); + ActsScalar minZ = -hlZ; + ActsScalar maxZ = hlZ; + + ActsScalar zShift = translation[2]; + ActsScalar otherHlZ = other.bounds().get(CylinderBounds::eHalfLengthZ); + ActsScalar otherMinZ = -otherHlZ + zShift; + ActsScalar otherMaxZ = otherHlZ + zShift; + + ACTS_VERBOSE("this: [" << minZ << ", " << maxZ << "] ~> " + << (minZ + maxZ) / 2.0 << " +- " << hlZ); + ACTS_VERBOSE("zShift: " << zShift); + + ACTS_VERBOSE("other: [" << otherMinZ << ", " << otherMaxZ << "] ~> " + << (otherMinZ + otherMaxZ) / 2.0 << " +- " + << otherHlZ); + + if (std::abs(maxZ - otherMinZ) > tolerance && + std::abs(minZ - otherMaxZ) > tolerance) { + ACTS_ERROR("CylinderSurface::merge: surfaces have incompatible z bounds"); + throw std::invalid_argument( + "CylinderSurface::merge: surfaces have incompatible z bounds"); + } + + ActsScalar newMaxZ = std::max(maxZ, otherMaxZ); + ActsScalar newMinZ = std::min(minZ, otherMinZ); + ActsScalar newHlZ = (newMaxZ - newMinZ) / 2.0; + ActsScalar newMidZ = (newMaxZ + newMinZ) / 2.0; + ACTS_VERBOSE("merged: [" << newMinZ << ", " << newMaxZ << "] ~> " << newMidZ + << " +- " << newHlZ); + + auto newBounds = std::make_shared(r, newHlZ); + + Transform3 newTransform = + transform(gctx) * Translation3{Vector3::UnitZ() * newMidZ}; + + return Surface::makeShared(newTransform, newBounds); + + } else if (direction == Acts::binRPhi) { + // no z shift is allowed + if (std::abs(translation[2]) > tolerance) { + ACTS_ERROR( + "CylinderSurface::merge: surfaces have relative translation in z for " + "rPhi merging"); + throw std::invalid_argument( + "CylinderSurface::merge: surfaces have relative translation in z for " + "rPhi merging"); + } + + ActsScalar hlPhi = bounds().get(CylinderBounds::eHalfPhiSector); + ActsScalar avgPhi = bounds().get(CylinderBounds::eAveragePhi); + + ActsScalar otherHlPhi = other.bounds().get(CylinderBounds::eHalfPhiSector); + ActsScalar otherAvgPhi = other.bounds().get(CylinderBounds::eAveragePhi); + + auto [newHlPhi, newAvgPhi] = detail::mergedPhiSector( + hlPhi, avgPhi, otherHlPhi, otherAvgPhi, logger, tolerance); + + auto newBounds = std::make_shared( + r, bounds().get(CylinderBounds::eHalfLengthZ), newHlPhi, newAvgPhi); + + return Surface::makeShared(transform(gctx), newBounds); + } else { + throw std::invalid_argument("CylinderSurface::merge: invalid direction " + + binningValueNames()[direction]); + } +} diff --git a/Core/src/Surfaces/DiscSurface.cpp b/Core/src/Surfaces/DiscSurface.cpp index 006c8decc04c..1ad326da70d2 100644 --- a/Core/src/Surfaces/DiscSurface.cpp +++ b/Core/src/Surfaces/DiscSurface.cpp @@ -9,6 +9,7 @@ #include "Acts/Surfaces/DiscSurface.hpp" #include "Acts/Definitions/Algebra.hpp" +#include "Acts/Definitions/Units.hpp" #include "Acts/Geometry/GeometryObject.hpp" #include "Acts/Surfaces/BoundaryCheck.hpp" #include "Acts/Surfaces/DiscBounds.hpp" @@ -18,7 +19,9 @@ #include "Acts/Surfaces/SurfaceBounds.hpp" #include "Acts/Surfaces/SurfaceError.hpp" #include "Acts/Surfaces/detail/FacesHelper.hpp" +#include "Acts/Surfaces/detail/MergeHelper.hpp" #include "Acts/Surfaces/detail/PlanarHelper.hpp" +#include "Acts/Utilities/BinningType.hpp" #include "Acts/Utilities/Intersection.hpp" #include "Acts/Utilities/JacobianHelpers.hpp" #include "Acts/Utilities/ThrowAssert.hpp" @@ -373,3 +376,122 @@ double Acts::DiscSurface::pathCorrection(const GeometryContext& gctx, /// we can ignore the global position here return 1. / std::abs(normal(gctx).dot(direction)); } + +std::shared_ptr Acts::DiscSurface::mergedWith( + const GeometryContext& gctx, const DiscSurface& other, + BinningValue direction, const Logger& logger) const { + using namespace Acts::UnitLiterals; + + ACTS_DEBUG("Merging disc surfaces in " << binningValueNames()[direction] + << " direction") + + Transform3 otherLocal = transform(gctx).inverse() * other.transform(gctx); + + constexpr auto tolerance = s_onSurfaceTolerance; + + // surface cannot have any relative rotation + if (!otherLocal.linear().isApprox(RotationMatrix3::Identity())) { + ACTS_ERROR("DiscSurface::merge: surfaces have relative rotation"); + throw std::invalid_argument( + "DiscSurface::merge: surfaces have relative rotation"); + } + + Vector3 translation = otherLocal.translation(); + + if (std::abs(translation[0]) > tolerance || + std::abs(translation[1]) > tolerance || + std::abs(translation[2]) > tolerance) { + ACTS_ERROR( + "DiscSurface::merge: surfaces have relative translation in x/y/z"); + throw std::invalid_argument( + "DiscSurface::merge: surfaces have relative translation in x/y/z"); + } + + const auto* bounds = dynamic_cast(m_bounds.get()); + const auto* otherBounds = + dynamic_cast(other.m_bounds.get()); + + if (bounds == nullptr || otherBounds == nullptr) { + ACTS_ERROR("DiscSurface::merge: surfaces have bounds other than radial"); + throw std::invalid_argument( + "DiscSurface::merge: surfaces have bounds other than radial"); + } + + ActsScalar minR = bounds->get(RadialBounds::eMinR); + ActsScalar maxR = bounds->get(RadialBounds::eMaxR); + + ActsScalar hlPhi = bounds->get(RadialBounds::eHalfPhiSector); + ActsScalar avgPhi = bounds->get(RadialBounds::eAveragePhi); + ActsScalar minPhi = detail::radian_sym(-hlPhi + avgPhi); + ActsScalar maxPhi = detail::radian_sym(hlPhi + avgPhi); + + ACTS_VERBOSE(" this: r = [" << minR << ", " << maxR << "]"); + ACTS_VERBOSE(" phi = [" + << minPhi / 1_degree << ", " << maxPhi / 1_degree << "] ~> " + << avgPhi / 1_degree << " +- " << hlPhi / 1_degree); + + ActsScalar otherMinR = otherBounds->get(RadialBounds::eMinR); + ActsScalar otherMaxR = otherBounds->get(RadialBounds::eMaxR); + ActsScalar otherAvgPhi = otherBounds->get(RadialBounds::eAveragePhi); + ActsScalar otherHlPhi = otherBounds->get(RadialBounds::eHalfPhiSector); + ActsScalar otherMinPhi = detail::radian_sym(-otherHlPhi + otherAvgPhi); + ActsScalar otherMaxPhi = detail::radian_sym(otherHlPhi + otherAvgPhi); + + ACTS_VERBOSE("other: r = [" << otherMinR << ", " << otherMaxR << "]"); + ACTS_VERBOSE(" phi = [" << otherMinPhi / 1_degree << ", " + << otherMaxPhi / 1_degree << "] ~> " + << otherAvgPhi / 1_degree << " +- " + << otherHlPhi / 1_degree); + + if (direction == Acts::binR) { + if (std::abs(minR - otherMaxR) > tolerance && + std::abs(maxR - otherMinR) > tolerance) { + ACTS_ERROR("DiscSurface::merge: surfaces are not touching r"); + throw std::invalid_argument( + "DiscSurface::merge: surfaces are not touching in r"); + } + + if (std::abs(avgPhi - otherAvgPhi) > tolerance) { + ACTS_ERROR("DiscSurface::merge: surfaces have different average phi"); + throw std::invalid_argument( + "DiscSurface::merge: surfaces have different average phi"); + } + + if (std::abs(hlPhi - otherHlPhi) > tolerance) { + ACTS_ERROR("DiscSurface::merge: surfaces have different half phi sector"); + throw std::invalid_argument( + "DiscSurface::merge: surfaces have different half phi sector"); + } + + ActsScalar newMinR = std::min(minR, otherMinR); + ActsScalar newMaxR = std::max(maxR, otherMaxR); + ACTS_VERBOSE(" new: r = [" << newMinR << ", " << newMaxR << "]"); + + auto newBounds = + std::make_shared(newMinR, newMaxR, hlPhi, avgPhi); + + return Surface::makeShared(transform(gctx), newBounds); + + } else if (direction == Acts::binPhi) { + if (std::abs(maxR - otherMaxR) > tolerance || + std::abs(minR - otherMinR) > tolerance) { + ACTS_ERROR("DiscSurface::merge: surfaces don't have same r bounds"); + throw std::invalid_argument( + "DiscSurface::merge: surfaces don't have same r bounds"); + } + + auto [newHlPhi, newAvgPhi] = detail::mergedPhiSector( + hlPhi, avgPhi, otherHlPhi, otherAvgPhi, logger, tolerance); + + auto newBounds = + std::make_shared(minR, maxR, newHlPhi, newAvgPhi); + + return Surface::makeShared(transform(gctx), newBounds); + + } else { + ACTS_ERROR("DiscSurface::merge: invalid direction " + << binningValueNames()[direction]); + throw std::invalid_argument("DiscSurface::merge: invalid direction " + + binningValueNames()[direction]); + } +} diff --git a/Core/src/Surfaces/detail/MergeHelper.cpp b/Core/src/Surfaces/detail/MergeHelper.cpp new file mode 100644 index 000000000000..afb55c405705 --- /dev/null +++ b/Core/src/Surfaces/detail/MergeHelper.cpp @@ -0,0 +1,65 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2024 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/Surfaces/detail/MergeHelper.hpp" + +namespace Acts::detail { + +std::pair mergedPhiSector( + ActsScalar hlPhi1, ActsScalar avgPhi1, ActsScalar hlPhi2, + ActsScalar avgPhi2, const Logger& logger, ActsScalar tolerance) { + using namespace Acts::UnitLiterals; + ActsScalar minPhi1 = detail::radian_sym(-hlPhi1 + avgPhi1); + ActsScalar maxPhi1 = detail::radian_sym(hlPhi1 + avgPhi1); + + ACTS_VERBOSE("one: [" << minPhi1 / 1_degree << ", " << maxPhi1 / 1_degree + << "] ~> " << avgPhi1 / 1_degree << " +- " + << hlPhi1 / 1_degree); + + ActsScalar maxPhi2 = detail::radian_sym(hlPhi2 + avgPhi2); + ActsScalar minPhi2 = detail::radian_sym(-hlPhi2 + avgPhi2); + + ACTS_VERBOSE("two: [" << minPhi2 / 1_degree << ", " << maxPhi2 / 1_degree + << "] ~> " << avgPhi2 / 1_degree << " +- " + << hlPhi2 / 1_degree); + + ACTS_VERBOSE("Checking for CCW or CW ordering"); + auto same = [tolerance](ActsScalar a, ActsScalar b) { + return std::abs(a - b) < tolerance; + }; + + ActsScalar newMaxPhi{}, newMinPhi{}; + ActsScalar newHlPhi = hlPhi1 + hlPhi2; + + if (same(minPhi1, maxPhi2)) { + ACTS_VERBOSE("-> CCW ordering: one is 'left' of two"); + + newMinPhi = minPhi2; + newMaxPhi = maxPhi1; + + } else if (same(maxPhi1, minPhi2)) { + ACTS_VERBOSE("-> CW ordering: this is 'right' of other"); + newMinPhi = minPhi1; + newMaxPhi = maxPhi2; + + } else { + ACTS_ERROR("Phi ranges are incompatible"); + throw std::invalid_argument("Phi ranges are incompatible"); + } + + ActsScalar newAvgPhi = detail::radian_sym(newMinPhi + newHlPhi); + + ACTS_VERBOSE("merged: [" << newMinPhi / 1_degree << ", " + << newMaxPhi / 1_degree << "] ~> " + << newAvgPhi / 1_degree << " +- " + << newHlPhi / 1_degree); + + return {newHlPhi, newAvgPhi}; +} + +} // namespace Acts::detail diff --git a/Tests/UnitTests/Core/Surfaces/CylinderSurfaceTests.cpp b/Tests/UnitTests/Core/Surfaces/CylinderSurfaceTests.cpp index d7e770a35bd5..44e5c61a13bd 100644 --- a/Tests/UnitTests/Core/Surfaces/CylinderSurfaceTests.cpp +++ b/Tests/UnitTests/Core/Surfaces/CylinderSurfaceTests.cpp @@ -6,6 +6,7 @@ // 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 #include @@ -23,7 +24,9 @@ #include "Acts/Utilities/BinningType.hpp" #include "Acts/Utilities/Helpers.hpp" #include "Acts/Utilities/Intersection.hpp" +#include "Acts/Utilities/Logger.hpp" #include "Acts/Utilities/Result.hpp" +#include "Acts/Utilities/detail/periodic.hpp" #include #include @@ -33,12 +36,16 @@ #include #include +using namespace Acts::UnitLiterals; + namespace Acts { class AssertionFailureException; } // namespace Acts namespace Acts::Test { +auto logger = Acts::getDefaultLogger("UnitTests", Acts::Logging::VERBOSE); + // Create a test context GeometryContext testContext = GeometryContext(); @@ -325,6 +332,217 @@ BOOST_AUTO_TEST_CASE(CylinderSurfaceBinningPosition) { } } +BOOST_AUTO_TEST_SUITE(CylinderSurfaceMerging) + +BOOST_DATA_TEST_CASE(IncompatibleZDirection, + (boost::unit_test::data::xrange(-135, 180, 45) * + boost::unit_test::data::make(Vector3{0_mm, 0_mm, 0_mm}, + Vector3{20_mm, 0_mm, 0_mm}, + Vector3{0_mm, 20_mm, 0_mm}, + Vector3{20_mm, 20_mm, 0_mm}, + Vector3{0_mm, 0_mm, 20_mm})), + angle, offset) { + Logging::ScopedFailureThreshold ft{Logging::FATAL}; + + Transform3 base = + AngleAxis3(angle * 1_degree, Vector3::UnitX()) * Translation3(offset); + + auto cyl = Surface::makeShared(base, 30_mm, 100_mm); + auto cyl2 = Surface::makeShared( + base * Translation3{Vector3::UnitZ() * 200_mm}, 30_mm, 100_mm); + + BOOST_CHECK_THROW(cyl->mergedWith(testContext, *cyl2, Acts::binPhi, *logger), + std::invalid_argument); + + auto cylShiftedXy = Surface::makeShared( + base * Translation3{Vector3{1_mm, 2_mm, 200_mm}}, 30_mm, 100_mm); + BOOST_CHECK_THROW( + cyl->mergedWith(testContext, *cylShiftedXy, Acts::binZ, *logger), + std::invalid_argument); + + auto cylRotatedZ = Surface::makeShared( + base * AngleAxis3{10_degree, Vector3::UnitZ()} * + Translation3{Vector3::UnitZ() * 200_mm}, + 30_mm, 100_mm); + BOOST_CHECK_THROW( + cyl->mergedWith(testContext, *cylRotatedZ, Acts::binZ, *logger), + std::invalid_argument); + + auto cylRotatedX = Surface::makeShared( + base * AngleAxis3{10_degree, Vector3::UnitX()} * + Translation3{Vector3::UnitZ() * 200_mm}, + 30_mm, 100_mm); + BOOST_CHECK_THROW( + cyl->mergedWith(testContext, *cylRotatedX, Acts::binZ, *logger), + std::invalid_argument); + + // Cylinder with different radius + auto cyl3 = Surface::makeShared( + base * Translation3{Vector3::UnitZ() * 200_mm}, 35_mm, 100_mm); + BOOST_CHECK_THROW(cyl->mergedWith(testContext, *cyl3, Acts::binZ, *logger), + std::invalid_argument); + + // Cylinder with bevel + auto cyl4 = Surface::makeShared( + base * Translation3{Vector3::UnitZ() * 200_mm}, 30_mm, 100_mm, M_PI, 0, + M_PI / 8.0); + BOOST_CHECK_THROW(cyl->mergedWith(testContext, *cyl4, Acts::binZ, *logger), + std::invalid_argument); + + auto cyl5 = Surface::makeShared( + base * Translation3{Vector3::UnitZ() * 200_mm}, 30_mm, 100_mm, M_PI, 0, 0, + M_PI / 8.0); + BOOST_CHECK_THROW(cyl->mergedWith(testContext, *cyl5, Acts::binZ, *logger), + std::invalid_argument); + + // Cylinder with overlap in z + auto cyl6 = Surface::makeShared( + base * Translation3{Vector3::UnitZ() * 150_mm}, 30_mm, 100_mm); + BOOST_CHECK_THROW(cyl->mergedWith(testContext, *cyl6, Acts::binZ, *logger), + std::invalid_argument); + + // Cylinder with gap in z + auto cyl7 = Surface::makeShared( + base * Translation3{Vector3::UnitZ() * 250_mm}, 30_mm, 100_mm); + BOOST_CHECK_THROW(cyl->mergedWith(testContext, *cyl7, Acts::binZ, *logger), + std::invalid_argument); +} + +BOOST_DATA_TEST_CASE(ZDirection, + (boost::unit_test::data::xrange(-135, 180, 45) * + boost::unit_test::data::make(Vector3{0_mm, 0_mm, 0_mm}, + Vector3{20_mm, 0_mm, 0_mm}, + Vector3{0_mm, 20_mm, 0_mm}, + Vector3{20_mm, 20_mm, 0_mm}, + Vector3{0_mm, 0_mm, 20_mm})), + angle, offset) { + Transform3 base = + AngleAxis3(angle * 1_degree, Vector3::UnitX()) * Translation3(offset); + + auto cyl = Surface::makeShared(base, 30_mm, 100_mm); + + auto cyl2 = Surface::makeShared( + base * Translation3{Vector3::UnitZ() * 200_mm}, 30_mm, 100_mm); + + auto cyl3 = cyl->mergedWith(testContext, *cyl2, Acts::binZ, *logger); + BOOST_REQUIRE_NE(cyl3, nullptr); + + auto cyl3Reversed = cyl2->mergedWith(testContext, *cyl, Acts::binZ, *logger); + BOOST_REQUIRE_NE(cyl3Reversed, nullptr); + BOOST_CHECK(*cyl3 == *cyl3Reversed); + + auto bounds = cyl3->bounds(); + + BOOST_CHECK_EQUAL(bounds.get(CylinderBounds::eR), 30_mm); + BOOST_CHECK_EQUAL(bounds.get(CylinderBounds::eHalfLengthZ), 200_mm); + + Transform3 expected = base * Translation3{Vector3::UnitZ() * 100_mm}; + BOOST_CHECK_EQUAL(expected.matrix(), cyl3->transform(testContext).matrix()); +} + +BOOST_DATA_TEST_CASE(IncompatibleRPhiDirection, + (boost::unit_test::data::xrange(-135, 180, 45) * + boost::unit_test::data::make(Vector3{0_mm, 0_mm, 0_mm}, + Vector3{20_mm, 0_mm, 0_mm}, + Vector3{0_mm, 20_mm, 0_mm}, + Vector3{20_mm, 20_mm, 0_mm}, + Vector3{0_mm, 0_mm, 20_mm}) * + boost::unit_test::data::xrange(-1300, 1300, 104)), + angle, offset, phiShift) { + Logging::ScopedFailureThreshold ft{Logging::FATAL}; + Transform3 base = + AngleAxis3(angle * 1_degree, Vector3::UnitX()) * Translation3(offset); + + auto a = [phiShift](ActsScalar v) { + return detail::radian_sym(v + phiShift * 1_degree); + }; + + auto cylPhi = Surface::makeShared(base, 30_mm, 100_mm, + 10_degree, a(40_degree)); + + // Cylinder with overlap in phi + auto cylPhi2 = Surface::makeShared(base, 30_mm, 100_mm, + 45_degree, a(85_degree)); + BOOST_CHECK_THROW( + cylPhi->mergedWith(testContext, *cylPhi2, Acts::binRPhi, *logger), + std::invalid_argument); + + // Cylinder with gap in phi + auto cylPhi3 = Surface::makeShared(base, 30_mm, 100_mm, + 45_degree, a(105_degree)); + BOOST_CHECK_THROW( + cylPhi->mergedWith(testContext, *cylPhi3, Acts::binRPhi, *logger), + std::invalid_argument); + + // Cylinder with a z shift + auto cylPhi4 = Surface::makeShared( + base * Translation3{Vector3::UnitZ() * 20_mm}, 30_mm, 100_mm, 45_degree, + a(95_degree)); + BOOST_CHECK_THROW( + cylPhi->mergedWith(testContext, *cylPhi4, Acts::binRPhi, *logger), + std::invalid_argument); +} + +BOOST_DATA_TEST_CASE(RPhiDirection, + (boost::unit_test::data::xrange(-135, 180, 45) * + boost::unit_test::data::make(Vector3{0_mm, 0_mm, 0_mm}, + Vector3{20_mm, 0_mm, 0_mm}, + Vector3{0_mm, 20_mm, 0_mm}, + Vector3{20_mm, 20_mm, 0_mm}, + Vector3{0_mm, 0_mm, 20_mm}) * + boost::unit_test::data::xrange(-1300, 1300, 104)), + angle, offset, phiShift) { + Transform3 base = + AngleAxis3(angle * 1_degree, Vector3::UnitX()) * Translation3(offset); + + auto a = [phiShift](ActsScalar v) { + return detail::radian_sym(v + phiShift * 1_degree); + }; + + auto cyl = Surface::makeShared(base, 30_mm, 100_mm, + 10_degree, a(40_degree)); + auto cyl2 = Surface::makeShared(base, 30_mm, 100_mm, + 45_degree, a(95_degree)); + + auto cyl3 = cyl->mergedWith(testContext, *cyl2, Acts::binRPhi, *logger); + BOOST_REQUIRE_NE(cyl3, nullptr); + BOOST_CHECK_EQUAL(base.matrix(), cyl3->transform(testContext).matrix()); + + auto cyl3Reversed = + cyl2->mergedWith(testContext, *cyl, Acts::binRPhi, *logger); + BOOST_REQUIRE_NE(cyl3Reversed, nullptr); + BOOST_CHECK(*cyl3 == *cyl3Reversed); + + const auto& bounds = cyl3->bounds(); + + BOOST_CHECK_SMALL( + detail::difference_periodic(bounds.get(CylinderBounds::eAveragePhi), + a(85_degree), 2 * M_PI), + 1e-6); + BOOST_CHECK_CLOSE(bounds.get(CylinderBounds::eHalfPhiSector), 55_degree, 0.1); + + auto cyl4 = Surface::makeShared(base, 30_mm, 100_mm, + 20_degree, a(170_degree)); + auto cyl5 = Surface::makeShared(base, 30_mm, 100_mm, + 10_degree, a(-160_degree)); + auto cyl45 = cyl4->mergedWith(testContext, *cyl5, Acts::binRPhi, *logger); + BOOST_REQUIRE_NE(cyl45, nullptr); + BOOST_CHECK_EQUAL(base.matrix(), cyl45->transform(testContext).matrix()); + + auto cyl54 = cyl5->mergedWith(testContext, *cyl4, Acts::binRPhi, *logger); + BOOST_REQUIRE_NE(cyl54, nullptr); + + BOOST_CHECK(*cyl54 == *cyl45); + + BOOST_CHECK_SMALL(detail::difference_periodic( + cyl45->bounds().get(CylinderBounds::eAveragePhi), + a(180_degree), 2 * M_PI), + 1e-6); + BOOST_CHECK_CLOSE(cyl45->bounds().get(CylinderBounds::eHalfPhiSector), + 30_degree, 0.1); +} + +BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END() } // namespace Acts::Test diff --git a/Tests/UnitTests/Core/Surfaces/DiscSurfaceTests.cpp b/Tests/UnitTests/Core/Surfaces/DiscSurfaceTests.cpp index 0785897f6710..ea5e8285e99d 100644 --- a/Tests/UnitTests/Core/Surfaces/DiscSurfaceTests.cpp +++ b/Tests/UnitTests/Core/Surfaces/DiscSurfaceTests.cpp @@ -6,6 +6,7 @@ // 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 #include @@ -37,6 +38,8 @@ #include #include +using namespace Acts::UnitLiterals; + namespace Acts { class AssertionFailureException; } // namespace Acts @@ -44,6 +47,7 @@ class AssertionFailureException; namespace Acts::Test { // Create a test context GeometryContext tgContext = GeometryContext(); +auto logger = Acts::getDefaultLogger("UnitTests", Acts::Logging::VERBOSE); BOOST_AUTO_TEST_SUITE(Surfaces) /// Unit tests for creating DiscSurface object @@ -372,6 +376,222 @@ BOOST_AUTO_TEST_CASE(DiscSurfaceBinningPosition) { } } +BOOST_AUTO_TEST_SUITE(DiscSurfaceMerging) + +namespace { +std::shared_ptr makeDisc(const Transform3& transform, double rmin, + double rmax, double halfPhi = M_PI, + double avgPhi = 0) { + return Surface::makeShared( + transform, std::make_shared(rmin, rmax, halfPhi, avgPhi)); +} + +} // namespace + +BOOST_AUTO_TEST_CASE(IncompatibleBounds) { + Logging::ScopedFailureThreshold ft{Logging::FATAL}; + Transform3 base = Transform3::Identity(); + auto discRadial = makeDisc(base, 30_mm, 100_mm); + auto discTrap = + Surface::makeShared(base, 20_mm, 40_mm, 100_mm, 150_mm); + auto discTrap2 = + Surface::makeShared(base, 20_mm, 40_mm, 30_mm, 100_mm); + + BOOST_CHECK_THROW(discRadial->mergedWith(tgContext, *discTrap, binR, *logger), + std::invalid_argument); + + BOOST_CHECK_THROW(discTrap2->mergedWith(tgContext, *discTrap, binR, *logger), + std::invalid_argument); +} + +BOOST_DATA_TEST_CASE(IncompatibleRDirection, + (boost::unit_test::data::xrange(-135, 180, 45) * + boost::unit_test::data::make(Vector3{0_mm, 0_mm, 0_mm}, + Vector3{20_mm, 0_mm, 0_mm}, + Vector3{0_mm, 20_mm, 0_mm}, + Vector3{20_mm, 20_mm, 0_mm}, + Vector3{0_mm, 0_mm, 20_mm})), + angle, offset) { + Logging::ScopedFailureThreshold ft{Logging::FATAL}; + + Transform3 base = + AngleAxis3(angle * 1_degree, Vector3::UnitX()) * Translation3(offset); + + auto disc = makeDisc(base, 30_mm, 100_mm); + + // Disc with overlap in r + auto discOverlap = makeDisc(base, 90_mm, 150_mm); + BOOST_CHECK_THROW( + disc->mergedWith(tgContext, *discOverlap, Acts::binR, *logger), + std::invalid_argument); + + // Disc with gap in r + auto discGap = makeDisc(base, 110_mm, 150_mm); + BOOST_CHECK_THROW(disc->mergedWith(tgContext, *discGap, Acts::binR, *logger), + std::invalid_argument); + + auto discShiftedZ = Surface::makeShared( + base * Translation3{Vector3::UnitZ() * 10_mm}, 100_mm, 150_mm); + BOOST_CHECK_THROW( + disc->mergedWith(tgContext, *discShiftedZ, Acts::binR, *logger), + std::invalid_argument); + + auto discShiftedXy = makeDisc( + base * Translation3{Vector3{1_mm, 2_mm, 200_mm}}, 100_mm, 150_mm); + BOOST_CHECK_THROW( + disc->mergedWith(tgContext, *discShiftedXy, Acts::binZ, *logger), + std::invalid_argument); + + auto discRotatedZ = + makeDisc(base * AngleAxis3{10_degree, Vector3::UnitZ()}, 100_mm, 150_mm); + BOOST_CHECK_THROW( + disc->mergedWith(tgContext, *discRotatedZ, Acts::binR, *logger), + std::invalid_argument); + auto discRotatedX = + makeDisc(base * AngleAxis3{10_degree, Vector3::UnitX()}, 100_mm, 150_mm); + BOOST_CHECK_THROW( + disc->mergedWith(tgContext, *discRotatedX, Acts::binR, *logger), + std::invalid_argument); +} + +BOOST_DATA_TEST_CASE(RDirection, + (boost::unit_test::data::xrange(-135, 180, 45) * + boost::unit_test::data::make(Vector3{0_mm, 0_mm, 0_mm}, + Vector3{20_mm, 0_mm, 0_mm}, + Vector3{0_mm, 20_mm, 0_mm}, + Vector3{20_mm, 20_mm, 0_mm}, + Vector3{0_mm, 0_mm, 20_mm})), + angle, offset) { + Transform3 base = + AngleAxis3(angle * 1_degree, Vector3::UnitX()) * Translation3(offset); + + auto disc = makeDisc(base, 30_mm, 100_mm); + + auto disc2 = makeDisc(base, 100_mm, 150_mm); + + auto disc3 = disc->mergedWith(tgContext, *disc2, Acts::binR, *logger); + BOOST_REQUIRE_NE(disc3, nullptr); + + auto disc3Reversed = disc2->mergedWith(tgContext, *disc, Acts::binR, *logger); + BOOST_REQUIRE_NE(disc3Reversed, nullptr); + BOOST_CHECK(*disc3 == *disc3Reversed); + + const auto* bounds = dynamic_cast(&disc3->bounds()); + BOOST_REQUIRE_NE(bounds, nullptr); + + BOOST_CHECK_EQUAL(bounds->get(RadialBounds::eMinR), 30_mm); + BOOST_CHECK_EQUAL(bounds->get(RadialBounds::eMaxR), 150_mm); + + // Disc did not move + BOOST_CHECK_EQUAL(base.matrix(), disc3->transform(tgContext).matrix()); +} + +BOOST_DATA_TEST_CASE(IncompatiblePhiDirection, + (boost::unit_test::data::xrange(-135, 180, 45) * + boost::unit_test::data::make(Vector3{0_mm, 0_mm, 0_mm}, + Vector3{20_mm, 0_mm, 0_mm}, + Vector3{0_mm, 20_mm, 0_mm}, + Vector3{20_mm, 20_mm, 0_mm}, + Vector3{0_mm, 0_mm, 20_mm}) * + boost::unit_test::data::xrange(-1300, 1300, 104)), + angle, offset, phiShift) { + Logging::ScopedFailureThreshold ft{Logging::FATAL}; + Transform3 base = + AngleAxis3(angle * 1_degree, Vector3::UnitX()) * Translation3(offset); + + auto a = [phiShift](ActsScalar v) { + return detail::radian_sym(v + phiShift * 1_degree); + }; + + auto discPhi = makeDisc(base, 30_mm, 100_mm, 10_degree, a(40_degree)); + + // Disc with overlap in phi + auto discPhi2 = makeDisc(base, 30_mm, 100_mm, 45_degree, a(85_degree)); + BOOST_CHECK_THROW( + discPhi->mergedWith(tgContext, *discPhi2, Acts::binPhi, *logger), + std::invalid_argument); + + // Disc with gap in phi + auto discPhi3 = makeDisc(base, 30_mm, 100_mm, 45_degree, a(105_degree)); + BOOST_CHECK_THROW( + discPhi->mergedWith(tgContext, *discPhi3, Acts::binPhi, *logger), + std::invalid_argument); + + // Disc with a z shift + auto discPhi4 = makeDisc(base * Translation3{Vector3::UnitZ() * 20_mm}, 30_mm, + 100_mm, 45_degree, a(95_degree)); + BOOST_CHECK_THROW( + discPhi->mergedWith(tgContext, *discPhi4, Acts::binPhi, *logger), + std::invalid_argument); + + // Disc with different r bounds: could be merged in r but not in phi + auto discPhi5 = makeDisc(base, 100_mm, 150_mm, 45_degree, a(95_degree)); + BOOST_CHECK_THROW( + discPhi->mergedWith(tgContext, *discPhi5, Acts::binPhi, *logger), + std::invalid_argument); +} + +BOOST_DATA_TEST_CASE(PhiDirection, + (boost::unit_test::data::xrange(-135, 180, 45) * + boost::unit_test::data::make(Vector3{0_mm, 0_mm, 0_mm}, + Vector3{20_mm, 0_mm, 0_mm}, + Vector3{0_mm, 20_mm, 0_mm}, + Vector3{20_mm, 20_mm, 0_mm}, + Vector3{0_mm, 0_mm, 20_mm}) * + boost::unit_test::data::xrange(-1300, 1300, 104)), + angle, offset, phiShift) { + Transform3 base = + AngleAxis3(angle * 1_degree, Vector3::UnitX()) * Translation3(offset); + + auto a = [phiShift](ActsScalar v) { + return detail::radian_sym(v + phiShift * 1_degree); + }; + + auto disc = makeDisc(base, 30_mm, 100_mm, 10_degree, a(40_degree)); + auto disc2 = makeDisc(base, 30_mm, 100_mm, 45_degree, a(95_degree)); + + auto disc3 = disc->mergedWith(tgContext, *disc2, Acts::binPhi, *logger); + BOOST_REQUIRE_NE(disc3, nullptr); + BOOST_CHECK_EQUAL(base.matrix(), disc3->transform(tgContext).matrix()); + + auto disc3Reversed = + disc2->mergedWith(tgContext, *disc, Acts::binPhi, *logger); + BOOST_REQUIRE_NE(disc3Reversed, nullptr); + BOOST_CHECK(*disc3 == *disc3Reversed); + + const auto* bounds = dynamic_cast(&disc3->bounds()); + BOOST_REQUIRE_NE(bounds, nullptr); + + BOOST_CHECK_SMALL( + detail::difference_periodic(bounds->get(RadialBounds::eAveragePhi), + a(85_degree), 2 * M_PI), + 1e-6); + BOOST_CHECK_CLOSE(bounds->get(RadialBounds::eHalfPhiSector), 55_degree, 0.1); + + auto disc4 = makeDisc(base, 30_mm, 100_mm, 20_degree, a(170_degree)); + auto disc5 = makeDisc(base, 30_mm, 100_mm, 10_degree, a(-160_degree)); + auto disc45 = disc4->mergedWith(tgContext, *disc5, Acts::binPhi, *logger); + BOOST_REQUIRE_NE(disc45, nullptr); + BOOST_CHECK_EQUAL(base.matrix(), disc45->transform(tgContext).matrix()); + + auto disc54 = disc5->mergedWith(tgContext, *disc4, Acts::binPhi, *logger); + BOOST_REQUIRE_NE(disc54, nullptr); + + BOOST_CHECK(*disc54 == *disc45); + + const auto* bounds45 = dynamic_cast(&disc45->bounds()); + BOOST_REQUIRE_NE(bounds, nullptr); + + BOOST_CHECK_SMALL( + detail::difference_periodic(bounds45->get(RadialBounds::eAveragePhi), + a(180_degree), 2 * M_PI), + 1e-6); + BOOST_CHECK_CLOSE(bounds45->get(RadialBounds::eHalfPhiSector), 30_degree, + 0.1); +} + +BOOST_AUTO_TEST_SUITE_END() + BOOST_AUTO_TEST_SUITE_END() } // namespace Acts::Test diff --git a/docs/core/geometry/surfaces.md b/docs/core/geometry/surfaces.md index 55a0d41039ce..0b634a670886 100644 --- a/docs/core/geometry/surfaces.md +++ b/docs/core/geometry/surfaces.md @@ -9,14 +9,11 @@ that inherit from {class}`Acts::SurfaceBounds`, which every surface must provide In case of boundless surfaces, a special {class}`Acts::InfiniteBounds` class is available. - - Each {class}`Acts::Surface` instance reports its type from {func}`Acts::Surface::type()`: :::{doxygenenum} Acts::Surface::SurfaceType ::: - | Surface Type | Local Coordinates | Bound Types available | |:---------------------------------------------------------------|-------------------|:--------------------------------------------------------------------------------------------------------------------------------| | {class}`Acts::ConeSurface` | $[r\phi, z]$ | {class}`Acts::ConeBounds` | @@ -40,8 +37,10 @@ transformations. :::{doxygenclass} Acts::PlaneSurface --- + members: globalToLocal,localToGlobal,intersect,normal --- + ::: ## Disc surface @@ -50,26 +49,32 @@ members: globalToLocal,localToGlobal,intersect,normal :::{doxygenclass} Acts::DiscSurface --- -members: globalToLocal,localToGlobal,intersect,normal + +members: globalToLocal,localToGlobal,intersect,normal,mergedWith --- + ::: - + ## Cylinder surface ![CylinderBounds](figures/CylinderBounds.png) :::{doxygenclass} Acts::CylinderSurface --- -members: globalToLocal,localToGlobal,intersect,normal + +members: globalToLocal,localToGlobal,intersect,normal,mergedWith --- + ::: ## Cone surface :::{doxygenclass} Acts::ConeSurface --- + members: globalToLocal,localToGlobal,intersect,normal --- + ::: ## Line surface @@ -84,26 +89,31 @@ It is pure-virtual, meaning that it can not be instantiated on its own. :::{doxygenclass} Acts::LineSurface --- + members: globalToLocal,localToGlobal,intersect,normal --- + ::: ### Straw surface :::{doxygenclass} Acts::StrawSurface --- + members: false --- + ::: ### Perigee surface :::{doxygenclass} Acts::PerigeeSurface --- + members: false --- -::: +::: ## API listings diff --git a/docs/figures/doxygen/Cylinder_Merging.svg b/docs/figures/doxygen/Cylinder_Merging.svg new file mode 100644 index 000000000000..4478bba85fb2 --- /dev/null +++ b/docs/figures/doxygen/Cylinder_Merging.svg @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + + + + 2 + + + + + 3 + + + + diff --git a/docs/figures/doxygen/Disc_Merging.svg b/docs/figures/doxygen/Disc_Merging.svg new file mode 100644 index 000000000000..8d6b40944007 --- /dev/null +++ b/docs/figures/doxygen/Disc_Merging.svg @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + + + + 2 + + + + + 3 + + + + + + + + + + + + From 06c2b631c7bfedc2383d7bb777e6dca329370b06 Mon Sep 17 00:00:00 2001 From: "Alexander J. Pfleger" <70842573+AJPfleger@users.noreply.github.com> Date: Sat, 15 Jun 2024 08:44:21 +0200 Subject: [PATCH 3/5] refactor(gx2f): clean up logging before material PR (#3294) blocked by: - https://github.com/acts-project/acts/pull/3293 --- .../TrackFitting/GlobalChiSquareFitter.hpp | 75 +++++++------------ 1 file changed, 28 insertions(+), 47 deletions(-) diff --git a/Core/include/Acts/TrackFitting/GlobalChiSquareFitter.hpp b/Core/include/Acts/TrackFitting/GlobalChiSquareFitter.hpp index 6b0e10dc86eb..71066e0f0e94 100644 --- a/Core/include/Acts/TrackFitting/GlobalChiSquareFitter.hpp +++ b/Core/include/Acts/TrackFitting/GlobalChiSquareFitter.hpp @@ -1,6 +1,6 @@ // This file is part of the Acts project. // -// Copyright (C) 2023 CERN for the benefit of the Acts project +// Copyright (C) 2023-2024 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 @@ -56,7 +56,7 @@ constexpr TrackStatePropMask trackStateMask = TrackStatePropMask::Predicted | TrackStatePropMask::Calibrated; } // namespace Gx2fConstants -/// Extension struct which holds delegates to customize the KF behavior +/// Extension struct which holds delegates to customise the GX2F behaviour template struct Gx2FitterExtensions { using TrackStateProxy = typename MultiTrajectory::TrackStateProxy; @@ -243,13 +243,17 @@ void addToGx2fSums(BoundMatrix& aMatrix, BoundVector& bVector, double& chi2sum, const BoundMatrix& jacobianFromStart, const track_state_t& trackState, const Logger& logger) { BoundVector predicted = trackState.predicted(); + ActsVector measurement = trackState.template calibrated(); + ActsSquareMatrix covarianceMeasurement = trackState.template calibratedCovariance(); + ActsMatrix projector = trackState.projector().template topLeftCorner(); ActsMatrix projJacobian = projector * jacobianFromStart; + ActsMatrix projPredicted = projector * predicted; ActsVector residual = measurement - projPredicted; @@ -445,26 +449,23 @@ class Gx2Fitter { auto surface = navigator.currentSurface(state.navigation); if (surface != nullptr) { ++result.surfaceCount; - ACTS_VERBOSE("Surface " << surface->geometryId() << " detected."); + const GeometryIdentifier geoId = surface->geometryId(); + ACTS_DEBUG("Surface " << geoId << " detected."); + + // Found material + if (surface->surfaceMaterial() != nullptr) { + ACTS_DEBUG(" The surface contains material."); + } // Check if we have a measurement surface - if (auto sourcelink_it = inputMeasurements->find(surface->geometryId()); + if (auto sourcelink_it = inputMeasurements->find(geoId); sourcelink_it != inputMeasurements->end()) { - ACTS_VERBOSE("Measurement surface " << surface->geometryId() - << " detected."); + ACTS_DEBUG(" The surface contains a measurement."); // Transport the covariance to the surface stepper.transportCovarianceToBound(state.stepping, *surface, freeToBoundCorrection); - ACTS_VERBOSE( - "Actor - indices before processing:" - << "\n " - << "result.lastMeasurementIndex: " << result.lastMeasurementIndex - << "\n " - << "result.lastTrackIndex: " << result.lastTrackIndex << "\n " - << "result.fittedStates->size(): " << result.fittedStates->size()) - // TODO generalize the update of the currentTrackIndex auto& fittedStates = *result.fittedStates; @@ -473,7 +474,7 @@ class Gx2Fitter { typename traj_t::TrackStateProxy trackStateProxy = fittedStates.makeTrackState(Gx2fConstants::trackStateMask, result.lastTrackIndex); - std::size_t currentTrackIndex = trackStateProxy.index(); + const std::size_t currentTrackIndex = trackStateProxy.index(); // Set the trackStateProxy components with the state from the ongoing // propagation @@ -512,15 +513,7 @@ class Gx2Fitter { typeFlags.set(TrackStateFlag::MeasurementFlag); // We count the processed measurement ++result.processedMeasurements; - ACTS_VERBOSE("Actor - indices after processing, before over writing:" - << "\n " - << "result.lastMeasurementIndex: " - << result.lastMeasurementIndex << "\n " - << "trackStateProxy.index(): " << trackStateProxy.index() - << "\n " - << "result.lastTrackIndex: " << result.lastTrackIndex - << "\n " - << "currentTrackIndex: " << currentTrackIndex) + result.lastMeasurementIndex = currentTrackIndex; result.lastTrackIndex = currentTrackIndex; @@ -542,12 +535,9 @@ class Gx2Fitter { << " detected."); // We only create track states here if there is already a measurement - // detected or if the surface has material (no holes before the first - // measurement) - if (result.measurementStates > 0 - // || surface->surfaceMaterial() != nullptr - ) { - ACTS_VERBOSE("Handle hole."); + // detected (no holes before the first measurement) + if (result.measurementStates > 0) { + ACTS_DEBUG(" Handle hole."); auto& fittedStates = *result.fittedStates; @@ -556,7 +546,8 @@ class Gx2Fitter { typename traj_t::TrackStateProxy trackStateProxy = fittedStates.makeTrackState(Gx2fConstants::trackStateMask, result.lastTrackIndex); - std::size_t currentTrackIndex = trackStateProxy.index(); + const std::size_t currentTrackIndex = trackStateProxy.index(); + { // Set the trackStateProxy components with the state from the // ongoing propagation @@ -597,16 +588,6 @@ class Gx2Fitter { } } - ACTS_VERBOSE( - "Actor - indices after processing, before over writing:" - << "\n " - << "result.lastMeasurementIndex: " - << result.lastMeasurementIndex << "\n " - << "trackStateProxy.index(): " << trackStateProxy.index() - << "\n " - << "result.lastTrackIndex: " << result.lastTrackIndex - << "\n " - << "currentTrackIndex: " << currentTrackIndex) result.lastTrackIndex = currentTrackIndex; if (trackStateProxy.typeFlags().test(TrackStateFlag::HoleFlag)) { @@ -616,7 +597,7 @@ class Gx2Fitter { ++result.processedStates; } else { - ACTS_VERBOSE("Ignoring hole, because no preceding measurements."); + ACTS_DEBUG(" Ignoring hole, because no preceding measurements."); } if (surface->surfaceMaterial() != nullptr) { @@ -626,12 +607,12 @@ class Gx2Fitter { // MaterialUpdateStage::FullUpdate); } } else { - ACTS_INFO("Actor: This case is not implemented yet") + ACTS_INFO("Surface " << geoId << " has no measurement/material/hole.") } } - ACTS_DEBUG("result.processedMeasurements: " - << result.processedMeasurements << "\n" - << "inputMeasurements.size(): " << inputMeasurements->size()) + ACTS_VERBOSE("result.processedMeasurements: " + << result.processedMeasurements << "\n" + << "inputMeasurements.size(): " << inputMeasurements->size()) if (result.processedMeasurements >= inputMeasurements->size()) { ACTS_INFO("Actor: finish: all measurements found."); result.finished = true; @@ -693,7 +674,7 @@ class Gx2Fitter { const -> std::enable_if_t< !_isdn, Result::TrackProxy>> { - // Preprocess Measurements (Sourcelinks -> map) + // Preprocess Measurements (SourceLinks -> map) // To be able to find measurements later, we put them into a map // We need to copy input SourceLinks anyway, so the map can own them. ACTS_VERBOSE("Preparing " << std::distance(it, end) From 1e0f2f1621be41dd5e32558f2c7a8de537e40717 Mon Sep 17 00:00:00 2001 From: "Alexander J. Pfleger" <70842573+AJPfleger@users.noreply.github.com> Date: Sun, 16 Jun 2024 11:08:11 +0200 Subject: [PATCH 4/5] fix(gx2f): forward propagation error (#3296) Somehow the GX2F was so perfect, that no errors occurred until I got today ``` terminate called after throwing an instance of 'std::bad_variant_access' what(): std::get: wrong index for variant ``` --- Core/include/Acts/TrackFitting/GlobalChiSquareFitter.hpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Core/include/Acts/TrackFitting/GlobalChiSquareFitter.hpp b/Core/include/Acts/TrackFitting/GlobalChiSquareFitter.hpp index 71066e0f0e94..8c8417826cc3 100644 --- a/Core/include/Acts/TrackFitting/GlobalChiSquareFitter.hpp +++ b/Core/include/Acts/TrackFitting/GlobalChiSquareFitter.hpp @@ -767,6 +767,11 @@ class Gx2Fitter { propagationResult, propagatorOptions, false); + if (!result.ok()) { + ACTS_ERROR("Propagation failed: " << result.error()); + return result.error(); + } + // TODO Improve Propagator + Actor [allocate before loop], rewrite // makeMeasurements auto& propRes = *result; From 7e1e81901c490be5f7554c60c0e483651c33cc12 Mon Sep 17 00:00:00 2001 From: "Alexander J. Pfleger" <70842573+AJPfleger@users.noreply.github.com> Date: Sun, 16 Jun 2024 13:48:21 +0200 Subject: [PATCH 5/5] docs: update links to avoid redirects (#3297) I didn't update - doxygen.org -> doxygen.nl, because the org page is their official intention - http://mozilla.org/MPL/2.0/ -> https://www.mozilla.org/en-US/MPL/2.0/, because we have it in all headers like this. ## From Docs Build - (contribution/guide: line 138) redirect http://alistapart.com/article/the-art-of-the-commit - permanently to https://alistapart.com/article/the-art-of-the-commit/ - (contribution/guide: line 78) redirect http://git-scm.com/docs/gittutorial - permanently to https://git-scm.com/docs/gittutorial - (contribution/guide: line 138) redirect http://chris.beams.io/posts/git-commit/ - permanently to https://cbea.ms/git-commit/ - ( getting_started: line 5) redirect http://boost.org - permanently to https://www.boost.org/ - ( getting_started: line 37) redirect http://geant4.org/ - permanently to https://geant4.org:443/ - (contribution/guide: line 156) redirect http://victorlin.me/posts/2013/09/30/keep-a-readable-git-history - permanently to https://fangpenlin.com/posts/2013/09/30/keep-a-readable-git-history/ - (contribution/guide: line 78) redirect http://www.atlassian.com/git/ - permanently to https://www.atlassian.com/git - (contribution/profiling: line 45) redirect http://www.graphviz.org/download/ - permanently to https://www.graphviz.org/download/ - (contribution/guide: line 184) redirect http://clang.llvm.org/docs/ClangFormat.html - with Found to https://clang.llvm.org/docs/ClangFormat.html - (contribution/guide: line 184) redirect http://clang.llvm.org/docs/ClangFormat.html#emacs-integration - with Found to https://clang.llvm.org/docs/ClangFormat.html - ( getting_started: line 336) redirect https://cmake.org/Wiki/CMake_Useful_Variables - permanently to https://cmake.org/rdrctcm.php?old_link=CMake_Useful_Variables - (contribution/guide: line 156) redirect https://developer.atlassian.com/blog/2016/04/stop-foxtrots-now/ - permanently to https://www.atlassian.com/blog/developer - ( getting_started: line 365) redirect https://git-lfs.github.com/ - permanently to https://git-lfs.com/ - (contribution/guide: line 25) redirect https://github.com/acts-project/acts-core/issues - permanently to https://github.com/acts-project/acts/issues - (contribution/guide: line 267) redirect https://github.com/acts-project/acts-core/pulls - permanently to https://github.com/acts-project/acts/pulls - (white_papers/correction-for-transport-jacobian: line 7) redirect https://github.com/beomki-yeo/path-correction-term-from-momentum-variation - permanently to https://github.com/acts-project/path-correction-term-from-momentum-variation - ( acts_project: line 3) redirect https://gitlab.cern.ch/atlas/athena/-/tree/master/Tracking - with Found to https://gitlab.cern.ch/atlas/athena/-/tree/main/Tracking - (white_papers/how_to_add: line 44) redirect https://overleaf.com - permanently to https://www.overleaf.com/ --- CONTRIBUTING.rst | 20 ++++++++++---------- Core/include/Acts/Utilities/BoundingBox.hpp | 2 +- Core/include/Acts/Utilities/BoundingBox.ipp | 2 +- README.md | 2 +- docs/acts_project.md | 2 +- docs/conf.py | 2 +- docs/contribution/profiling.md | 2 +- docs/getting_started.md | 12 ++++++------ docs/white_papers.toml | 2 +- docs/white_papers/how_to_add.md | 2 +- 10 files changed, 24 insertions(+), 24 deletions(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index d846d2e5e213..95da66688e32 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -23,7 +23,7 @@ Bug reports and feature requests -------------------------------- To report an issue and before starting work, please create an issue in the -`GitHub issue tracker `_. A +`GitHub issue tracker `_. A comprehensive explanation will help the development team to respond in a timely manner. Verbalising the issue before starting work allows the other contributors to chime in and avoids disagreements how to progress. @@ -76,10 +76,10 @@ be merged in, request a review from the `reviewers team request is reviewed, it can be merged in. To get started with git, please refer to the `short introduction -`_ as well as the `full git documentation +`_ as well as the `full git documentation `_. Tutorials as well as explanations of concepts and workflows with git can also be found on `Atlassian -`_. +`_. Checklist for pull requests ~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -138,8 +138,8 @@ familiar with development process in the Acts project. Well-written commit messages are key to understand your changes. There are many guidelines available on how to write proper commit logs (e.g. - `here `__, - `here `__, or + `here `__, + `here `__, or `here `__). As a short summary: @@ -162,9 +162,9 @@ familiar with development process in the Acts project. remote repository. These merge commits are considered to contribute little information to the development process of the feature and they clutter the history (read more e.g. - `here `__ + `here `__ or - `here `__). + `here `__). This problem can be avoided by using ``git pull --rebase`` which replays your local (un-pushed) commits on the tip of the remote branch. You can make this the default behaviour by running @@ -182,7 +182,7 @@ Coding style and guidelines ~~~~~~~~~~~~~~~~~~~~~~~~~~~ The Acts project uses -`clang-format `_ for +`clang-format `_ for formatting its source code. A ``.clang-format`` configuration file comes with the project and should be used to automatically format the code. Developers can use the provided Docker image to format their project or @@ -192,7 +192,7 @@ installing `the same clang version as used in the CI `_ is recommended. There are several instructions available on how to integrate clang-format with your favourite IDE (e.g. `Xcode `_, -`emacs `_). +`emacs `_). The Acts CI system will automatically check code formatting using the provided clang-format configuration and will notify incompatible formatting. @@ -269,7 +269,7 @@ member of the reviewers team before being merged but anyone is welcome to contribute by commenting on code changes. You can help reviewing proposed contributions by going to `the "pull requests" section of the Acts (core) GitHub -repository `_. +repository `_. As some of the guidelines recommended here require rights granted to the reviewers team, this guide specifically addresses the people in this diff --git a/Core/include/Acts/Utilities/BoundingBox.hpp b/Core/include/Acts/Utilities/BoundingBox.hpp index 6b2527917d58..f0824f9100c3 100644 --- a/Core/include/Acts/Utilities/BoundingBox.hpp +++ b/Core/include/Acts/Utilities/BoundingBox.hpp @@ -182,7 +182,7 @@ class AxisAlignedBoundingBox { * * @note This implementation may treat parallel rays on any of the slabs * as **outside** due to how @c NaNs are handled by Eigen. - * See http://eigen.tuxfamily.org/bz/show_bug.cgi?id=564 + * See https://eigen.tuxfamily.org/bz/show_bug.cgi?id=564 * @param ray The ray to intersect with * @return Whether the ray intersects this AABB */ diff --git a/Core/include/Acts/Utilities/BoundingBox.ipp b/Core/include/Acts/Utilities/BoundingBox.ipp index 59cacc1fb65f..eb7b33568944 100644 --- a/Core/include/Acts/Utilities/BoundingBox.ipp +++ b/Core/include/Acts/Utilities/BoundingBox.ipp @@ -128,7 +128,7 @@ bool Acts::AxisAlignedBoundingBox::intersect( // Calculate the component wise min/max between the t0s and t1s // this is non-compliant with IEEE-754-2008, NaN gets propagated through - // http://eigen.tuxfamily.org/bz/show_bug.cgi?id=564 + // https://eigen.tuxfamily.org/bz/show_bug.cgi?id=564 // this means that rays parallel to boundaries might not be considered // to intersect. vertex_array_type tsmaller = t0s.min(t1s); diff --git a/README.md b/README.md index 1921f2af428b..600c2c6eda6a 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ More information can be found in the [Acts documentation](https://acts.readthedo Acts is developed in C++ and is build using [CMake](https://cmake.org). Building the core library requires a C++17 compatible compiler, -[Boost](http://boost.org), and [Eigen](http://eigen.tuxfamily.org). The +[Boost](https://www.boost.org), and [Eigen](https://eigen.tuxfamily.org). The following commands will clone the repository, configure, and build the core library diff --git a/docs/acts_project.md b/docs/acts_project.md index a5bbd9055e3b..eecdfc0fe90d 100644 --- a/docs/acts_project.md +++ b/docs/acts_project.md @@ -1,6 +1,6 @@ # The ACTS project -The *A Common Tracking Software (ACTS)* project is an attempt to preserve and evolve the track reconstruction software of the LHC era towards HL-LHC and beyond. It has been initiated in 2016 starting from the [ATLAS Common Tracking Software](https://gitlab.cern.ch/atlas/athena/-/tree/master/Tracking). Given the changing computing landscape, dedicated care of parallel code execution is taken, and is written in `C++17`. +The *A Common Tracking Software (ACTS)* project is an attempt to preserve and evolve the track reconstruction software of the LHC era towards HL-LHC and beyond. It has been initiated in 2016 starting from the [ATLAS Common Tracking Software](https://gitlab.cern.ch/atlas/athena/-/tree/main/Tracking). Given the changing computing landscape, dedicated care of parallel code execution is taken, and is written in `C++17`. A [coherent write-up of the project](https://link.springer.com/article/10.1007/s41781-021-00078-8) has been published in 2022 in Springer's CSBS. diff --git a/docs/conf.py b/docs/conf.py index fa9d4424d8c4..158b96524bee 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -62,7 +62,7 @@ linkcheck_ignore = [ r"https://doi.org/.*", r"https://cernvm.cern.ch/.*", - r"http://eigen.tuxfamily.org.*", + r"https://eigen.tuxfamily.org.*", r"https://pythia.org.*", r"https://lcginfo.cern.ch/.*", r"https://.*\.?intel.com/.*", diff --git a/docs/contribution/profiling.md b/docs/contribution/profiling.md index 3d7d2eb7e6c5..2deb685dee61 100644 --- a/docs/contribution/profiling.md +++ b/docs/contribution/profiling.md @@ -43,7 +43,7 @@ An older version of pprof comes bundled with gperftools but using the newer Go v ### Install Go pprof (Optional) First, you must install Go. Instructions to do so are available [here](https://go.dev/doc/install). -Optionally, you can install [Graphviz](http://www.graphviz.org/download/) to produce visualisations of profiles. +Optionally, you can install [Graphviz](https://www.graphviz.org/download/) to produce visualisations of profiles. Then, run the following command to install pprof itself: diff --git a/docs/getting_started.md b/docs/getting_started.md index f280de9b754f..56cb3100f3f0 100644 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -4,7 +4,7 @@ ACTS is developed in C++ and is built using [CMake](https://cmake.org). Building the core library requires a C++17 compatible compiler, -[Boost](http://boost.org), and [Eigen](http://eigen.tuxfamily.org). The +[Boost](https://www.boost.org), and [Eigen](https://eigen.tuxfamily.org). The following commands will clone the repository, configure, and build the core library: @@ -25,8 +25,8 @@ The following dependencies are required to build the ACTS core library: - A C++17 compatible compiler (recent versions of either gcc and clang should work) - [CMake](https://cmake.org) >= 3.14 -- [Boost](http://boost.org) >= 1.71 with `filesystem`, `program_options`, and `unit_test_framework` -- [Eigen](http://eigen.tuxfamily.org) >= 3.3.7 +- [Boost](https://www.boost.org) >= 1.71 with `filesystem`, `program_options`, and `unit_test_framework` +- [Eigen](https://eigen.tuxfamily.org) >= 3.3.7 The following dependencies are optional and are needed to build additional components: @@ -34,7 +34,7 @@ components: - [CUDA](https://developer.nvidia.com/cuda-zone) for the CUDA plugin and the Exa.TrkX plugin and its examples - [DD4hep](http://dd4hep.cern.ch) >= 1.11 for the DD4hep plugin and some examples - [Doxygen](http://doxygen.org) >= 1.8.15 for the documentation -- [Geant4](http://geant4.org/) for some examples +- [Geant4](https://geant4.org/) for some examples - [HepMC](https://gitlab.cern.ch/hepmc/HepMC3) >= 3.2.1 for some examples - [Intel Threading Building Blocks](https://github.com/oneapi-src/oneTBB) >= 2020.1 for the examples - [ONNX Runtime](https://onnxruntime.ai/) >= 1.12.0 for the ONNX plugin, the Exa.TrkX plugin and some examples @@ -336,7 +336,7 @@ want and it will figure out the rest. In addition to the ACTS-specific options, many generic options are available that modify various aspects of the build. The following options are some of the most common ones. For more details, have a look at the annotated list of [useful -CMake variables](https://cmake.org/Wiki/CMake_Useful_Variables) or at the [CMake +CMake variables](https://gitlab.kitware.com/cmake/community/-/wikis/doc/cmake/Useful-Variables) or at the [CMake documentation](https://cmake.org/documentation/). | Option | Description | @@ -363,7 +363,7 @@ The following environment variables might be useful. ## The OpenDataDetector ACTS comes packaged with a detector modeled using DD4hep that can be used to test your algorithms. It comes equipped with a magnetic field file as well as an already built material map. -It is available via the git submodule feature by performing the following steps ([`git lfs`](https://git-lfs.github.com/) need to be installed on your machine): +It is available via the git submodule feature by performing the following steps ([`git lfs`](https://git-lfs.com/) need to be installed on your machine): ```console $ git submodule init diff --git a/docs/white_papers.toml b/docs/white_papers.toml index 5e2d2eab47a9..7dc0e7902595 100644 --- a/docs/white_papers.toml +++ b/docs/white_papers.toml @@ -17,7 +17,7 @@ authors = [ "Felix Russo",] title = "Gaussian Track Densities" description = "Given the impact parameters $(d_0, z_0, t_0)$ of a track in Perigee parametrization, one can model the probability of the particle passing exactly through a point $(d, z, t)$ using a multivariate Gaussian distribution. In this white paper, we derive the maximum and the width of such a distribution for $d = 0$. This is useful in vertex seed finding, where we only consider the track density along the beam axis. We use the analytical results from this white paper in the unit test of the Acts module AdaptiveGridTrackDensity." [[white_papers]] -repository = "https://github.com/beomki-yeo/path-correction-term-from-momentum-variation" +repository = "https://github.com/acts-project/path-correction-term-from-momentum-variation" slug = "correction-for-transport-jacobian" pdf_url = "https://github.com/acts-project/path-correction-term-from-momentum-variation/releases/download/v2.0/main.pdf" diff --git a/docs/white_papers/how_to_add.md b/docs/white_papers/how_to_add.md index 200e5188f2b7..ce0eb0429566 100644 --- a/docs/white_papers/how_to_add.md +++ b/docs/white_papers/how_to_add.md @@ -41,7 +41,7 @@ LaTeX installations. `latexmk` is configured to use LuaLatex by default. ## Integrate with Overleaf -[Overleaf](https://overleaf.com) is a convenient web-based LaTeX authoring tool. It has GitHub integration 🎉! +[Overleaf](https://www.overleaf.com) is a convenient web-based LaTeX authoring tool. It has GitHub integration 🎉! :::{image} figures/overleaf_import_github.png :align: center