From aa860d75e8f2f4ee60051f3ad29fff0153701a04 Mon Sep 17 00:00:00 2001 From: Andreas Stefl Date: Thu, 27 Jul 2023 17:47:34 +0200 Subject: [PATCH 01/21] refactor!: Rename `Single*TrackParameters` to `Generic*TrackParameters` (#2269) I am unhappy with the name `Single*TrackParameters` as it could refer to singly charged or single track. The track actually supports different kinds of charge hypothesis so I thought `Generic*TrackParameter` might fit better. I also merged `NeutralTrackParameters` into `TrackParameters` and removed the external template instantiation as it could be a performance hit because it disables inlining. Pulled these changes out of https://github.com/acts-project/acts/pull/2181 --- ...rs.hpp => GenericBoundTrackParameters.hpp} | 49 ++++++++++--------- ... => GenericCurvilinearTrackParameters.hpp} | 34 ++++++------- ...ers.hpp => GenericFreeTrackParameters.hpp} | 36 +++++++------- .../MultiComponentBoundTrackParameters.hpp | 8 +-- .../Acts/EventData/NeutralTrackParameters.hpp | 27 ---------- .../Acts/EventData/TrackParameters.hpp | 21 ++++---- Core/include/Acts/Propagator/AtlasStepper.hpp | 2 +- Core/include/Acts/Propagator/EigenStepper.hpp | 4 +- Core/include/Acts/Propagator/EigenStepper.ipp | 2 +- .../Acts/Propagator/MultiEigenStepperLoop.hpp | 2 +- .../Acts/Propagator/StraightLineStepper.hpp | 6 +-- Core/src/EventData/CMakeLists.txt | 1 - Core/src/EventData/NeutralTrackParameters.cpp | 27 ---------- Core/src/EventData/TrackParameters.cpp | 12 +++-- Core/src/Material/SurfaceMaterialMapper.cpp | 1 - Core/src/Material/VolumeMaterialMapper.cpp | 1 - .../Propagator/detail/CovarianceEngine.cpp | 4 +- .../Printers/TrackParametersPrinter.cpp | 2 +- .../Propagation/PropagationAlgorithm.hpp | 1 - .../Propagation/src/PropagationAlgorithm.cpp | 3 +- .../TrackFitting/src/RefittingAlgorithm.cpp | 2 +- .../src/TrackFittingAlgorithm.cpp | 2 +- .../Utilities/src/TracksToTrajectories.cpp | 2 +- .../IterativeVertexFinderAlgorithm.hpp | 2 +- .../AdaptiveMultiVertexFinderAlgorithm.cpp | 2 +- .../Io/Csv/src/CsvTrackParameterWriter.cpp | 2 +- Examples/Io/EDM4hep/src/EDM4hepUtil.cpp | 2 +- .../Performance/VertexPerformanceWriter.cpp | 2 +- .../Io/Root/src/RootAthenaNTupleReader.cpp | 2 +- .../Root/src/RootTrajectorySummaryWriter.cpp | 2 +- .../include/ActsFatras/Kernel/Simulation.hpp | 4 +- .../Acts/Plugins/EDM4hep/EDM4hepUtil.hpp | 10 ++-- Plugins/EDM4hep/src/EDM4hepUtil.cpp | 4 +- .../Fatras/FatrasSimulationTests.cpp | 1 - Tests/IntegrationTests/PropagationTests.hpp | 31 ++++++------ .../EventData/BoundTrackParametersTests.cpp | 7 ++- .../CurvilinearTrackParametersTests.cpp | 7 ++- .../EventData/FreeTrackParametersTests.cpp | 7 ++- ...ultiComponentBoundTrackParametersTests.cpp | 4 +- .../Material/VolumeMaterialMapperTests.cpp | 1 - .../Core/Propagator/AtlasStepperTests.cpp | 4 +- .../Core/Propagator/EigenStepperTests.cpp | 5 +- .../Core/Propagator/ExtrapolatorTests.cpp | 2 +- .../Core/Propagator/JacobianTests.cpp | 2 +- .../Propagator/KalmanExtrapolatorTests.cpp | 2 +- .../Core/Propagator/LoopProtectionTests.cpp | 2 +- .../Propagator/MaterialCollectionTests.cpp | 2 +- .../Core/Propagator/MultiStepperTests.cpp | 6 +-- .../Core/Propagator/NavigatorTests.cpp | 2 +- .../Core/Propagator/PropagatorTests.cpp | 4 +- .../Propagator/StraightLineStepperTests.cpp | 5 +- .../EstimateTrackParamsFromSeedTest.cpp | 2 +- .../SpacePointBuilderTests.cpp | 2 +- .../CombinatorialKalmanFilterTests.cpp | 4 +- .../UnitTests/Core/TrackFitting/GsfTests.cpp | 10 ++-- .../Core/TrackFitting/KalmanFitterTests.cpp | 2 +- .../AdaptiveGridTrackDensityTests.cpp | 2 +- .../AdaptiveMultiVertexFinderTests.cpp | 2 +- .../AdaptiveMultiVertexFitterTests.cpp | 2 +- .../FullBilloirVertexFitterTests.cpp | 2 +- .../GaussianGridTrackDensityTests.cpp | 2 +- .../GridDensityVertexFinderTests.cpp | 2 +- .../Vertexing/ImpactPointEstimatorTests.cpp | 2 +- .../Vertexing/IterativeVertexFinderTests.cpp | 2 +- .../KalmanVertexTrackUpdaterTests.cpp | 2 +- .../Vertexing/KalmanVertexUpdaterTests.cpp | 2 +- .../TrackDensityVertexFinderTests.cpp | 2 +- .../EDM4hep/ConvertTrackEDM4hepTest.cpp | 22 ++++----- docs/core/eventdata.md | 8 +-- 69 files changed, 192 insertions(+), 255 deletions(-) rename Core/include/Acts/EventData/{SingleBoundTrackParameters.hpp => GenericBoundTrackParameters.hpp} (86%) rename Core/include/Acts/EventData/{SingleCurvilinearTrackParameters.hpp => GenericCurvilinearTrackParameters.hpp} (84%) rename Core/include/Acts/EventData/{SingleFreeTrackParameters.hpp => GenericFreeTrackParameters.hpp} (85%) delete mode 100644 Core/include/Acts/EventData/NeutralTrackParameters.hpp delete mode 100644 Core/src/EventData/NeutralTrackParameters.cpp diff --git a/Core/include/Acts/EventData/SingleBoundTrackParameters.hpp b/Core/include/Acts/EventData/GenericBoundTrackParameters.hpp similarity index 86% rename from Core/include/Acts/EventData/SingleBoundTrackParameters.hpp rename to Core/include/Acts/EventData/GenericBoundTrackParameters.hpp index da9586579ea..3776d305add 100644 --- a/Core/include/Acts/EventData/SingleBoundTrackParameters.hpp +++ b/Core/include/Acts/EventData/GenericBoundTrackParameters.hpp @@ -33,7 +33,7 @@ namespace Acts { /// /// @note This class holds shared ownership on its reference surface. template -class SingleBoundTrackParameters { +class GenericBoundTrackParameters { public: using Scalar = ActsScalar; using ParametersVector = BoundVector; @@ -51,9 +51,9 @@ class SingleBoundTrackParameters { /// an input here to be consistent with the other constructors below that /// that also take the charge as an input. The charge sign is only used in /// debug builds to check for consistency with the q/p parameter. - SingleBoundTrackParameters(std::shared_ptr surface, - const ParametersVector& params, Scalar q, - std::optional cov = std::nullopt) + GenericBoundTrackParameters( + std::shared_ptr surface, const ParametersVector& params, + Scalar q, std::optional cov = std::nullopt) : m_params(params), m_cov(std::move(cov)), m_surface(std::move(surface)), @@ -74,9 +74,9 @@ class SingleBoundTrackParameters { /// ambiguities, i.e. the charge type is default-constructible. template , int> = 0> - SingleBoundTrackParameters(std::shared_ptr surface, - const ParametersVector& params, - std::optional cov = std::nullopt) + GenericBoundTrackParameters( + std::shared_ptr surface, const ParametersVector& params, + std::optional cov = std::nullopt) : m_params(params), m_cov(std::move(cov)), m_surface(std::move(surface)) { assert(m_surface); normalizePhiTheta(); @@ -95,7 +95,7 @@ class SingleBoundTrackParameters { /// /// @note The returned result indicates whether the free parameters could /// successfully be converted to on-surface parameters. - static Result> create( + static Result> create( std::shared_ptr surface, const GeometryContext& geoCtx, const Vector4& pos4, const Vector3& dir, Scalar p, Scalar q, std::optional cov = std::nullopt) { @@ -107,8 +107,8 @@ class SingleBoundTrackParameters { return bound.error(); } - return SingleBoundTrackParameters{std::move(surface), *bound, q, - std::move(cov)}; + return GenericBoundTrackParameters{std::move(surface), *bound, q, + std::move(cov)}; } /// Factory to construct from four-position, direction, and @@ -128,7 +128,7 @@ class SingleBoundTrackParameters { /// successfully be converted to on-surface parameters. template , int> = 0> - static Result> create( + static Result> create( std::shared_ptr surface, const GeometryContext& geoCtx, const Vector4& pos4, const Vector3& dir, Scalar qOverP, std::optional cov = std::nullopt) { @@ -138,18 +138,19 @@ class SingleBoundTrackParameters { return bound.error(); } - return SingleBoundTrackParameters{std::move(surface), *bound, - std::move(cov)}; + return GenericBoundTrackParameters{std::move(surface), *bound, + std::move(cov)}; } /// Parameters are not default constructible due to the charge type. - SingleBoundTrackParameters() = delete; - SingleBoundTrackParameters(const SingleBoundTrackParameters&) = default; - SingleBoundTrackParameters(SingleBoundTrackParameters&&) = default; - ~SingleBoundTrackParameters() = default; - SingleBoundTrackParameters& operator=(const SingleBoundTrackParameters&) = + GenericBoundTrackParameters() = delete; + GenericBoundTrackParameters(const GenericBoundTrackParameters&) = default; + GenericBoundTrackParameters(GenericBoundTrackParameters&&) = default; + ~GenericBoundTrackParameters() = default; + GenericBoundTrackParameters& operator=(const GenericBoundTrackParameters&) = + default; + GenericBoundTrackParameters& operator=(GenericBoundTrackParameters&&) = default; - SingleBoundTrackParameters& operator=(SingleBoundTrackParameters&&) = default; /// Parameters vector. ParametersVector& parameters() { return m_params; } @@ -263,20 +264,20 @@ class SingleBoundTrackParameters { /// of equality in different contexts. None of that can be handled by /// this operator. Users should think really hard if this is what they /// want and we might decided that we will remove this in the future. - friend bool operator==(const SingleBoundTrackParameters& lhs, - const SingleBoundTrackParameters& rhs) { + friend bool operator==(const GenericBoundTrackParameters& lhs, + const GenericBoundTrackParameters& rhs) { return (lhs.m_params == rhs.m_params) and (lhs.m_cov == rhs.m_cov) and (lhs.m_surface == rhs.m_surface) and (lhs.m_chargeInterpreter == rhs.m_chargeInterpreter); } /// Compare two bound track parameters for bitwise in-equality. - friend bool operator!=(const SingleBoundTrackParameters& lhs, - const SingleBoundTrackParameters& rhs) { + friend bool operator!=(const GenericBoundTrackParameters& lhs, + const GenericBoundTrackParameters& rhs) { return not(lhs == rhs); } /// Print information to the output stream. friend std::ostream& operator<<(std::ostream& os, - const SingleBoundTrackParameters& tp) { + const GenericBoundTrackParameters& tp) { detail::printBoundParameters( os, tp.referenceSurface(), tp.parameters(), tp.covariance().has_value() ? &tp.covariance().value() : nullptr); diff --git a/Core/include/Acts/EventData/SingleCurvilinearTrackParameters.hpp b/Core/include/Acts/EventData/GenericCurvilinearTrackParameters.hpp similarity index 84% rename from Core/include/Acts/EventData/SingleCurvilinearTrackParameters.hpp rename to Core/include/Acts/EventData/GenericCurvilinearTrackParameters.hpp index 1ca67bab05c..8d36eaafe78 100644 --- a/Core/include/Acts/EventData/SingleCurvilinearTrackParameters.hpp +++ b/Core/include/Acts/EventData/GenericCurvilinearTrackParameters.hpp @@ -8,7 +8,7 @@ #pragma once -#include "Acts/EventData/SingleBoundTrackParameters.hpp" +#include "Acts/EventData/GenericBoundTrackParameters.hpp" #include "Acts/Surfaces/PlaneSurface.hpp" namespace Acts { @@ -22,11 +22,11 @@ namespace Acts { /// parameters and their corresponding covariance matrix are stored in /// curvilinear parametrization. /// -/// @see SingleBoundTrackParameters +/// @see GenericBoundTrackParameters template -class SingleCurvilinearTrackParameters - : public SingleBoundTrackParameters { - using Base = SingleBoundTrackParameters; +class GenericCurvilinearTrackParameters + : public GenericBoundTrackParameters { + using Base = GenericBoundTrackParameters; public: using Scalar = ActsScalar; @@ -40,7 +40,7 @@ class SingleCurvilinearTrackParameters /// @param p Absolute momentum /// @param q Particle charge /// @param cov Curvilinear bound parameters covariance matrix - SingleCurvilinearTrackParameters( + GenericCurvilinearTrackParameters( const Vector4& pos4, const Vector3& dir, Scalar p, Scalar q, std::optional cov = std::nullopt) : Base(Surface::makeShared(pos4.segment<3>(ePos0), dir), @@ -61,7 +61,7 @@ class SingleCurvilinearTrackParameters /// ambiguities, i.e. the charge interpretation type is default-constructible. template , int> = 0> - SingleCurvilinearTrackParameters( + GenericCurvilinearTrackParameters( const Vector4& pos4, const Vector3& dir, Scalar qOverP, std::optional cov = std::nullopt) : Base(Surface::makeShared(pos4.segment<3>(ePos0), dir), @@ -77,7 +77,7 @@ class SingleCurvilinearTrackParameters /// @param p Absolute momentum /// @param q Particle charge /// @param cov Curvilinear bound parameters covariance matrix - SingleCurvilinearTrackParameters( + GenericCurvilinearTrackParameters( const Vector4& pos4, Scalar phi, Scalar theta, Scalar p, Scalar q, std::optional cov = std::nullopt) : Base(Surface::makeShared( @@ -101,7 +101,7 @@ class SingleCurvilinearTrackParameters /// ambiguities, i.e. the charge interpretation type is default-constructible. template , int> = 0> - SingleCurvilinearTrackParameters( + GenericCurvilinearTrackParameters( const Vector4& pos4, Scalar phi, Scalar theta, Scalar qOverP, std::optional cov = std::nullopt) : Base(Surface::makeShared( @@ -112,16 +112,16 @@ class SingleCurvilinearTrackParameters std::move(cov)) {} /// Parameters are not default constructible due to the charge type. - SingleCurvilinearTrackParameters() = delete; - SingleCurvilinearTrackParameters(const SingleCurvilinearTrackParameters&) = + GenericCurvilinearTrackParameters() = delete; + GenericCurvilinearTrackParameters(const GenericCurvilinearTrackParameters&) = default; - SingleCurvilinearTrackParameters(SingleCurvilinearTrackParameters&&) = + GenericCurvilinearTrackParameters(GenericCurvilinearTrackParameters&&) = default; - ~SingleCurvilinearTrackParameters() = default; - SingleCurvilinearTrackParameters& operator=( - const SingleCurvilinearTrackParameters&) = default; - SingleCurvilinearTrackParameters& operator=( - SingleCurvilinearTrackParameters&&) = default; + ~GenericCurvilinearTrackParameters() = default; + GenericCurvilinearTrackParameters& operator=( + const GenericCurvilinearTrackParameters&) = default; + GenericCurvilinearTrackParameters& operator=( + GenericCurvilinearTrackParameters&&) = default; }; } // namespace Acts diff --git a/Core/include/Acts/EventData/SingleFreeTrackParameters.hpp b/Core/include/Acts/EventData/GenericFreeTrackParameters.hpp similarity index 85% rename from Core/include/Acts/EventData/SingleFreeTrackParameters.hpp rename to Core/include/Acts/EventData/GenericFreeTrackParameters.hpp index b0e8501d6f7..84ad5b8abd4 100644 --- a/Core/include/Acts/EventData/SingleFreeTrackParameters.hpp +++ b/Core/include/Acts/EventData/GenericFreeTrackParameters.hpp @@ -28,7 +28,7 @@ namespace Acts { /// Parameters and covariance matrix are stored using the free parametrization /// defined in `enum FreeIndices`. template -class SingleFreeTrackParameters { +class GenericFreeTrackParameters { public: using Scalar = ActsScalar; using ParametersVector = FreeVector; @@ -45,8 +45,8 @@ class SingleFreeTrackParameters { /// an input here to be consistent with the other constructors below that /// that also take the charge as an input. The charge sign is only used in /// debug builds to check for consistency with the q/p parameter. - SingleFreeTrackParameters(const ParametersVector& params, Scalar q, - std::optional cov = std::nullopt) + GenericFreeTrackParameters(const ParametersVector& params, Scalar q, + std::optional cov = std::nullopt) : m_params(params), m_cov(std::move(cov)), m_chargeInterpreter(std::abs(q)) { @@ -63,8 +63,8 @@ class SingleFreeTrackParameters { /// ambiguities, i.e. the charge interpretation type is default-constructible. template , int> = 0> - SingleFreeTrackParameters(const ParametersVector& params, - std::optional cov = std::nullopt) + GenericFreeTrackParameters(const ParametersVector& params, + std::optional cov = std::nullopt) : m_params(params), m_cov(std::move(cov)) {} /// Construct from four-position, angles, absolute momentum, and charge. @@ -75,9 +75,9 @@ class SingleFreeTrackParameters { /// @param p Absolute momentum /// @param q Particle charge /// @param cov Free parameters covariance matrix - SingleFreeTrackParameters(const Vector4& pos4, Scalar phi, Scalar theta, - Scalar p, Scalar q, - std::optional cov = std::nullopt) + GenericFreeTrackParameters(const Vector4& pos4, Scalar phi, Scalar theta, + Scalar p, Scalar q, + std::optional cov = std::nullopt) : m_params(FreeVector::Zero()), m_cov(std::move(cov)), m_chargeInterpreter(std::abs(q)) { @@ -106,9 +106,9 @@ class SingleFreeTrackParameters { /// ambiguities, i.e. the charge interpretation type is default-constructible. template , int> = 0> - SingleFreeTrackParameters(const Vector4& pos4, Scalar phi, Scalar theta, - Scalar qOverP, - std::optional cov = std::nullopt) + GenericFreeTrackParameters(const Vector4& pos4, Scalar phi, Scalar theta, + Scalar qOverP, + std::optional cov = std::nullopt) : m_params(FreeVector::Zero()), m_cov(std::move(cov)) { auto dir = makeDirectionUnitFromPhiTheta(phi, theta); m_params[eFreePos0] = pos4[ePos0]; @@ -122,13 +122,13 @@ class SingleFreeTrackParameters { } /// Parameters are not default constructible due to the charge type. - SingleFreeTrackParameters() = delete; - SingleFreeTrackParameters(const SingleFreeTrackParameters&) = default; - SingleFreeTrackParameters(SingleFreeTrackParameters&&) = default; - ~SingleFreeTrackParameters() = default; - SingleFreeTrackParameters& operator=(const SingleFreeTrackParameters&) = + GenericFreeTrackParameters() = delete; + GenericFreeTrackParameters(const GenericFreeTrackParameters&) = default; + GenericFreeTrackParameters(GenericFreeTrackParameters&&) = default; + ~GenericFreeTrackParameters() = default; + GenericFreeTrackParameters& operator=(const GenericFreeTrackParameters&) = default; - SingleFreeTrackParameters& operator=(SingleFreeTrackParameters&&) = default; + GenericFreeTrackParameters& operator=(GenericFreeTrackParameters&&) = default; /// Parameters vector. const ParametersVector& parameters() const { return m_params; } @@ -194,7 +194,7 @@ class SingleFreeTrackParameters { /// Print information to the output stream. friend std::ostream& operator<<(std::ostream& os, - const SingleFreeTrackParameters& tp) { + const GenericFreeTrackParameters& tp) { detail::printFreeParameters( os, tp.parameters(), tp.covariance().has_value() ? &tp.covariance().value() : nullptr); diff --git a/Core/include/Acts/EventData/MultiComponentBoundTrackParameters.hpp b/Core/include/Acts/EventData/MultiComponentBoundTrackParameters.hpp index f1b292911c2..9659bea77a6 100644 --- a/Core/include/Acts/EventData/MultiComponentBoundTrackParameters.hpp +++ b/Core/include/Acts/EventData/MultiComponentBoundTrackParameters.hpp @@ -8,7 +8,7 @@ #pragma once -#include "Acts/EventData/SingleBoundTrackParameters.hpp" +#include "Acts/EventData/GenericBoundTrackParameters.hpp" #include "Acts/Surfaces/Surface.hpp" #include @@ -21,7 +21,7 @@ namespace Acts { /// This class is only a light wrapper around a surface and a vector of /// parameters. Its main purpose is to provide many constructors for the /// underlying vector. Most accessors are generated from the -/// SingleBoundTrackParameters equivalent and thus may be expensive +/// GenericBoundTrackParameters equivalent and thus may be expensive /// @tparam charge_t Helper type to interpret the particle charge/momentum /// @note This class holds shared ownership on its reference surface. /// @note The accessors for parameters, covariance, position, etc. @@ -31,7 +31,7 @@ namespace Acts { /// TODO Add constructor from range and projector maybe? template class MultiComponentBoundTrackParameters { - using SingleParameters = SingleBoundTrackParameters; + using SingleParameters = GenericBoundTrackParameters; std::vector>> m_components; @@ -157,7 +157,7 @@ class MultiComponentBoundTrackParameters { /// Reference surface onto which the parameters are bound. const Surface& referenceSurface() const { return *m_surface; } - /// Get the weight and a SingleBoundTrackParameters object for one component + /// Get the weight and a GenericBoundTrackParameters object for one component std::pair operator[](std::size_t i) const { return std::make_pair( std::get(m_components[i]), diff --git a/Core/include/Acts/EventData/NeutralTrackParameters.hpp b/Core/include/Acts/EventData/NeutralTrackParameters.hpp deleted file mode 100644 index 06f8cbc19ea..00000000000 --- a/Core/include/Acts/EventData/NeutralTrackParameters.hpp +++ /dev/null @@ -1,27 +0,0 @@ -// This file is part of the Acts project. -// -// Copyright (C) 2016-2020 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/EventData/Charge.hpp" -#include "Acts/EventData/SingleBoundTrackParameters.hpp" -#include "Acts/EventData/SingleCurvilinearTrackParameters.hpp" -#include "Acts/EventData/SingleFreeTrackParameters.hpp" - -namespace Acts { - -extern template class SingleBoundTrackParameters; -extern template class SingleCurvilinearTrackParameters; -extern template class SingleFreeTrackParameters; - -using NeutralBoundTrackParameters = SingleBoundTrackParameters; -using NeutralCurvilinearTrackParameters = - SingleCurvilinearTrackParameters; -using NeutralFreeTrackParameters = SingleFreeTrackParameters; - -} // namespace Acts diff --git a/Core/include/Acts/EventData/TrackParameters.hpp b/Core/include/Acts/EventData/TrackParameters.hpp index ad9e0924281..ad8f1b90ee1 100644 --- a/Core/include/Acts/EventData/TrackParameters.hpp +++ b/Core/include/Acts/EventData/TrackParameters.hpp @@ -9,19 +9,20 @@ #pragma once #include "Acts/EventData/Charge.hpp" -#include "Acts/EventData/SingleBoundTrackParameters.hpp" -#include "Acts/EventData/SingleCurvilinearTrackParameters.hpp" -#include "Acts/EventData/SingleFreeTrackParameters.hpp" +#include "Acts/EventData/GenericBoundTrackParameters.hpp" +#include "Acts/EventData/GenericCurvilinearTrackParameters.hpp" +#include "Acts/EventData/GenericFreeTrackParameters.hpp" namespace Acts { -extern template class SingleBoundTrackParameters; -extern template class SingleCurvilinearTrackParameters; -extern template class SingleFreeTrackParameters; - -using BoundTrackParameters = SingleBoundTrackParameters; +using BoundTrackParameters = GenericBoundTrackParameters; using CurvilinearTrackParameters = - SingleCurvilinearTrackParameters; -using FreeTrackParameters = SingleFreeTrackParameters; + GenericCurvilinearTrackParameters; +using FreeTrackParameters = GenericFreeTrackParameters; + +using NeutralBoundTrackParameters = GenericBoundTrackParameters; +using NeutralCurvilinearTrackParameters = + GenericCurvilinearTrackParameters; +using NeutralFreeTrackParameters = GenericFreeTrackParameters; } // namespace Acts diff --git a/Core/include/Acts/Propagator/AtlasStepper.hpp b/Core/include/Acts/Propagator/AtlasStepper.hpp index b74535b56d5..7574fd1ec93 100644 --- a/Core/include/Acts/Propagator/AtlasStepper.hpp +++ b/Core/include/Acts/Propagator/AtlasStepper.hpp @@ -297,7 +297,7 @@ class AtlasStepper { template State makeState(std::reference_wrapper gctx, std::reference_wrapper mctx, - const SingleBoundTrackParameters& par, + const GenericBoundTrackParameters& par, Direction navDir = Direction::Forward, double ssize = std::numeric_limits::max(), double stolerance = s_onSurfaceTolerance) const { diff --git a/Core/include/Acts/Propagator/EigenStepper.hpp b/Core/include/Acts/Propagator/EigenStepper.hpp index c7eae5c5342..7ca2e54c60c 100644 --- a/Core/include/Acts/Propagator/EigenStepper.hpp +++ b/Core/include/Acts/Propagator/EigenStepper.hpp @@ -76,7 +76,7 @@ class EigenStepper { template explicit State(const GeometryContext& gctx, MagneticFieldProvider::Cache fieldCacheIn, - const SingleBoundTrackParameters& par, + const GenericBoundTrackParameters& par, Direction ndir = Direction::Forward, double ssize = std::numeric_limits::max()) : absCharge(std::abs(par.charge())), @@ -167,7 +167,7 @@ class EigenStepper { template State makeState(std::reference_wrapper gctx, std::reference_wrapper mctx, - const SingleBoundTrackParameters& par, + const GenericBoundTrackParameters& par, Direction navDir = Direction::Forward, double ssize = std::numeric_limits::max()) const; diff --git a/Core/include/Acts/Propagator/EigenStepper.ipp b/Core/include/Acts/Propagator/EigenStepper.ipp index 53a8338a0f9..b5a60342d59 100644 --- a/Core/include/Acts/Propagator/EigenStepper.ipp +++ b/Core/include/Acts/Propagator/EigenStepper.ipp @@ -19,7 +19,7 @@ template auto Acts::EigenStepper::makeState( std::reference_wrapper gctx, std::reference_wrapper mctx, - const SingleBoundTrackParameters& par, Direction navDir, + const GenericBoundTrackParameters& par, Direction navDir, double ssize) const -> State { return State{gctx, m_bField->makeCache(mctx), par, navDir, ssize}; } diff --git a/Core/include/Acts/Propagator/MultiEigenStepperLoop.hpp b/Core/include/Acts/Propagator/MultiEigenStepperLoop.hpp index 55131f4d93f..41dea12c979 100644 --- a/Core/include/Acts/Propagator/MultiEigenStepperLoop.hpp +++ b/Core/include/Acts/Propagator/MultiEigenStepperLoop.hpp @@ -612,7 +612,7 @@ class MultiEigenStepperLoop /// the component number is again modified template Result addComponent( - State& state, const SingleBoundTrackParameters& pars, + State& state, const GenericBoundTrackParameters& pars, double weight) const { state.components.push_back( {SingleState(state.geoContext, diff --git a/Core/include/Acts/Propagator/StraightLineStepper.hpp b/Core/include/Acts/Propagator/StraightLineStepper.hpp index e779dc41b1c..ad475d40c5b 100644 --- a/Core/include/Acts/Propagator/StraightLineStepper.hpp +++ b/Core/include/Acts/Propagator/StraightLineStepper.hpp @@ -37,7 +37,7 @@ namespace Acts { template -class SingleBoundTrackParameters; +class GenericBoundTrackParameters; /// @brief straight line stepper based on Surface intersection /// @@ -73,7 +73,7 @@ class StraightLineStepper { template explicit State(const GeometryContext& gctx, const MagneticFieldContext& mctx, - const SingleBoundTrackParameters& par, + const GenericBoundTrackParameters& par, Direction ndir = Direction::Forward, double ssize = std::numeric_limits::max(), double stolerance = s_onSurfaceTolerance) @@ -147,7 +147,7 @@ class StraightLineStepper { template State makeState(std::reference_wrapper gctx, std::reference_wrapper mctx, - const SingleBoundTrackParameters& par, + const GenericBoundTrackParameters& par, Direction navDir = Direction::Forward, double ssize = std::numeric_limits::max(), double stolerance = s_onSurfaceTolerance) const { diff --git a/Core/src/EventData/CMakeLists.txt b/Core/src/EventData/CMakeLists.txt index ad67f3b4936..8064f509ab3 100644 --- a/Core/src/EventData/CMakeLists.txt +++ b/Core/src/EventData/CMakeLists.txt @@ -1,7 +1,6 @@ target_sources( ActsCore PRIVATE - NeutralTrackParameters.cpp PrintParameters.cpp TrackParameters.cpp TransformationBoundToFree.cpp diff --git a/Core/src/EventData/NeutralTrackParameters.cpp b/Core/src/EventData/NeutralTrackParameters.cpp deleted file mode 100644 index 908895ece27..00000000000 --- a/Core/src/EventData/NeutralTrackParameters.cpp +++ /dev/null @@ -1,27 +0,0 @@ -// This file is part of the Acts project. -// -// Copyright (C) 2016-2020 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/EventData/NeutralTrackParameters.hpp" - -#include "Acts/EventData/TrackParametersConcept.hpp" - -namespace Acts { - -// explicitly instantiate templates -template class SingleBoundTrackParameters; -template class SingleCurvilinearTrackParameters; -template class SingleFreeTrackParameters; - -// ensure concrete classes satisfy the concepts -static_assert( - Concepts::BoundTrackParametersConcept); -static_assert( - Concepts::BoundTrackParametersConcept); -static_assert(Concepts::FreeTrackParametersConcept); - -} // namespace Acts diff --git a/Core/src/EventData/TrackParameters.cpp b/Core/src/EventData/TrackParameters.cpp index 05ef2fafed0..b8e47ba57dc 100644 --- a/Core/src/EventData/TrackParameters.cpp +++ b/Core/src/EventData/TrackParameters.cpp @@ -12,15 +12,17 @@ namespace Acts { -// explicitly instantiate templates -template class SingleBoundTrackParameters; -template class SingleCurvilinearTrackParameters; -template class SingleFreeTrackParameters; - // ensure concrete classes satisfy the concepts + static_assert(Concepts::BoundTrackParametersConcept); static_assert( Concepts::BoundTrackParametersConcept); static_assert(Concepts::FreeTrackParametersConcept); +static_assert( + Concepts::BoundTrackParametersConcept); +static_assert( + Concepts::BoundTrackParametersConcept); +static_assert(Concepts::FreeTrackParametersConcept); + } // namespace Acts diff --git a/Core/src/Material/SurfaceMaterialMapper.cpp b/Core/src/Material/SurfaceMaterialMapper.cpp index 50350f8d81e..8dbf0544b8a 100644 --- a/Core/src/Material/SurfaceMaterialMapper.cpp +++ b/Core/src/Material/SurfaceMaterialMapper.cpp @@ -11,7 +11,6 @@ #include "Acts/Definitions/Algebra.hpp" #include "Acts/Definitions/Direction.hpp" #include "Acts/Definitions/Tolerance.hpp" -#include "Acts/EventData/NeutralTrackParameters.hpp" #include "Acts/EventData/TrackParameters.hpp" #include "Acts/Geometry/ApproachDescriptor.hpp" #include "Acts/Geometry/BoundarySurfaceT.hpp" diff --git a/Core/src/Material/VolumeMaterialMapper.cpp b/Core/src/Material/VolumeMaterialMapper.cpp index 586ea430e6e..9ddee6bc274 100644 --- a/Core/src/Material/VolumeMaterialMapper.cpp +++ b/Core/src/Material/VolumeMaterialMapper.cpp @@ -10,7 +10,6 @@ #include "Acts/Definitions/Direction.hpp" #include "Acts/Definitions/Tolerance.hpp" -#include "Acts/EventData/NeutralTrackParameters.hpp" #include "Acts/EventData/TrackParameters.hpp" #include "Acts/Geometry/ApproachDescriptor.hpp" #include "Acts/Geometry/BoundarySurfaceT.hpp" diff --git a/Core/src/Propagator/detail/CovarianceEngine.cpp b/Core/src/Propagator/detail/CovarianceEngine.cpp index 13e926e1457..e04f8ee1194 100644 --- a/Core/src/Propagator/detail/CovarianceEngine.cpp +++ b/Core/src/Propagator/detail/CovarianceEngine.cpp @@ -10,8 +10,8 @@ #include "Acts/Definitions/Common.hpp" #include "Acts/Definitions/Tolerance.hpp" -#include "Acts/EventData/SingleBoundTrackParameters.hpp" -#include "Acts/EventData/SingleCurvilinearTrackParameters.hpp" +#include "Acts/EventData/GenericBoundTrackParameters.hpp" +#include "Acts/EventData/GenericCurvilinearTrackParameters.hpp" #include "Acts/EventData/detail/CorrectedTransformationFreeToBound.hpp" #include "Acts/EventData/detail/TransformationBoundToFree.hpp" #include "Acts/EventData/detail/TransformationFreeToBound.hpp" diff --git a/Examples/Algorithms/Printers/ActsExamples/Printers/TrackParametersPrinter.cpp b/Examples/Algorithms/Printers/ActsExamples/Printers/TrackParametersPrinter.cpp index f537ec4c540..1b90ad08ce0 100644 --- a/Examples/Algorithms/Printers/ActsExamples/Printers/TrackParametersPrinter.cpp +++ b/Examples/Algorithms/Printers/ActsExamples/Printers/TrackParametersPrinter.cpp @@ -8,7 +8,7 @@ #include "TrackParametersPrinter.hpp" -#include "Acts/EventData/SingleBoundTrackParameters.hpp" +#include "Acts/EventData/GenericBoundTrackParameters.hpp" #include "Acts/Utilities/Logger.hpp" #include "ActsExamples/EventData/Track.hpp" #include "ActsExamples/Framework/AlgorithmContext.hpp" diff --git a/Examples/Algorithms/Propagation/include/ActsExamples/Propagation/PropagationAlgorithm.hpp b/Examples/Algorithms/Propagation/include/ActsExamples/Propagation/PropagationAlgorithm.hpp index afb3e678065..acc4874c2b3 100644 --- a/Examples/Algorithms/Propagation/include/ActsExamples/Propagation/PropagationAlgorithm.hpp +++ b/Examples/Algorithms/Propagation/include/ActsExamples/Propagation/PropagationAlgorithm.hpp @@ -11,7 +11,6 @@ #include "Acts/Definitions/Algebra.hpp" #include "Acts/Definitions/TrackParametrization.hpp" #include "Acts/Definitions/Units.hpp" -#include "Acts/EventData/NeutralTrackParameters.hpp" #include "Acts/EventData/TrackParameters.hpp" #include "Acts/Material/MaterialInteraction.hpp" #include "Acts/Propagator/AbortList.hpp" diff --git a/Examples/Algorithms/Propagation/src/PropagationAlgorithm.cpp b/Examples/Algorithms/Propagation/src/PropagationAlgorithm.cpp index 80705834c6a..95981ccacd1 100644 --- a/Examples/Algorithms/Propagation/src/PropagationAlgorithm.cpp +++ b/Examples/Algorithms/Propagation/src/PropagationAlgorithm.cpp @@ -8,8 +8,7 @@ #include "ActsExamples/Propagation/PropagationAlgorithm.hpp" -#include "Acts/EventData/NeutralTrackParameters.hpp" -#include "Acts/EventData/SingleBoundTrackParameters.hpp" +#include "Acts/EventData/GenericBoundTrackParameters.hpp" #include "Acts/EventData/TrackParameters.hpp" #include "Acts/Surfaces/PerigeeSurface.hpp" #include "Acts/Surfaces/Surface.hpp" diff --git a/Examples/Algorithms/TrackFitting/src/RefittingAlgorithm.cpp b/Examples/Algorithms/TrackFitting/src/RefittingAlgorithm.cpp index a290e5dacbf..abd75d54608 100644 --- a/Examples/Algorithms/TrackFitting/src/RefittingAlgorithm.cpp +++ b/Examples/Algorithms/TrackFitting/src/RefittingAlgorithm.cpp @@ -9,8 +9,8 @@ #include "ActsExamples/TrackFitting/RefittingAlgorithm.hpp" #include "Acts/Definitions/Algebra.hpp" +#include "Acts/EventData/GenericBoundTrackParameters.hpp" #include "Acts/EventData/MultiTrajectory.hpp" -#include "Acts/EventData/SingleBoundTrackParameters.hpp" #include "Acts/EventData/SourceLink.hpp" #include "Acts/EventData/TrackContainer.hpp" #include "Acts/EventData/TrackParameters.hpp" diff --git a/Examples/Algorithms/TrackFitting/src/TrackFittingAlgorithm.cpp b/Examples/Algorithms/TrackFitting/src/TrackFittingAlgorithm.cpp index a0d116c0459..63935488395 100644 --- a/Examples/Algorithms/TrackFitting/src/TrackFittingAlgorithm.cpp +++ b/Examples/Algorithms/TrackFitting/src/TrackFittingAlgorithm.cpp @@ -9,7 +9,7 @@ #include "ActsExamples/TrackFitting/TrackFittingAlgorithm.hpp" #include "Acts/Definitions/Algebra.hpp" -#include "Acts/EventData/SingleBoundTrackParameters.hpp" +#include "Acts/EventData/GenericBoundTrackParameters.hpp" #include "Acts/EventData/SourceLink.hpp" #include "Acts/EventData/TrackProxy.hpp" #include "Acts/EventData/VectorMultiTrajectory.hpp" diff --git a/Examples/Algorithms/Utilities/src/TracksToTrajectories.cpp b/Examples/Algorithms/Utilities/src/TracksToTrajectories.cpp index 2737b8ff7ef..a490c378968 100644 --- a/Examples/Algorithms/Utilities/src/TracksToTrajectories.cpp +++ b/Examples/Algorithms/Utilities/src/TracksToTrajectories.cpp @@ -8,8 +8,8 @@ #include "ActsExamples/Utilities/TracksToTrajectories.hpp" +#include "Acts/EventData/GenericBoundTrackParameters.hpp" #include "Acts/EventData/MultiTrajectory.hpp" -#include "Acts/EventData/SingleBoundTrackParameters.hpp" #include "Acts/EventData/TrackContainer.hpp" #include "Acts/EventData/TrackProxy.hpp" #include "Acts/Surfaces/Surface.hpp" diff --git a/Examples/Algorithms/Vertexing/include/ActsExamples/Vertexing/IterativeVertexFinderAlgorithm.hpp b/Examples/Algorithms/Vertexing/include/ActsExamples/Vertexing/IterativeVertexFinderAlgorithm.hpp index c5474627646..e9def2e01d4 100644 --- a/Examples/Algorithms/Vertexing/include/ActsExamples/Vertexing/IterativeVertexFinderAlgorithm.hpp +++ b/Examples/Algorithms/Vertexing/include/ActsExamples/Vertexing/IterativeVertexFinderAlgorithm.hpp @@ -11,7 +11,7 @@ #include "Acts/Definitions/Algebra.hpp" #include "Acts/Definitions/Direction.hpp" #include "Acts/EventData/Charge.hpp" -#include "Acts/EventData/SingleBoundTrackParameters.hpp" +#include "Acts/EventData/GenericBoundTrackParameters.hpp" #include "Acts/EventData/TrackParameters.hpp" #include "Acts/Geometry/GeometryIdentifier.hpp" #include "Acts/MagneticField/MagneticFieldProvider.hpp" diff --git a/Examples/Algorithms/Vertexing/src/AdaptiveMultiVertexFinderAlgorithm.cpp b/Examples/Algorithms/Vertexing/src/AdaptiveMultiVertexFinderAlgorithm.cpp index df8bbd1c691..5003b26b08d 100644 --- a/Examples/Algorithms/Vertexing/src/AdaptiveMultiVertexFinderAlgorithm.cpp +++ b/Examples/Algorithms/Vertexing/src/AdaptiveMultiVertexFinderAlgorithm.cpp @@ -10,7 +10,7 @@ #include "Acts/Definitions/Algebra.hpp" #include "Acts/Definitions/Units.hpp" -#include "Acts/EventData/SingleBoundTrackParameters.hpp" +#include "Acts/EventData/GenericBoundTrackParameters.hpp" #include "Acts/Propagator/EigenStepper.hpp" #include "Acts/Utilities/AnnealingUtility.hpp" #include "Acts/Utilities/Logger.hpp" diff --git a/Examples/Io/Csv/src/CsvTrackParameterWriter.cpp b/Examples/Io/Csv/src/CsvTrackParameterWriter.cpp index aec1f34cf57..a42988dacb3 100644 --- a/Examples/Io/Csv/src/CsvTrackParameterWriter.cpp +++ b/Examples/Io/Csv/src/CsvTrackParameterWriter.cpp @@ -9,7 +9,7 @@ #include "ActsExamples/Io/Csv/CsvTrackParameterWriter.hpp" #include "Acts/Definitions/TrackParametrization.hpp" -#include "Acts/EventData/SingleBoundTrackParameters.hpp" +#include "Acts/EventData/GenericBoundTrackParameters.hpp" #include "ActsExamples/EventData/Trajectories.hpp" #include "ActsExamples/Framework/AlgorithmContext.hpp" #include "ActsExamples/Utilities/Paths.hpp" diff --git a/Examples/Io/EDM4hep/src/EDM4hepUtil.cpp b/Examples/Io/EDM4hep/src/EDM4hepUtil.cpp index d84760989f2..79508c45ce4 100644 --- a/Examples/Io/EDM4hep/src/EDM4hepUtil.cpp +++ b/Examples/Io/EDM4hep/src/EDM4hepUtil.cpp @@ -278,7 +278,7 @@ void EDM4hepUtil::writeTrajectory( edm4hep::TrackState trackState; // This makes the hard assumption that |q| = 1 - Acts::SingleBoundTrackParameters parObj{ + Acts::GenericBoundTrackParameters parObj{ state.referenceSurface().getSharedPtr(), state.parameters(), state.covariance()}; diff --git a/Examples/Io/Performance/ActsExamples/Io/Performance/VertexPerformanceWriter.cpp b/Examples/Io/Performance/ActsExamples/Io/Performance/VertexPerformanceWriter.cpp index e12092b78f1..1786cf7b7b9 100644 --- a/Examples/Io/Performance/ActsExamples/Io/Performance/VertexPerformanceWriter.cpp +++ b/Examples/Io/Performance/ActsExamples/Io/Performance/VertexPerformanceWriter.cpp @@ -10,9 +10,9 @@ #include "Acts/Definitions/Algebra.hpp" #include "Acts/Definitions/TrackParametrization.hpp" +#include "Acts/EventData/GenericBoundTrackParameters.hpp" #include "Acts/EventData/MultiTrajectory.hpp" #include "Acts/EventData/MultiTrajectoryHelpers.hpp" -#include "Acts/EventData/SingleBoundTrackParameters.hpp" #include "Acts/EventData/TrackParameters.hpp" #include "Acts/Utilities/Logger.hpp" #include "Acts/Utilities/MultiIndex.hpp" diff --git a/Examples/Io/Root/src/RootAthenaNTupleReader.cpp b/Examples/Io/Root/src/RootAthenaNTupleReader.cpp index 4e62f80e28a..fe07f1e9794 100644 --- a/Examples/Io/Root/src/RootAthenaNTupleReader.cpp +++ b/Examples/Io/Root/src/RootAthenaNTupleReader.cpp @@ -9,7 +9,7 @@ #include "ActsExamples/Io/Root/RootAthenaNTupleReader.hpp" #include "Acts/Definitions/TrackParametrization.hpp" -#include "Acts/EventData/SingleBoundTrackParameters.hpp" +#include "Acts/EventData/GenericBoundTrackParameters.hpp" #include "Acts/EventData/TrackParameters.hpp" #include "Acts/Surfaces/PerigeeSurface.hpp" #include "Acts/Surfaces/Surface.hpp" diff --git a/Examples/Io/Root/src/RootTrajectorySummaryWriter.cpp b/Examples/Io/Root/src/RootTrajectorySummaryWriter.cpp index 823cc4d0184..cfe33e56d05 100644 --- a/Examples/Io/Root/src/RootTrajectorySummaryWriter.cpp +++ b/Examples/Io/Root/src/RootTrajectorySummaryWriter.cpp @@ -9,8 +9,8 @@ #include "ActsExamples/Io/Root/RootTrajectorySummaryWriter.hpp" #include "Acts/Definitions/TrackParametrization.hpp" +#include "Acts/EventData/GenericBoundTrackParameters.hpp" #include "Acts/EventData/MultiTrajectoryHelpers.hpp" -#include "Acts/EventData/SingleBoundTrackParameters.hpp" #include "Acts/EventData/VectorMultiTrajectory.hpp" #include "Acts/Surfaces/Surface.hpp" #include "Acts/Utilities/Intersection.hpp" diff --git a/Fatras/include/ActsFatras/Kernel/Simulation.hpp b/Fatras/include/ActsFatras/Kernel/Simulation.hpp index 9c02eb0236c..a59ebe6cdad 100644 --- a/Fatras/include/ActsFatras/Kernel/Simulation.hpp +++ b/Fatras/include/ActsFatras/Kernel/Simulation.hpp @@ -9,7 +9,7 @@ #pragma once #include "Acts/EventData/Charge.hpp" -#include "Acts/EventData/SingleCurvilinearTrackParameters.hpp" +#include "Acts/EventData/GenericCurvilinearTrackParameters.hpp" #include "Acts/Geometry/GeometryContext.hpp" #include "Acts/MagneticField/MagneticFieldContext.hpp" #include "Acts/Propagator/AbortList.hpp" @@ -92,7 +92,7 @@ struct SingleParticleSimulation { actor.selectHitSurface = selectHitSurface; actor.initialParticle = particle; // use AnyCharge to be able to handle neutral and charged parameters - Acts::SingleCurvilinearTrackParameters start( + Acts::GenericCurvilinearTrackParameters start( particle.fourPosition(), particle.direction(), particle.absoluteMomentum(), particle.charge()); auto result = propagator.propagate(start, options); diff --git a/Plugins/EDM4hep/include/Acts/Plugins/EDM4hep/EDM4hepUtil.hpp b/Plugins/EDM4hep/include/Acts/Plugins/EDM4hep/EDM4hepUtil.hpp index 0c375134d59..5c5192c6083 100644 --- a/Plugins/EDM4hep/include/Acts/Plugins/EDM4hep/EDM4hepUtil.hpp +++ b/Plugins/EDM4hep/include/Acts/Plugins/EDM4hep/EDM4hepUtil.hpp @@ -10,8 +10,8 @@ #include "Acts/Definitions/Algebra.hpp" #include "Acts/Definitions/TrackParametrization.hpp" #include "Acts/EventData/Charge.hpp" +#include "Acts/EventData/GenericBoundTrackParameters.hpp" #include "Acts/EventData/MultiTrajectory.hpp" -#include "Acts/EventData/SingleBoundTrackParameters.hpp" #include "Acts/EventData/TrackContainer.hpp" #include "Acts/EventData/TrackStatePropMask.hpp" #include "Acts/EventData/detail/TransformationBoundToFree.hpp" @@ -53,9 +53,9 @@ void packCovariance(const ActsSymMatrix<6>& from, float* to); Parameters convertTrackParametersToEdm4hep( const Acts::GeometryContext& gctx, double Bz, - const SingleBoundTrackParameters& params); + const GenericBoundTrackParameters& params); -SingleBoundTrackParameters convertTrackParametersFromEdm4hep( +GenericBoundTrackParameters convertTrackParametersFromEdm4hep( double Bz, const Parameters& params); } // namespace detail @@ -102,7 +102,7 @@ void writeTrack( trackState.location = edm4hep::TrackState::AtOther; // This makes the hard assumption that |q| = 1 - SingleBoundTrackParameters params{ + GenericBoundTrackParameters params{ state.referenceSurface().getSharedPtr(), state.parameters(), state.covariance()}; @@ -133,7 +133,7 @@ void writeTrack( auto& ipState = outTrackStates.emplace_back(); // Convert the track parameters at the IP - SingleBoundTrackParameters trackParams{ + GenericBoundTrackParameters trackParams{ track.referenceSurface().getSharedPtr(), track.parameters(), track.covariance()}; diff --git a/Plugins/EDM4hep/src/EDM4hepUtil.cpp b/Plugins/EDM4hep/src/EDM4hepUtil.cpp index 211297f439d..5e023c8131f 100644 --- a/Plugins/EDM4hep/src/EDM4hepUtil.cpp +++ b/Plugins/EDM4hep/src/EDM4hepUtil.cpp @@ -109,7 +109,7 @@ void unpackCovariance(const float* from, ActsSymMatrix<6>& to) { Parameters convertTrackParametersToEdm4hep( const Acts::GeometryContext& gctx, double Bz, - const SingleBoundTrackParameters& params) { + const GenericBoundTrackParameters& params) { Acts::Vector3 global = params.referenceSurface().localToGlobal( gctx, params.parameters().template head<2>(), params.momentum()); @@ -175,7 +175,7 @@ Parameters convertTrackParametersToEdm4hep( return result; } -SingleBoundTrackParameters convertTrackParametersFromEdm4hep( +GenericBoundTrackParameters convertTrackParametersFromEdm4hep( double Bz, const Parameters& params) { BoundVector targetPars; diff --git a/Tests/IntegrationTests/Fatras/FatrasSimulationTests.cpp b/Tests/IntegrationTests/Fatras/FatrasSimulationTests.cpp index 2d92b594b88..89c25807762 100644 --- a/Tests/IntegrationTests/Fatras/FatrasSimulationTests.cpp +++ b/Tests/IntegrationTests/Fatras/FatrasSimulationTests.cpp @@ -10,7 +10,6 @@ #include #include "Acts/Definitions/ParticleData.hpp" -#include "Acts/EventData/NeutralTrackParameters.hpp" #include "Acts/EventData/TrackParameters.hpp" #include "Acts/MagneticField/ConstantBField.hpp" #include "Acts/Propagator/EigenStepper.hpp" diff --git a/Tests/IntegrationTests/PropagationTests.hpp b/Tests/IntegrationTests/PropagationTests.hpp index 3d0ab9a9092..786b1769076 100644 --- a/Tests/IntegrationTests/PropagationTests.hpp +++ b/Tests/IntegrationTests/PropagationTests.hpp @@ -10,7 +10,6 @@ #include "Acts/Definitions/Direction.hpp" #include "Acts/Definitions/Units.hpp" -#include "Acts/EventData/NeutralTrackParameters.hpp" #include "Acts/EventData/TrackParameters.hpp" #include "Acts/Geometry/GeometryContext.hpp" #include "Acts/MagneticField/MagneticFieldContext.hpp" @@ -99,8 +98,8 @@ inline Acts::NeutralCurvilinearTrackParameters makeParametersCurvilinearNeutral( /// \warning Does not check that they are defined on the same surface. template inline void checkParametersConsistency( - const Acts::SingleBoundTrackParameters& cmp, - const Acts::SingleBoundTrackParameters& ref, + const Acts::GenericBoundTrackParameters& cmp, + const Acts::GenericBoundTrackParameters& ref, const Acts::GeometryContext& geoCtx, double epsPos, double epsDir, double epsMom) { using namespace Acts; @@ -134,8 +133,8 @@ inline void checkParametersConsistency( /// \warning Does not check that the parameters value itself are consistent. template inline void checkCovarianceConsistency( - const Acts::SingleBoundTrackParameters& cmp, - const Acts::SingleBoundTrackParameters& ref, + const Acts::GenericBoundTrackParameters& cmp, + const Acts::GenericBoundTrackParameters& ref, double relativeTolerance) { // either both or none have covariance set if (cmp.covariance().has_value()) { @@ -157,7 +156,7 @@ inline void checkCovarianceConsistency( /// Construct the transformation from the curvilinear to the global coordinates. template inline Acts::Transform3 makeCurvilinearTransform( - const Acts::SingleBoundTrackParameters& params, + const Acts::GenericBoundTrackParameters& params, const Acts::GeometryContext& geoCtx) { Acts::Vector3 unitW = params.unitDirection(); auto [unitU, unitV] = Acts::makeCurvilinearUnitVectors(unitW); @@ -176,7 +175,7 @@ inline Acts::Transform3 makeCurvilinearTransform( struct ZCylinderSurfaceBuilder { template std::shared_ptr operator()( - const Acts::SingleBoundTrackParameters& params, + const Acts::GenericBoundTrackParameters& params, const Acts::GeometryContext& geoCtx) { auto radius = params.position(geoCtx).template head<2>().norm(); auto halfz = std::numeric_limits::max(); @@ -189,7 +188,7 @@ struct ZCylinderSurfaceBuilder { struct DiscSurfaceBuilder { template std::shared_ptr operator()( - const Acts::SingleBoundTrackParameters& params, + const Acts::GenericBoundTrackParameters& params, const Acts::GeometryContext& geoCtx) { using namespace Acts; using namespace Acts::UnitLiterals; @@ -213,7 +212,7 @@ struct DiscSurfaceBuilder { struct PlaneSurfaceBuilder { template std::shared_ptr operator()( - const Acts::SingleBoundTrackParameters& params, + const Acts::GenericBoundTrackParameters& params, const Acts::GeometryContext& geoCtx) { return Acts::Surface::makeShared( makeCurvilinearTransform(params, geoCtx)); @@ -224,7 +223,7 @@ struct PlaneSurfaceBuilder { struct ZStrawSurfaceBuilder { template std::shared_ptr operator()( - const Acts::SingleBoundTrackParameters& params, + const Acts::GenericBoundTrackParameters& params, const Acts::GeometryContext& geoCtx) { return Acts::Surface::makeShared( Acts::Transform3(Acts::Translation3(params.position(geoCtx)))); @@ -242,7 +241,7 @@ template transportFreely( const propagator_t& propagator, const Acts::GeometryContext& geoCtx, const Acts::MagneticFieldContext& magCtx, - const Acts::SingleCurvilinearTrackParameters& initialParams, + const Acts::GenericCurvilinearTrackParameters& initialParams, double pathLength) { using namespace Acts::UnitLiterals; @@ -270,7 +269,7 @@ template transportToSurface( const propagator_t& propagator, const Acts::GeometryContext& geoCtx, const Acts::MagneticFieldContext& magCtx, - const Acts::SingleCurvilinearTrackParameters& initialParams, + const Acts::GenericCurvilinearTrackParameters& initialParams, const Acts::Surface& targetSurface, double pathLimit) { using namespace Acts::UnitLiterals; @@ -302,7 +301,7 @@ template & initialParams, + const Acts::GenericCurvilinearTrackParameters& initialParams, double pathLength, double epsPos, double epsDir, double epsMom) { // propagate parameters Acts::Direction::Forward auto [fwdParams, fwdPathLength] = @@ -329,7 +328,7 @@ template & initialParams, + const Acts::GenericCurvilinearTrackParameters& initialParams, double pathLength, surface_builder_t&& buildTargetSurface, double epsPos, double epsDir, double epsMom) { // free propagation for the given path length @@ -371,7 +370,7 @@ inline void runForwardComparisonTest( const cmp_propagator_t& cmpPropagator, const ref_propagator_t& refPropagator, const Acts::GeometryContext& geoCtx, const Acts::MagneticFieldContext& magCtx, - const Acts::SingleCurvilinearTrackParameters& initialParams, + const Acts::GenericCurvilinearTrackParameters& initialParams, double pathLength, double epsPos, double epsDir, double epsMom, double tolCov) { // propagate twice using the two different propagators @@ -402,7 +401,7 @@ inline void runToSurfaceComparisonTest( const cmp_propagator_t& cmpPropagator, const ref_propagator_t& refPropagator, const Acts::GeometryContext& geoCtx, const Acts::MagneticFieldContext& magCtx, - const Acts::SingleCurvilinearTrackParameters& initialParams, + const Acts::GenericCurvilinearTrackParameters& initialParams, double pathLength, surface_builder_t&& buildTargetSurface, double epsPos, double epsDir, double epsMom, double tolCov) { // free propagation with the reference propagator for the given path length diff --git a/Tests/UnitTests/Core/EventData/BoundTrackParametersTests.cpp b/Tests/UnitTests/Core/EventData/BoundTrackParametersTests.cpp index c51d6864604..2f0abcb294a 100644 --- a/Tests/UnitTests/Core/EventData/BoundTrackParametersTests.cpp +++ b/Tests/UnitTests/Core/EventData/BoundTrackParametersTests.cpp @@ -14,8 +14,7 @@ #include "Acts/Definitions/TrackParametrization.hpp" #include "Acts/Definitions/Units.hpp" #include "Acts/EventData/Charge.hpp" -#include "Acts/EventData/NeutralTrackParameters.hpp" -#include "Acts/EventData/SingleBoundTrackParameters.hpp" +#include "Acts/EventData/GenericBoundTrackParameters.hpp" #include "Acts/EventData/TrackParameters.hpp" #include "Acts/Geometry/GeometryContext.hpp" #include "Acts/Surfaces/ConeSurface.hpp" @@ -45,14 +44,14 @@ namespace { namespace bdata = boost::unit_test::data; using namespace Acts; using namespace Acts::UnitLiterals; -using AnyBoundTrackParameters = SingleBoundTrackParameters; +using AnyBoundTrackParameters = GenericBoundTrackParameters; constexpr auto eps = 8 * std::numeric_limits::epsilon(); const GeometryContext geoCtx; const BoundSymMatrix cov = BoundSymMatrix::Identity(); template -void checkParameters(const SingleBoundTrackParameters& params, +void checkParameters(const GenericBoundTrackParameters& params, double l0, double l1, double time, double phi, double theta, double p, double q, const Vector3& pos, const Vector3& unitDir) { diff --git a/Tests/UnitTests/Core/EventData/CurvilinearTrackParametersTests.cpp b/Tests/UnitTests/Core/EventData/CurvilinearTrackParametersTests.cpp index 2c26cb5445b..ffc4ebc8345 100644 --- a/Tests/UnitTests/Core/EventData/CurvilinearTrackParametersTests.cpp +++ b/Tests/UnitTests/Core/EventData/CurvilinearTrackParametersTests.cpp @@ -13,8 +13,7 @@ #include "Acts/Definitions/TrackParametrization.hpp" #include "Acts/Definitions/Units.hpp" #include "Acts/EventData/Charge.hpp" -#include "Acts/EventData/NeutralTrackParameters.hpp" -#include "Acts/EventData/SingleCurvilinearTrackParameters.hpp" +#include "Acts/EventData/GenericCurvilinearTrackParameters.hpp" #include "Acts/EventData/TrackParameters.hpp" #include "Acts/Geometry/GeometryContext.hpp" #include "Acts/Tests/CommonHelpers/FloatComparisons.hpp" @@ -34,14 +33,14 @@ namespace { using namespace Acts; using namespace Acts::UnitLiterals; using AnyCurvilinearTrackParameters = - SingleCurvilinearTrackParameters; + GenericCurvilinearTrackParameters; constexpr auto eps = 8 * std::numeric_limits::epsilon(); const GeometryContext geoCtx; const BoundSymMatrix cov = BoundSymMatrix::Identity(); template -void checkParameters(const SingleCurvilinearTrackParameters& params, +void checkParameters(const GenericCurvilinearTrackParameters& params, double phi, double theta, double p, double q, const Vector4& pos4, const Vector3& unitDir) { const auto qOverP = (q != 0) ? (q / p) : (1 / p); diff --git a/Tests/UnitTests/Core/EventData/FreeTrackParametersTests.cpp b/Tests/UnitTests/Core/EventData/FreeTrackParametersTests.cpp index 843a6d35043..5fdf2087db8 100644 --- a/Tests/UnitTests/Core/EventData/FreeTrackParametersTests.cpp +++ b/Tests/UnitTests/Core/EventData/FreeTrackParametersTests.cpp @@ -14,8 +14,7 @@ #include "Acts/Definitions/TrackParametrization.hpp" #include "Acts/Definitions/Units.hpp" #include "Acts/EventData/Charge.hpp" -#include "Acts/EventData/NeutralTrackParameters.hpp" -#include "Acts/EventData/SingleFreeTrackParameters.hpp" +#include "Acts/EventData/GenericFreeTrackParameters.hpp" #include "Acts/EventData/TrackParameters.hpp" #include "Acts/Geometry/GeometryContext.hpp" #include "Acts/Tests/CommonHelpers/FloatComparisons.hpp" @@ -32,14 +31,14 @@ namespace { using namespace Acts; using namespace Acts::UnitLiterals; -using AnyFreeTrackParameters = SingleFreeTrackParameters; +using AnyFreeTrackParameters = GenericFreeTrackParameters; constexpr auto eps = 8 * std::numeric_limits::epsilon(); const GeometryContext geoCtx; const FreeSymMatrix cov = FreeSymMatrix::Identity(); template -void checkParameters(const SingleFreeTrackParameters& params, +void checkParameters(const GenericFreeTrackParameters& params, const Vector4& pos4, const Vector3& unitDir, double p, double q) { const auto qOverP = (q != 0) ? (q / p) : (1 / p); diff --git a/Tests/UnitTests/Core/EventData/MultiComponentBoundTrackParametersTests.cpp b/Tests/UnitTests/Core/EventData/MultiComponentBoundTrackParametersTests.cpp index 3c6ffdfba8c..45a1f8b08d8 100644 --- a/Tests/UnitTests/Core/EventData/MultiComponentBoundTrackParametersTests.cpp +++ b/Tests/UnitTests/Core/EventData/MultiComponentBoundTrackParametersTests.cpp @@ -11,7 +11,7 @@ #include "Acts/Definitions/Algebra.hpp" #include "Acts/Definitions/TrackParametrization.hpp" #include "Acts/Definitions/Units.hpp" -#include "Acts/EventData/SingleBoundTrackParameters.hpp" +#include "Acts/EventData/GenericBoundTrackParameters.hpp" #include "Acts/Geometry/GeometryContext.hpp" #include "Acts/Surfaces/Surface.hpp" #include @@ -57,7 +57,7 @@ BOOST_AUTO_TEST_CASE(test_accessors) { auto surface = Acts::Surface::makeShared( Vector3::Ones(), Vector3::Ones().normalized()); - const SingleBoundTrackParameters single_pars( + const GenericBoundTrackParameters single_pars( surface, BoundVector::Ones(), cov); const auto multi_pars = [&]() { diff --git a/Tests/UnitTests/Core/Material/VolumeMaterialMapperTests.cpp b/Tests/UnitTests/Core/Material/VolumeMaterialMapperTests.cpp index bc46cfd39ad..963b574379e 100644 --- a/Tests/UnitTests/Core/Material/VolumeMaterialMapperTests.cpp +++ b/Tests/UnitTests/Core/Material/VolumeMaterialMapperTests.cpp @@ -11,7 +11,6 @@ #include "Acts/Definitions/Algebra.hpp" #include "Acts/Definitions/Direction.hpp" #include "Acts/Definitions/Units.hpp" -#include "Acts/EventData/NeutralTrackParameters.hpp" #include "Acts/EventData/TrackParameters.hpp" #include "Acts/Geometry/CuboidVolumeBuilder.hpp" #include "Acts/Geometry/GeometryContext.hpp" diff --git a/Tests/UnitTests/Core/Propagator/AtlasStepperTests.cpp b/Tests/UnitTests/Core/Propagator/AtlasStepperTests.cpp index 58b995ab371..4df6a9e09aa 100644 --- a/Tests/UnitTests/Core/Propagator/AtlasStepperTests.cpp +++ b/Tests/UnitTests/Core/Propagator/AtlasStepperTests.cpp @@ -15,8 +15,8 @@ #include "Acts/Definitions/TrackParametrization.hpp" #include "Acts/Definitions/Units.hpp" #include "Acts/EventData/Charge.hpp" -#include "Acts/EventData/SingleBoundTrackParameters.hpp" -#include "Acts/EventData/SingleCurvilinearTrackParameters.hpp" +#include "Acts/EventData/GenericBoundTrackParameters.hpp" +#include "Acts/EventData/GenericCurvilinearTrackParameters.hpp" #include "Acts/EventData/TrackParameters.hpp" #include "Acts/EventData/detail/TransformationBoundToFree.hpp" #include "Acts/Geometry/GeometryContext.hpp" diff --git a/Tests/UnitTests/Core/Propagator/EigenStepperTests.cpp b/Tests/UnitTests/Core/Propagator/EigenStepperTests.cpp index e753b82e215..cf3ce2c6fc7 100644 --- a/Tests/UnitTests/Core/Propagator/EigenStepperTests.cpp +++ b/Tests/UnitTests/Core/Propagator/EigenStepperTests.cpp @@ -13,9 +13,8 @@ #include "Acts/Definitions/TrackParametrization.hpp" #include "Acts/Definitions/Units.hpp" #include "Acts/EventData/Charge.hpp" -#include "Acts/EventData/NeutralTrackParameters.hpp" -#include "Acts/EventData/SingleBoundTrackParameters.hpp" -#include "Acts/EventData/SingleCurvilinearTrackParameters.hpp" +#include "Acts/EventData/GenericBoundTrackParameters.hpp" +#include "Acts/EventData/GenericCurvilinearTrackParameters.hpp" #include "Acts/EventData/TrackParameters.hpp" #include "Acts/EventData/detail/TransformationBoundToFree.hpp" #include "Acts/Geometry/BoundarySurfaceT.hpp" diff --git a/Tests/UnitTests/Core/Propagator/ExtrapolatorTests.cpp b/Tests/UnitTests/Core/Propagator/ExtrapolatorTests.cpp index 0372ba35dd0..6ae63dccf95 100644 --- a/Tests/UnitTests/Core/Propagator/ExtrapolatorTests.cpp +++ b/Tests/UnitTests/Core/Propagator/ExtrapolatorTests.cpp @@ -14,7 +14,7 @@ #include "Acts/Definitions/Direction.hpp" #include "Acts/Definitions/TrackParametrization.hpp" #include "Acts/Definitions/Units.hpp" -#include "Acts/EventData/SingleCurvilinearTrackParameters.hpp" +#include "Acts/EventData/GenericCurvilinearTrackParameters.hpp" #include "Acts/EventData/TrackParameters.hpp" #include "Acts/Geometry/GeometryContext.hpp" #include "Acts/MagneticField/ConstantBField.hpp" diff --git a/Tests/UnitTests/Core/Propagator/JacobianTests.cpp b/Tests/UnitTests/Core/Propagator/JacobianTests.cpp index c43a8f433f4..d21ce86f724 100644 --- a/Tests/UnitTests/Core/Propagator/JacobianTests.cpp +++ b/Tests/UnitTests/Core/Propagator/JacobianTests.cpp @@ -13,7 +13,7 @@ #include "Acts/Definitions/Algebra.hpp" #include "Acts/Definitions/TrackParametrization.hpp" #include "Acts/Definitions/Units.hpp" -#include "Acts/EventData/SingleCurvilinearTrackParameters.hpp" +#include "Acts/EventData/GenericCurvilinearTrackParameters.hpp" #include "Acts/EventData/TrackParameters.hpp" #include "Acts/Geometry/GeometryContext.hpp" #include "Acts/MagneticField/ConstantBField.hpp" diff --git a/Tests/UnitTests/Core/Propagator/KalmanExtrapolatorTests.cpp b/Tests/UnitTests/Core/Propagator/KalmanExtrapolatorTests.cpp index 9d1667bf2b6..aff85613a2f 100644 --- a/Tests/UnitTests/Core/Propagator/KalmanExtrapolatorTests.cpp +++ b/Tests/UnitTests/Core/Propagator/KalmanExtrapolatorTests.cpp @@ -12,7 +12,7 @@ #include "Acts/Definitions/Direction.hpp" #include "Acts/Definitions/TrackParametrization.hpp" #include "Acts/Definitions/Units.hpp" -#include "Acts/EventData/SingleCurvilinearTrackParameters.hpp" +#include "Acts/EventData/GenericCurvilinearTrackParameters.hpp" #include "Acts/EventData/TrackParameters.hpp" #include "Acts/Geometry/GeometryContext.hpp" #include "Acts/MagneticField/ConstantBField.hpp" diff --git a/Tests/UnitTests/Core/Propagator/LoopProtectionTests.cpp b/Tests/UnitTests/Core/Propagator/LoopProtectionTests.cpp index 981755e8a73..978d55d17dc 100644 --- a/Tests/UnitTests/Core/Propagator/LoopProtectionTests.cpp +++ b/Tests/UnitTests/Core/Propagator/LoopProtectionTests.cpp @@ -13,7 +13,7 @@ #include "Acts/Definitions/Algebra.hpp" #include "Acts/Definitions/Direction.hpp" #include "Acts/Definitions/Units.hpp" -#include "Acts/EventData/SingleCurvilinearTrackParameters.hpp" +#include "Acts/EventData/GenericCurvilinearTrackParameters.hpp" #include "Acts/EventData/TrackParameters.hpp" #include "Acts/Geometry/GeometryContext.hpp" #include "Acts/MagneticField/ConstantBField.hpp" diff --git a/Tests/UnitTests/Core/Propagator/MaterialCollectionTests.cpp b/Tests/UnitTests/Core/Propagator/MaterialCollectionTests.cpp index 69b2f72bb17..723a6cbadf5 100644 --- a/Tests/UnitTests/Core/Propagator/MaterialCollectionTests.cpp +++ b/Tests/UnitTests/Core/Propagator/MaterialCollectionTests.cpp @@ -14,7 +14,7 @@ #include "Acts/Definitions/Direction.hpp" #include "Acts/Definitions/TrackParametrization.hpp" #include "Acts/Definitions/Units.hpp" -#include "Acts/EventData/SingleCurvilinearTrackParameters.hpp" +#include "Acts/EventData/GenericCurvilinearTrackParameters.hpp" #include "Acts/EventData/TrackParameters.hpp" #include "Acts/Geometry/GeometryContext.hpp" #include "Acts/Geometry/GeometryIdentifier.hpp" diff --git a/Tests/UnitTests/Core/Propagator/MultiStepperTests.cpp b/Tests/UnitTests/Core/Propagator/MultiStepperTests.cpp index 49aff99e094..12865ee271c 100644 --- a/Tests/UnitTests/Core/Propagator/MultiStepperTests.cpp +++ b/Tests/UnitTests/Core/Propagator/MultiStepperTests.cpp @@ -12,8 +12,8 @@ #include "Acts/Definitions/Direction.hpp" #include "Acts/Definitions/TrackParametrization.hpp" #include "Acts/EventData/Charge.hpp" +#include "Acts/EventData/GenericBoundTrackParameters.hpp" #include "Acts/EventData/MultiComponentBoundTrackParameters.hpp" -#include "Acts/EventData/SingleBoundTrackParameters.hpp" #include "Acts/EventData/TrackParameters.hpp" #include "Acts/EventData/detail/CorrectedTransformationFreeToBound.hpp" #include "Acts/Geometry/GeometryContext.hpp" @@ -227,7 +227,7 @@ void test_multi_stepper_vs_eigen_stepper() { Vector3::Zero(), Vector3::Ones().normalized()); MultiComponentBoundTrackParameters multi_pars(surface, cmps); - SingleBoundTrackParameters single_pars(surface, pars, cov); + GenericBoundTrackParameters single_pars(surface, pars, cov); MultiState multi_state(geoCtx, magCtx, defaultBField, multi_pars, defaultNDir, defaultStepSize); @@ -603,7 +603,7 @@ void test_combined_curvilinear_state_function() { std::vector>> cmps(4, {0.25, pars, cov}); - SingleBoundTrackParameters check_pars(surface, pars, cov); + GenericBoundTrackParameters check_pars(surface, pars, cov); MultiComponentBoundTrackParameters multi_pars(surface, cmps); MultiState multi_state(geoCtx, magCtx, defaultBField, multi_pars, defaultNDir, diff --git a/Tests/UnitTests/Core/Propagator/NavigatorTests.cpp b/Tests/UnitTests/Core/Propagator/NavigatorTests.cpp index 6e386cc58f6..896bf8c5f2b 100644 --- a/Tests/UnitTests/Core/Propagator/NavigatorTests.cpp +++ b/Tests/UnitTests/Core/Propagator/NavigatorTests.cpp @@ -16,7 +16,7 @@ #include "Acts/Definitions/Tolerance.hpp" #include "Acts/Definitions/TrackParametrization.hpp" #include "Acts/Definitions/Units.hpp" -#include "Acts/EventData/SingleBoundTrackParameters.hpp" +#include "Acts/EventData/GenericBoundTrackParameters.hpp" #include "Acts/EventData/TrackParameters.hpp" #include "Acts/Geometry/GeometryContext.hpp" #include "Acts/Geometry/TrackingGeometry.hpp" diff --git a/Tests/UnitTests/Core/Propagator/PropagatorTests.cpp b/Tests/UnitTests/Core/Propagator/PropagatorTests.cpp index 59b3538b449..51c1af0abda 100644 --- a/Tests/UnitTests/Core/Propagator/PropagatorTests.cpp +++ b/Tests/UnitTests/Core/Propagator/PropagatorTests.cpp @@ -14,8 +14,8 @@ #include "Acts/Definitions/Direction.hpp" #include "Acts/Definitions/TrackParametrization.hpp" #include "Acts/Definitions/Units.hpp" -#include "Acts/EventData/SingleBoundTrackParameters.hpp" -#include "Acts/EventData/SingleCurvilinearTrackParameters.hpp" +#include "Acts/EventData/GenericBoundTrackParameters.hpp" +#include "Acts/EventData/GenericCurvilinearTrackParameters.hpp" #include "Acts/EventData/TrackParameters.hpp" #include "Acts/Geometry/GeometryContext.hpp" #include "Acts/Geometry/GeometryIdentifier.hpp" diff --git a/Tests/UnitTests/Core/Propagator/StraightLineStepperTests.cpp b/Tests/UnitTests/Core/Propagator/StraightLineStepperTests.cpp index ce44cab69e3..820a19a550a 100644 --- a/Tests/UnitTests/Core/Propagator/StraightLineStepperTests.cpp +++ b/Tests/UnitTests/Core/Propagator/StraightLineStepperTests.cpp @@ -12,9 +12,8 @@ #include "Acts/Definitions/Direction.hpp" #include "Acts/Definitions/TrackParametrization.hpp" #include "Acts/EventData/Charge.hpp" -#include "Acts/EventData/NeutralTrackParameters.hpp" -#include "Acts/EventData/SingleBoundTrackParameters.hpp" -#include "Acts/EventData/SingleCurvilinearTrackParameters.hpp" +#include "Acts/EventData/GenericBoundTrackParameters.hpp" +#include "Acts/EventData/GenericCurvilinearTrackParameters.hpp" #include "Acts/EventData/TrackParameters.hpp" #include "Acts/EventData/detail/CorrectedTransformationFreeToBound.hpp" #include "Acts/EventData/detail/TransformationBoundToFree.hpp" diff --git a/Tests/UnitTests/Core/Seeding/EstimateTrackParamsFromSeedTest.cpp b/Tests/UnitTests/Core/Seeding/EstimateTrackParamsFromSeedTest.cpp index 740ad594d19..e440dbe7ea7 100644 --- a/Tests/UnitTests/Core/Seeding/EstimateTrackParamsFromSeedTest.cpp +++ b/Tests/UnitTests/Core/Seeding/EstimateTrackParamsFromSeedTest.cpp @@ -12,7 +12,7 @@ #include "Acts/Definitions/Direction.hpp" #include "Acts/Definitions/TrackParametrization.hpp" #include "Acts/Definitions/Units.hpp" -#include "Acts/EventData/SingleCurvilinearTrackParameters.hpp" +#include "Acts/EventData/GenericCurvilinearTrackParameters.hpp" #include "Acts/EventData/TrackParameters.hpp" #include "Acts/Geometry/GeometryContext.hpp" #include "Acts/Geometry/GeometryIdentifier.hpp" diff --git a/Tests/UnitTests/Core/SpacePointFormation/SpacePointBuilderTests.cpp b/Tests/UnitTests/Core/SpacePointFormation/SpacePointBuilderTests.cpp index 6eb3625a646..ecd2be80d67 100644 --- a/Tests/UnitTests/Core/SpacePointFormation/SpacePointBuilderTests.cpp +++ b/Tests/UnitTests/Core/SpacePointFormation/SpacePointBuilderTests.cpp @@ -13,8 +13,8 @@ #include "Acts/Definitions/Direction.hpp" #include "Acts/Definitions/TrackParametrization.hpp" #include "Acts/Definitions/Units.hpp" +#include "Acts/EventData/GenericCurvilinearTrackParameters.hpp" #include "Acts/EventData/Measurement.hpp" -#include "Acts/EventData/SingleCurvilinearTrackParameters.hpp" #include "Acts/EventData/SourceLink.hpp" #include "Acts/EventData/TrackParameters.hpp" #include "Acts/Geometry/GeometryContext.hpp" diff --git a/Tests/UnitTests/Core/TrackFinding/CombinatorialKalmanFilterTests.cpp b/Tests/UnitTests/Core/TrackFinding/CombinatorialKalmanFilterTests.cpp index a20e898c7c5..22b1eb31e27 100644 --- a/Tests/UnitTests/Core/TrackFinding/CombinatorialKalmanFilterTests.cpp +++ b/Tests/UnitTests/Core/TrackFinding/CombinatorialKalmanFilterTests.cpp @@ -12,9 +12,9 @@ #include "Acts/Definitions/Direction.hpp" #include "Acts/Definitions/TrackParametrization.hpp" #include "Acts/Definitions/Units.hpp" +#include "Acts/EventData/GenericBoundTrackParameters.hpp" +#include "Acts/EventData/GenericCurvilinearTrackParameters.hpp" #include "Acts/EventData/MultiTrajectory.hpp" -#include "Acts/EventData/SingleBoundTrackParameters.hpp" -#include "Acts/EventData/SingleCurvilinearTrackParameters.hpp" #include "Acts/EventData/SourceLink.hpp" #include "Acts/EventData/TrackContainer.hpp" #include "Acts/EventData/TrackParameters.hpp" diff --git a/Tests/UnitTests/Core/TrackFitting/GsfTests.cpp b/Tests/UnitTests/Core/TrackFitting/GsfTests.cpp index c8079926147..fb45398381b 100644 --- a/Tests/UnitTests/Core/TrackFitting/GsfTests.cpp +++ b/Tests/UnitTests/Core/TrackFitting/GsfTests.cpp @@ -14,10 +14,10 @@ #include "Acts/Definitions/TrackParametrization.hpp" #include "Acts/Definitions/Units.hpp" #include "Acts/EventData/Charge.hpp" +#include "Acts/EventData/GenericBoundTrackParameters.hpp" +#include "Acts/EventData/GenericCurvilinearTrackParameters.hpp" #include "Acts/EventData/MultiComponentBoundTrackParameters.hpp" #include "Acts/EventData/MultiTrajectory.hpp" -#include "Acts/EventData/SingleBoundTrackParameters.hpp" -#include "Acts/EventData/SingleCurvilinearTrackParameters.hpp" #include "Acts/EventData/TrackContainer.hpp" #include "Acts/EventData/TrackParameters.hpp" #include "Acts/EventData/TrackProxy.hpp" @@ -98,14 +98,14 @@ auto makeDefaultGsfOptions() { } // A Helper type to allow us to put the MultiComponentBoundTrackParameters into -// the function so that it can also be used as SingleBoundTrackParameters for +// the function so that it can also be used as GenericBoundTrackParameters for // the MeasurementsCreator template -struct MultiCmpsParsInterface : public SingleBoundTrackParameters { +struct MultiCmpsParsInterface : public GenericBoundTrackParameters { MultiComponentBoundTrackParameters multi_pars; MultiCmpsParsInterface(const MultiComponentBoundTrackParameters &p) - : SingleBoundTrackParameters( + : GenericBoundTrackParameters( p.referenceSurface().getSharedPtr(), p.parameters(), p.covariance()), multi_pars(p) {} diff --git a/Tests/UnitTests/Core/TrackFitting/KalmanFitterTests.cpp b/Tests/UnitTests/Core/TrackFitting/KalmanFitterTests.cpp index 78f9cfe49c8..ec5f4794562 100644 --- a/Tests/UnitTests/Core/TrackFitting/KalmanFitterTests.cpp +++ b/Tests/UnitTests/Core/TrackFitting/KalmanFitterTests.cpp @@ -12,8 +12,8 @@ #include "Acts/Definitions/Direction.hpp" #include "Acts/Definitions/TrackParametrization.hpp" #include "Acts/Definitions/Units.hpp" +#include "Acts/EventData/GenericCurvilinearTrackParameters.hpp" #include "Acts/EventData/MultiTrajectory.hpp" -#include "Acts/EventData/SingleCurvilinearTrackParameters.hpp" #include "Acts/EventData/TrackParameters.hpp" #include "Acts/EventData/TrackStatePropMask.hpp" #include "Acts/EventData/VectorMultiTrajectory.hpp" diff --git a/Tests/UnitTests/Core/Vertexing/AdaptiveGridTrackDensityTests.cpp b/Tests/UnitTests/Core/Vertexing/AdaptiveGridTrackDensityTests.cpp index 60cc230cb13..71e95d23896 100644 --- a/Tests/UnitTests/Core/Vertexing/AdaptiveGridTrackDensityTests.cpp +++ b/Tests/UnitTests/Core/Vertexing/AdaptiveGridTrackDensityTests.cpp @@ -13,7 +13,7 @@ #include "Acts/Definitions/Algebra.hpp" #include "Acts/Definitions/TrackParametrization.hpp" #include "Acts/Definitions/Units.hpp" -#include "Acts/EventData/SingleBoundTrackParameters.hpp" +#include "Acts/EventData/GenericBoundTrackParameters.hpp" #include "Acts/EventData/TrackParameters.hpp" #include "Acts/Surfaces/PerigeeSurface.hpp" #include "Acts/Surfaces/Surface.hpp" diff --git a/Tests/UnitTests/Core/Vertexing/AdaptiveMultiVertexFinderTests.cpp b/Tests/UnitTests/Core/Vertexing/AdaptiveMultiVertexFinderTests.cpp index 3fd95204bc8..b4314bba723 100644 --- a/Tests/UnitTests/Core/Vertexing/AdaptiveMultiVertexFinderTests.cpp +++ b/Tests/UnitTests/Core/Vertexing/AdaptiveMultiVertexFinderTests.cpp @@ -14,7 +14,7 @@ #include "Acts/Definitions/Direction.hpp" #include "Acts/Definitions/TrackParametrization.hpp" #include "Acts/Definitions/Units.hpp" -#include "Acts/EventData/SingleBoundTrackParameters.hpp" +#include "Acts/EventData/GenericBoundTrackParameters.hpp" #include "Acts/EventData/TrackParameters.hpp" #include "Acts/Geometry/GeometryContext.hpp" #include "Acts/Geometry/GeometryIdentifier.hpp" diff --git a/Tests/UnitTests/Core/Vertexing/AdaptiveMultiVertexFitterTests.cpp b/Tests/UnitTests/Core/Vertexing/AdaptiveMultiVertexFitterTests.cpp index 33a23d893af..fcb7562fdff 100644 --- a/Tests/UnitTests/Core/Vertexing/AdaptiveMultiVertexFitterTests.cpp +++ b/Tests/UnitTests/Core/Vertexing/AdaptiveMultiVertexFitterTests.cpp @@ -15,7 +15,7 @@ #include "Acts/Definitions/TrackParametrization.hpp" #include "Acts/Definitions/Units.hpp" #include "Acts/EventData/Charge.hpp" -#include "Acts/EventData/SingleBoundTrackParameters.hpp" +#include "Acts/EventData/GenericBoundTrackParameters.hpp" #include "Acts/EventData/TrackParameters.hpp" #include "Acts/Geometry/GeometryContext.hpp" #include "Acts/Geometry/GeometryIdentifier.hpp" diff --git a/Tests/UnitTests/Core/Vertexing/FullBilloirVertexFitterTests.cpp b/Tests/UnitTests/Core/Vertexing/FullBilloirVertexFitterTests.cpp index 6ee8b7517e7..e78479f8186 100644 --- a/Tests/UnitTests/Core/Vertexing/FullBilloirVertexFitterTests.cpp +++ b/Tests/UnitTests/Core/Vertexing/FullBilloirVertexFitterTests.cpp @@ -14,7 +14,7 @@ #include "Acts/Definitions/Direction.hpp" #include "Acts/Definitions/TrackParametrization.hpp" #include "Acts/Definitions/Units.hpp" -#include "Acts/EventData/SingleBoundTrackParameters.hpp" +#include "Acts/EventData/GenericBoundTrackParameters.hpp" #include "Acts/EventData/TrackParameters.hpp" #include "Acts/Geometry/GeometryContext.hpp" #include "Acts/Geometry/GeometryIdentifier.hpp" diff --git a/Tests/UnitTests/Core/Vertexing/GaussianGridTrackDensityTests.cpp b/Tests/UnitTests/Core/Vertexing/GaussianGridTrackDensityTests.cpp index 496d8a5999a..49da24c0f61 100644 --- a/Tests/UnitTests/Core/Vertexing/GaussianGridTrackDensityTests.cpp +++ b/Tests/UnitTests/Core/Vertexing/GaussianGridTrackDensityTests.cpp @@ -13,7 +13,7 @@ #include "Acts/Definitions/Algebra.hpp" #include "Acts/Definitions/TrackParametrization.hpp" #include "Acts/Definitions/Units.hpp" -#include "Acts/EventData/SingleBoundTrackParameters.hpp" +#include "Acts/EventData/GenericBoundTrackParameters.hpp" #include "Acts/EventData/TrackParameters.hpp" #include "Acts/Geometry/GeometryContext.hpp" #include "Acts/Surfaces/PerigeeSurface.hpp" diff --git a/Tests/UnitTests/Core/Vertexing/GridDensityVertexFinderTests.cpp b/Tests/UnitTests/Core/Vertexing/GridDensityVertexFinderTests.cpp index b2fd6c71733..d39dba276c4 100644 --- a/Tests/UnitTests/Core/Vertexing/GridDensityVertexFinderTests.cpp +++ b/Tests/UnitTests/Core/Vertexing/GridDensityVertexFinderTests.cpp @@ -15,7 +15,7 @@ #include "Acts/Definitions/TrackParametrization.hpp" #include "Acts/Definitions/Units.hpp" #include "Acts/EventData/Charge.hpp" -#include "Acts/EventData/SingleBoundTrackParameters.hpp" +#include "Acts/EventData/GenericBoundTrackParameters.hpp" #include "Acts/EventData/TrackParameters.hpp" #include "Acts/Geometry/GeometryContext.hpp" #include "Acts/MagneticField/MagneticFieldContext.hpp" diff --git a/Tests/UnitTests/Core/Vertexing/ImpactPointEstimatorTests.cpp b/Tests/UnitTests/Core/Vertexing/ImpactPointEstimatorTests.cpp index 24242b0fd8c..c5b32efd8ab 100644 --- a/Tests/UnitTests/Core/Vertexing/ImpactPointEstimatorTests.cpp +++ b/Tests/UnitTests/Core/Vertexing/ImpactPointEstimatorTests.cpp @@ -15,7 +15,7 @@ #include "Acts/Definitions/TrackParametrization.hpp" #include "Acts/Definitions/Units.hpp" #include "Acts/EventData/Charge.hpp" -#include "Acts/EventData/SingleBoundTrackParameters.hpp" +#include "Acts/EventData/GenericBoundTrackParameters.hpp" #include "Acts/EventData/TrackParameters.hpp" #include "Acts/Geometry/GeometryContext.hpp" #include "Acts/Geometry/GeometryIdentifier.hpp" diff --git a/Tests/UnitTests/Core/Vertexing/IterativeVertexFinderTests.cpp b/Tests/UnitTests/Core/Vertexing/IterativeVertexFinderTests.cpp index 187de631f1e..bf124b99bb8 100644 --- a/Tests/UnitTests/Core/Vertexing/IterativeVertexFinderTests.cpp +++ b/Tests/UnitTests/Core/Vertexing/IterativeVertexFinderTests.cpp @@ -16,7 +16,7 @@ #include "Acts/Definitions/TrackParametrization.hpp" #include "Acts/Definitions/Units.hpp" #include "Acts/EventData/Charge.hpp" -#include "Acts/EventData/SingleBoundTrackParameters.hpp" +#include "Acts/EventData/GenericBoundTrackParameters.hpp" #include "Acts/EventData/TrackParameters.hpp" #include "Acts/Geometry/GeometryContext.hpp" #include "Acts/Geometry/GeometryIdentifier.hpp" diff --git a/Tests/UnitTests/Core/Vertexing/KalmanVertexTrackUpdaterTests.cpp b/Tests/UnitTests/Core/Vertexing/KalmanVertexTrackUpdaterTests.cpp index f26fdd9535e..ad896275b0c 100644 --- a/Tests/UnitTests/Core/Vertexing/KalmanVertexTrackUpdaterTests.cpp +++ b/Tests/UnitTests/Core/Vertexing/KalmanVertexTrackUpdaterTests.cpp @@ -14,7 +14,7 @@ #include "Acts/Definitions/Direction.hpp" #include "Acts/Definitions/TrackParametrization.hpp" #include "Acts/Definitions/Units.hpp" -#include "Acts/EventData/SingleBoundTrackParameters.hpp" +#include "Acts/EventData/GenericBoundTrackParameters.hpp" #include "Acts/EventData/TrackParameters.hpp" #include "Acts/Geometry/GeometryContext.hpp" #include "Acts/Geometry/GeometryIdentifier.hpp" diff --git a/Tests/UnitTests/Core/Vertexing/KalmanVertexUpdaterTests.cpp b/Tests/UnitTests/Core/Vertexing/KalmanVertexUpdaterTests.cpp index 295a9d968c0..0e3f2ea123d 100644 --- a/Tests/UnitTests/Core/Vertexing/KalmanVertexUpdaterTests.cpp +++ b/Tests/UnitTests/Core/Vertexing/KalmanVertexUpdaterTests.cpp @@ -14,7 +14,7 @@ #include "Acts/Definitions/Direction.hpp" #include "Acts/Definitions/TrackParametrization.hpp" #include "Acts/Definitions/Units.hpp" -#include "Acts/EventData/SingleBoundTrackParameters.hpp" +#include "Acts/EventData/GenericBoundTrackParameters.hpp" #include "Acts/EventData/TrackParameters.hpp" #include "Acts/Geometry/GeometryContext.hpp" #include "Acts/Geometry/GeometryIdentifier.hpp" diff --git a/Tests/UnitTests/Core/Vertexing/TrackDensityVertexFinderTests.cpp b/Tests/UnitTests/Core/Vertexing/TrackDensityVertexFinderTests.cpp index a44f4e3a814..02981b115ab 100644 --- a/Tests/UnitTests/Core/Vertexing/TrackDensityVertexFinderTests.cpp +++ b/Tests/UnitTests/Core/Vertexing/TrackDensityVertexFinderTests.cpp @@ -15,7 +15,7 @@ #include "Acts/Definitions/TrackParametrization.hpp" #include "Acts/Definitions/Units.hpp" #include "Acts/EventData/Charge.hpp" -#include "Acts/EventData/SingleBoundTrackParameters.hpp" +#include "Acts/EventData/GenericBoundTrackParameters.hpp" #include "Acts/EventData/TrackParameters.hpp" #include "Acts/Geometry/GeometryContext.hpp" #include "Acts/MagneticField/MagneticFieldContext.hpp" diff --git a/Tests/UnitTests/Plugins/EDM4hep/ConvertTrackEDM4hepTest.cpp b/Tests/UnitTests/Plugins/EDM4hep/ConvertTrackEDM4hepTest.cpp index 7f26b2d4234..dac0dc3e41a 100644 --- a/Tests/UnitTests/Plugins/EDM4hep/ConvertTrackEDM4hepTest.cpp +++ b/Tests/UnitTests/Plugins/EDM4hep/ConvertTrackEDM4hepTest.cpp @@ -13,8 +13,8 @@ #include "Acts/Definitions/TrackParametrization.hpp" #include "Acts/EventData/Charge.hpp" +#include "Acts/EventData/GenericBoundTrackParameters.hpp" #include "Acts/EventData/MultiTrajectory.hpp" -#include "Acts/EventData/SingleBoundTrackParameters.hpp" #include "Acts/EventData/TrackHelpers.hpp" #include "Acts/EventData/TrackStatePropMask.hpp" #include "Acts/EventData/VectorMultiTrajectory.hpp" @@ -48,7 +48,7 @@ BOOST_AUTO_TEST_CASE(ConvertTrackParametersToEdm4hepWithPerigee) { cov.setIdentity(); cov(5, 5) = 25_ns; - SingleBoundTrackParameters boundPar{refSurface, par, cov}; + GenericBoundTrackParameters boundPar{refSurface, par, cov}; double Bz = 2_T; @@ -71,7 +71,7 @@ BOOST_AUTO_TEST_CASE(ConvertTrackParametersToEdm4hepWithPerigee) { // convert back for roundtrip test - SingleBoundTrackParameters roundtripPar = + GenericBoundTrackParameters roundtripPar = EDM4hepUtil::detail::convertTrackParametersFromEdm4hep(Bz, converted); BOOST_CHECK(roundtripPar.parameters().isApprox(boundPar.parameters())); @@ -90,7 +90,7 @@ BOOST_AUTO_TEST_CASE(ConvertTrackParametersToEdm4hepWithOutPerigee) { cov.setIdentity(); cov(5, 5) = 25_ns; - SingleBoundTrackParameters boundPar{refSurface, par, cov}; + GenericBoundTrackParameters boundPar{refSurface, par, cov}; double Bz = 2_T; @@ -112,7 +112,7 @@ BOOST_AUTO_TEST_CASE(ConvertTrackParametersToEdm4hepWithOutPerigee) { BOOST_CHECK_EQUAL(converted.covariance.value()(5, 5), 25_ns); // convert back for roundtrip test - SingleBoundTrackParameters roundtripPar = + GenericBoundTrackParameters roundtripPar = EDM4hepUtil::detail::convertTrackParametersFromEdm4hep(Bz, converted); BOOST_CHECK_EQUAL(roundtripPar.parameters().template head<2>(), @@ -129,8 +129,8 @@ BOOST_AUTO_TEST_CASE(ConvertTrackParametersToEdm4hepWithPerigeeNoCov) { par << 1_mm, 5_mm, 0, M_PI_2, -1 / 1_GeV, 5_ns; // -> perpendicular to perigee and pointing right, should be PCA - SingleBoundTrackParameters boundPar{refSurface, par, - std::nullopt}; + GenericBoundTrackParameters boundPar{refSurface, par, + std::nullopt}; double Bz = 2_T; @@ -148,7 +148,7 @@ BOOST_AUTO_TEST_CASE(ConvertTrackParametersToEdm4hepWithPerigeeNoCov) { // convert back for roundtrip test - SingleBoundTrackParameters roundtripPar = + GenericBoundTrackParameters roundtripPar = EDM4hepUtil::detail::convertTrackParametersFromEdm4hep(Bz, converted); BOOST_CHECK(roundtripPar.parameters().isApprox(boundPar.parameters())); @@ -162,8 +162,8 @@ BOOST_AUTO_TEST_CASE(ConvertTrackParametersToEdm4hepWithOutPerigeeNoCov) { BoundVector par; par << 1_mm, 5_mm, M_PI / 4., M_PI_2, -1 / 1_GeV, 5_ns; - SingleBoundTrackParameters boundPar{refSurface, par, - std::nullopt}; + GenericBoundTrackParameters boundPar{refSurface, par, + std::nullopt}; double Bz = 2_T; @@ -180,7 +180,7 @@ BOOST_AUTO_TEST_CASE(ConvertTrackParametersToEdm4hepWithOutPerigeeNoCov) { CHECK_CLOSE_ABS(converted.values[2], par[2], 1e-6); // convert back for roundtrip test - SingleBoundTrackParameters roundtripPar = + GenericBoundTrackParameters roundtripPar = EDM4hepUtil::detail::convertTrackParametersFromEdm4hep(Bz, converted); BOOST_CHECK_EQUAL(roundtripPar.parameters().template head<2>(), diff --git a/docs/core/eventdata.md b/docs/core/eventdata.md index ba786e22199..c3fcfdfa531 100644 --- a/docs/core/eventdata.md +++ b/docs/core/eventdata.md @@ -87,8 +87,8 @@ which either is a `ChargedPolicy` class for charged track parameter representati ```cpp namespace Acts { typedef SingleTrackParameters TrackParameters; - typedef SingleCurvilinearTrackParameters CurvilinearTrackParameters; - typedef SingleBoundTrackParameters BoundTrackParameters; + typedef GenericCurvilinearTrackParameters CurvilinearTrackParameters; + typedef GenericBoundTrackParameters BoundTrackParameters; } // end of namespace Acts ``` @@ -97,9 +97,9 @@ Or, respectively, a `NeutralPolicy` object ```cpp namespace Acts { typedef SingleTrackParameters NeutralParameters; - typedef SingleCurvilinearTrackParameters + typedef GenericCurvilinearTrackParameters NeutralCurvilinearTrackParameters; - typedef SingleBoundTrackParameters NeutralBoundTrackParameters; + typedef GenericBoundTrackParameters NeutralBoundTrackParameters; } // end of namespace Acts ``` From 96ead7c75094c874cb103d51fc38e3cb951bc846 Mon Sep 17 00:00:00 2001 From: Andreas Stefl Date: Thu, 27 Jul 2023 19:29:41 +0200 Subject: [PATCH 02/21] fix: Align `globalToLocal` and `intersection` in `LineSurface` (#2287) This is supposed to be a proper fix after the quick fix in https://github.com/acts-project/acts/pull/2239 In `globalToLocal` we did not respect the direction which is important for the line surface. I tried to make the code a little more clear and move away from constructor initialization in the mathy part. I also added a unit test to check the intersection against the propagation. I will let the CI judge how much this breaks --- .../performance_amvf_orthogonal_hist.root | Bin 29093 -> 29106 bytes .../performance_amvf_seeded_hist.root | Bin 29106 -> 29068 bytes ...performance_amvf_truth_estimated_hist.root | Bin 29044 -> 29057 bytes .../performance_amvf_truth_smeared_hist.root | Bin 28585 -> 28616 bytes Core/include/Acts/Surfaces/LineSurface.hpp | 4 +- Core/src/Surfaces/LineSurface.cpp | 230 +++++++++--------- Examples/Python/tests/root_file_hashes.txt | 24 +- .../Core/Surfaces/LineSurfaceTests.cpp | 81 +++++- 8 files changed, 202 insertions(+), 137 deletions(-) diff --git a/CI/physmon/reference/performance_amvf_orthogonal_hist.root b/CI/physmon/reference/performance_amvf_orthogonal_hist.root index f95bea9c288fc82a4a754980fe4b02f8c1d7cc5b..288244ac76020e14a2e69ca347834ebcfe59d037 100644 GIT binary patch delta 13641 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 E0o;!2@&Et; delta 13796 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 diff --git a/CI/physmon/reference/performance_amvf_seeded_hist.root b/CI/physmon/reference/performance_amvf_seeded_hist.root index 6d4d6137d15371833b868d5c402d2d67e90eaddb..f4d36ce451c3fc41851fc0aeb6610e8bb40dd6df 100644 GIT binary patch delta 13481 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 O;`Yor(vv3+^uGWCO8cV# delta 13935 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 L=N9b4n*{oQ>wQXU delta 13394 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 diff --git a/CI/physmon/reference/performance_amvf_truth_smeared_hist.root b/CI/physmon/reference/performance_amvf_truth_smeared_hist.root index f300279202b13c8451a5034d5e349d5c7bc59b48..006c91aa521390c4bc6e9df67dc96d0814ebf107 100644 GIT binary patch delta 12800 zcmb_@1yoeq+x`rVAT8Y;0@5Pg4blzL4HD9DknS2v5TrY$lo~-w8bLZlDN#X6a^OGA zz1JJR`(NL0ed}Yb!#Z+g9lim9K-M6T+aUZ< zg`Wq)4^;T!g#!O)0|H^xfp)_ou8 z7^DmW!_VP;fj|&9yL&0MoC@&Eh_68CbMUKhbH9BJ0@-=p^oXYOXOAGHn;ZZ91FPM1 zjof>4KV;612E|Afg9IW6eu-}vqC7i2h60YNT+PLr4wJ@(m>`qu4q!7YUea}Y<)?5= zP9cp@lir3z?OTk6>`ywp5=9T+cyy~|nfwiQ6do(pSJ0XQO~{1BquB0lJZWSQA523V z1Olrd4E7)feD~&AkSriUNMvAQNG4Llzgnf$_+QyuTv8{6+tWnY8${USzOjc~4H-kG z1d~C&A~*f3J#vTP|NT>HfgVEPR%vdmqRJzzGT&H5fx#!;8bwEuM;6t?#>!0x1fS_2 zkYr0bh!JX%#Ui?-x^x*;ABzhpMx;MuAY>bvu$$;PT1){59SuziP45vcGMH~^Tv0hB zWjHyqm&OLC5HCdTn%3iexpoL02sHXi=ADg)l$90S%>U<^$%x^TjfEJPB4S`HHv{9l z1F=JUfQ9$xbx4C982}&MM=(ec732$UPJ^nLNZ}1YSN!}Y)U0{ug8%wS1@;!Jknw_zG zFL?jt;P~2z{R;R(v&SgZND*X9PFXWZgW>xW!|jgo{nO13vcNXdp(zcIWwQt3O{5N= zeu~B_hkhCo8d5G9hH5Y8S8KPN&lC`UaH*6izmlCP$g7@c1o;V1zBTUA#G3Gp*iJJc zP`<_!W4irbRr#oz<>(p{T46R4W0oQBP@(-q>y;O?NdO06GRlT4P`g)?zC2x(Z7!VY zTko2_y|*yXleb;R_Ve~xodp34NjNN! z;f&Py7DI;}_!q$ZLNc0{#}hAm!y70s{sER$sFSuP(EwB}`+V?yG!=U^ihH@KQOSGd zD&bHlt5#)P#k=IfQtyYxi`$KgelEmw~<{U7h7xMF&2IL^j^2)ByW#n zUqBkb51?KeKg;u65k6V=L5;zR#7 zI+TLjL6mS9rCyKd5m>2_+Vx9lxWxByuKG+RY6X^w{o(YZq^_$p(ZyT|o)*|zgo{-W zRq|xnTY9t1^xE}(@W=OExA_NP zYUJiS-YV_BADxPRy1OcN@F8c8xpY+3ZMl$1KbUO8c#MbXsnh>SWOYf);)*Ml^ZbR$ zn(>-pWV~Qpf8SK>k04>`WtCC}Q6(ymX#aRo4KwC}cS`PwOBiXw=!ymT{@Lg1`Ti}n zKp^eVnf{Ohn@vT2V0ETK#^45L0=mOSY+ayWxrwQEhB-S&(E1&l?=>$MipSVrXz=cW zDqhxJ8h+lu-(dgDA+Te%;bC;fUvgx)C4$MuBfdI)8PspLQyI6;shNwF=EtjI#=>Gk z?vdW1cuQC{k69z7&2*4wC%H{bc2DI<9Ef;O?0t6q)y>5+6%G_6_CI+7gy0VW_|@Nu z?Ke0W-N51e4>+W0IB81}^=Uv#!=0TQuuw?~qU}kTFha{qT8-FE4e#of7wCP05^?Y- zIjFjbz(6JEw~<^;&he>hOihfgib~~=t4(` z-iPYo7=j}w%Ot2AXhj~@)y~jxOTzAIRiM6@@&&Pt0<)}TDgJ~w!Ji!!PUOILs#vl(D)<)zFQxIwPp;Xl1mfEkg9?3mI2)6 zpxwoDpX=F3i`C$>n8X*?;_KtLbFOO&s859EFS1YdQ#-kL@gxGzD~kADPf0LjE1KO2 zY8$5YX8}hhPZqsT+1-6zCJl8x%@CWJyqBk0TraC`FtNe5!)KjGc{U1_r1FdLe@^7F z5FuHsy-K^)cPVVwp|+x=O7afSN=}xu3$AbpOiP~Tu-+&-)5vWg230`$P2I>SP(Nq$ z)k2vJTY2o;<^|h8(E%Dk8P+i37e0+gMdJ55b@R=6Tw4d@j#f#9;EkNl`|{m$#|0Rk znL8N}fps-7YYh%akFh~}$&OpRekfrb<2YLtbQ6!}BvEqo>=>cNLqEt2% z0a|-J6s43?RAPF~@N!yodREP8t*Q_}@>{jX99F7)H*<-1L;W2)A6seB+jIBPFGfa0 z+CAF?qVS|K=uvjwNQKyz7ew8~_&Dua>&ywVp~65Rj+YLmLQxE2;8vyHDC8nFmJ8tz zcW|yUK$d={W9u?pE31X#2nHO*U8ME~kF|ZP=_dKk-2q!-9&xr#Q2ldgJHPjxJ6gN? zG9gbEn>LK6sHUbn&dezm#By*ZQ^~3*u2vqOtdhUCfLgBg@zsUnlATcGjFFUK?Fm;y z6VeZ;F!$&!tRF7qdq{>bV@ztMpbiJrmgl#ZIPjeRwdhP@%$`l;US#O}(DQMMJs!?!jWJMyA24 zYZmwSZ{=rKHrz;wc`Y_ghQmgYQp?oWm5M@eqtWy9Z?^3@z;Gz^;eiPiYWYg zlE3bFZt5lO2EBBp^q{M}x1xpJWDS1yYZP0Oh+8{Z^cwu4)% zY5Y>104u^{3fF5R8PQ#I_n@u8l&%SfV3`~WXeV^L|CM&)a1*)dnkkAT#kQ}G{ndg{ z0PhcPy6=1*(zpw0KG4}_h~(&eW#T~txfBd_Mmu&jj}mk z_smjT*kuJyvSl7v5t0!0W~s%ufp|urTT~vBTZs*RvqF2uFOEUpx&|%XRZCg9x?0uo z14ezieumn@UvUA1PyZ6>itzvPDI(G}xLFU-kU;VP;w4AK2b6spgfGJHmrobuE3#5G zMW^8u>bz@}tJZeHS8+jRu4$34Rx>VA%8qQkUv(|@MvA1wN{HqhH|;JC7ulUmuG&v0 zD|@dA3PM$&xGP(Ep1bW5E1jYCNDfl$Veuf?_LKt0ZR^4WbQLGhhazD*jw^Bt;<0H! z|7vskhYbHS(%KBvIZ3@$WU(;X%5=vKH45cMI!f?)@EP@6E4MD|Ix6nx(DTD76`@!s zCtmE2R|B~1d=h5Qmq#HxR0c@Z=Y|29iRHVtPgJf~F=4gK*k?f}S3d8$2?cRnq+yGz z#fJj!Dyd_Or`*&RCOerGJ|5>VUpp_rM%>TzF%MGbb`a)cf|M)knxhwDCr1-+KNLOZ zQ$Tyiw+`}4^in8cdYW0TFc@e319`xTX3%DnaGepRwDWaZd!64er-Kl~+#}Op3cfVR zanqH|UCMd9c53?uX4_Jg;39v~l<<{0S$XzId&kkat<5S>k8ZJ_^8){6vt64Rz%SVM zDmVGEtM7`_P39B*A?3V4i#qA&sD9=rkX0eVLkf|8m(B4nvlSF|ckBB4ll4zj-F7KB zMrIXM>on__OKDPCCJb(KqhNV_b;?eGWeW0C8x+0Osfx~XJ~Z7D_!333zq0EFxS+$5o)`+Kh{D& z3CVw@*zekP#La3-3CKq0^p(U_`TC4|re@qSy+m6X7aRB7TkiBx&?y{F)l;%zETSJM z8NPBZk>j1TJX`AtYVelVs&FTn|7oE#@4MLYvc;ye17ClN{Uxj6#kXBj*+O65Jky?s zf?v~R=uM$PNnPDO<}YOwZk2Jv7JN74-Cc5yVg{$TG`_Y1UBI^CIAmtARmXa4eBrfM zjNWp!>lgIg37|QL4*EIonrj*8Spae-c7Nh7pJ%^f`Z9yw7$@e|3T-v(nk76=NJ8dY zO%*~VQOPIC#l0iUoU&|f`~|wYH2IdgR&1y}ru7J$;5)NMD)M@TSTOgo-vPbfQ%c4< z($$G%+{tkW129sjxmt{K#d&TOifN_FTCuM3>3fQ#Ek2BWSn1b6v*++37 z$qj`jj)@IEU+|F4+|f_wZtZ4)S>sgm)-g|D$vy7E9JA2OynTdi=qim)eA`Q@`yJ(d z*Ea#{RJ_j*?L#jgz$n%+{5}j6zMWRCnO@6_2q4?t6aI5RYe(Zikv@WH1 zWF2k5U)-G=OtUfD)L=2@S>2zjgl_%r(n8VtS^s|K~{O zjz@(LHkfdknG5n8CJfAGD|$I5mOxpt=n;K(S z5;%Ns`6G28uOLPFdli)JHA9nNKVY4@O3N34x)fJ=Z+C+kE?;SL^=93LbnHIwNIwl~?5nH@lo(n3=|ZnW3@+JjOEg9k z)oe}8!rPySh%|^$HFb<+YmkIsn9Vzr<_}$OEi_dVua&xZ?TW2l_=;mMcu{xUWT$&# zC(8l8p`o)UR-%QkDf`ffiw8?JdaQSNFb6aSYzLthnI>wkQR(*JC zSG%@T{zS)o$KnqThf10n$zeB%M<0i}%ice{7@4nvuI#nRJ+FHP2DPqq2CaRwiI|>? zVSa;bel*=sm}H{adI)fCL+5qNDPNzgs;P6Goz;6m z)+xIU2vCY%knN4X;z#OKgraQAN+l_Cz6kVm3}2~PSufF%H7!Uh6kdy$e(s>Cz^uV3 zMvVLU?E}ol!&i@Z>qgGX-bo!``z3ZLnlUpSB*rHf#V2WNJk*HsS@(CY)|#cCI@a%> zf9XDk>;WjKawn_Am1ati=1?cID3mgI1PvYaJAIzn`n=SD49RWlJE4E+xU|gYI{Qwj zpOb?%$I?G&;gz!ZMo0R~K8m>h`n?Ntwbi)zL+a$$ZC~c+3=Tu4dHqxO8wN#q<*CMk zvWy0?7%$Yuw4*M*mw7``nu}@9c^4Q{gDqd6<$ecdC(3$)Z~1XSE|GfzE$HL1jON?` zP;g{MBU~B#{!h}7S^b3-R4X@cL&TcX^#_Hd{1$)?^g_p!jH%Koy<`V_5_({iknZ&K|ueQ}B!==X&JZ z$O>1e0JcUr3K23X&(gTreRDD@@ufS~NC->S0SHXc4cl zd3hG4+DkNdX-zNfYovTgq?_ra(Bnf=a!0dmszB&?1=moSDh;T5$-=Vw)cXRsL#$O@ zNhhhSaZE#19VDPj)w%NYyzWEzk18W43OyR%A8yEBT3$4qu2(WxtQi-mUM#Z*js=G+ z)V3!U?fabsN^RY4{=tngaf{fL*RcL*yz7>~iT_aaXDpcabz9M@$WENjPl1K{F0Kus z>LvXOr{jyNTD_It$=a3IX_-xvKX@C9cREH{~6!TeD){KXz?nlYEM~H-O&j zIeCUtJ8pi@lXHYG$&dZP#hV8?wzkQB(_^9?mejr@P1|Dg(5kl~A~;*Ety#t@Be$(f zMQz{PaAE0-P3Dfg8djzCRJy-cpNLr&J~g;cmML5VzW=2@@zy{DJ{21O^JKcytRL^E zP$d*6AuMo*bN$odawCu_xdHm`$h0hAhkO{uLhyih?wc=hh2M!u)m$G3A78xbB~9i> zj)#EJ7>T33W09v%K+$IECO6_cLdw->fE>!R0rY?89S}%?;}?`}Xa|9$qhGW$s*&pK<+@hc;77h z71EPN#gf{{_{5y#_up`-HoS_>=%k|Qz`OKir|X$Lt$^6iWJ1&v#0 zTT3yU-7=lfRqhD69s|BykZWha1!M0NQpHLs-J9Gwui3UzNP&az;k64X6cF}ey}UJE zBO~Z`zuD#XlSb(9PNt(TlOZgv+G=<{i5n0PpbNnHcJbqQ(fNtZz2dd_!YjHrIM!ZO z{O_CHYI6$gs#yFcC|BcZSnnQnd1k{UGJ+r%k160n0b*Uu8wIOcsGyi%(=8o64x(D_LW zBKp-i=VP`F9!;_tw7V82N$!t+b`h(8&9$50%vE2HKlDC>3m+BkCDwP3RP1tx`1%L0Mm&J0N@0n5`* zGPo;L5;!I^jX%wd(z{C-I&i=by7Oh2rzEcD8$-D=YhUK@v7%zfcpri%UAy#;^6#<7 z0|GpB{>t}Vgu%=%w!3++Y1j4ioYQAa*QZ%u_hn&BPU zVQlQF!yaz0R&g8o&g!YV$z@}(<-94%YexkdGpJ?>FG{6xC=4(wU6+wYdlY3~(e6gD zb+_yXm8<6F{I)c$+2(_pb!$@`afKQi!2x$#P8@@>aM$|B^3MDDl<)fF*q9Cj_%t}u;`{_& zJUbiR6FSUUWk6BKqdj$*Lz(Ud253}3^)>tNoW1cVTh>lkMhO-_{5U*dFqUe$Pt4t| z7#|`UTK)D;)Eo9hA_XiDqB^;UsHZE-@%9(pPwE?)KfK@f=(1Jk5v-F1HwjzJ+$|>B zYiFk2(yMW%nrW(VG`e<@I+@u#J#CzXV!=!vZ?4`tXrd_({G63h9=M;b4MfG1MU+~# zQJ4ig-5(YC$_7D8s@1N*D39oqvBeBrc}*WiL&D>RM*S`WyHYY@2OYBoC=(=A?Ttz;p!pr{ZxT;-Bx?cw z2Z5MjXq2<(AYV(5bh!Vj{_4LQ_o<`({oO;ToPodG_nXSOy5#3ktjQyXhZRSgr7VjQ z^U2H7qCyV9wp5l0$HO19cejUd#mF|)N1vkiE_|xj@9poA@0J#!Fn0I0<9;3krr3Iv z=i~j;_GjE9^KNDXE)%3k{Le|FXeMP%+IMZc-*VmMsM4ywuy8-m)qQ>^iSzWKpqeC^ z`_FfZ`1j3L7MC)#o~sn#MhAT%`!dpJ6&Oq_#B<`O4QT78ZK{&L(Mh%*myaZP5`fg= zHM{+O?OSE7;}^iC+^1%UJCJpCar-1P9dH^fgURD+JT)R%-h|QIU1DPAj@$BD^)wt-9cmR>kn8bZ!!PV zTN(|2^%kT;qZO$w2x9qA0iZjSgl7}rotr!Ux|jQK_Rl-uF{1&3Tibt+8T(SkIb?4V z3|mpN#uR7gH0!rRJ<)a9*lVBF~DBT6N0-;+YVkavKkB#t{WKCY7q!ok!l$vjz$TO1=VAg-9Cm_4W zG!}=qc7SZhGd33&aF0j+4Yp-RMwH-F2-#4voef8=ZV?`EYYXO9HL`h;jPT7cSR>vhjo=4CJjGflP(z3*^H!Fy=wi}6GFCX~qp$&c2cv^vco4<^i?w@EMeusBLyWT%-9h1(G- z+eJKkuy$VYI6&hS5Wut~fxk;tUaQ|pztjM7ak#b(JEhp@Id)phW-(z}OXVI}4?kop zfs45;{zk`XCL#TepUTAGpGBGcSy?E5MuZXPr?EWv!ofLLN3^_!6(GuldBG{!+2v?c zc21Rd`n|GU$t!B-EniLtSY<9@3+`-H1Ux!!7E&4z+s)go)&0xc7_M4yEnCma_eRnr zhrH8j!M#k_{VMB7d0Vch9fu!_3bL$2qlqps^;EuAMg#d?z$1QD>i#HUeXw4Is5w(W z>hx~`WB)|8{^GCayvc-&5Ulm$cT|5*9pwnWiC_85R&2uz;D2*?0TG+)#ypCtJZI`e zXIbx)`zW-m!m@kS4bO1Dsa_#D-*vt_-;kWJz7#N4m8m*EC#-$qlee`r_WGnt<}r|s z&m4XOY5F99(&EjNQeyqj?r=yG#pV!fp<=k7r8HvLeUlT*%A$LOif=ZItoE^(S<6$& zp|6>J9ocWsTS4vZ7nkA+R?X#8kJol>SQ=u^O*NMN z%rWt5@#)VQTj`^B)vv>Xsb5{$Ap>X4G~1h8PKmO!IV&W@Kl6@SP3|ffn^}En`&|1? zswZu2D)ytaBqn9Bb>fe$Tx5~c&j)4Sj*w=6ooklBP5+eD8CcM&Qmxn6JIfk<)??cB zMl+a{P33#$v@R0cmWU{u9tJYSsP|dCd8g=~dtgRuyRRP)5W0$CbTX~U>%9VknM{t; zDj^z$TYKp&J;EOvha8;3N_8}BYox4(!ab%J^6|%+`Kzz;cj%nL&~#25IyGRy#{s$n zIzAkWX;emT`pf5|$zWH8!e$0!lK?#oaX3?*pbDUZm__2#+? zwWdJ54)Hd^ASMvq8WHg@q@TM_GUmDR?6s%(CPpFeL8wW*K}e^)=Zi*BJcU^|u6UED zo&$p$@(anR+u852HYuFhV5P{;(WTs?05sO}8 z5*fZ0_GEr5Fx%|m;PY51O1?PmsK%XH?)@ctc>;Qc$h(U0A1lh60{!^!7$m^m#($0B>Rj=iCu;7~od7?6T^7k47grE*_#z6Zsw;*w)<%PYXBZ|1` zpLuOYO&es!hd+7D>nJSu#&@z>I&3f{F41Hwv*GI?fb<`kvGMfZ@q!?L>)-LxoYv_? zqzT{1_ogCixfiP@87C*pA1m93t7`2UPow$JazfWKcA0egREklGjA4k@o(^n?bcL*B zwFP{9#Ke{v5}^jyb$ir%FE;b4h6%cX{?>a?pRvvkk;7dZ)JgX=)DqGLP|jqDgsBZ} zVy&9QD_*o_t2AXRy5fstR)^EZUG_F@?ss@IPr$YhC+##A>7w7uKM-(IaBvox0h>z1 z#jjlT;ig+He{M(Xf(DSOV+JG~A!P+}5N;W*nq4a@9?zH{Epb-1KpzxF6*&3=#{j26 z;FI#?z}w?cZo~GEO8`fCD@@6V=J1E|`<5-DrXt=U%qT3txIi|mgchFG%uzeRZFt&KSZTnNuoQq+YT`Az-1wmKe_V1KFh_;SMbPlho=f?Svm z05@7n!PHNY7IS=`U_;y1H(3p*j~`Ayjp3oS!M;a6BE?KeN{T{e@boD*%E)60h6<28 zmkc=pkR}{04I+kx4Js9>gCjv8q5<`Ha3bUWry27mauurcn$GVIQI!Z7-nIxz5$u%kyC3|axJ_un+kDgb%5N0n5i(XO zUe+XDe>_7&!tV;R4YCiiYwBu&4R=2k#b4WV)-hd`~`NtVSy9t3Ex zwV{+Zvo!~aflH3O8;u?~kQ8f9@|I}zNATPKJ?ta9sqMvuSH03fh;7n|U1>nVm|<76 zT~J+Dg=B`L(IVrT@7j=LYf9-1GS%eMN2Ouhvb`3_vO{Y}X=usOG76{$J#(S#mfB%AS zoyb4YnfpVE$sX}%g{j`ZcMpTN9x3b=M%G%eycRg#GLoNkm97-r&lu~?s!yv-RYN3v zm$e{$lIYpj`q>zISUA6kju~ySwN4%y-N(lh1kUUr?h&n9pquJv__U!XA_k%iLWR$p zofpK(PUIiOt&~`QFM6gYR6)%5-)oX@+mjLgd+jh8JSqIQI@({W=O1N}@Mix_xg2~7 z|C_=XcyGVgS^$VAL}=0fS!@As_FrtQ!(08!{^_qLKooAk3%LHhkQx5`f2(AMclM7> zxPRG$gE#u06?1r_|G8ug1lP#^XIT|K_Wwc@g}3^f{C4xQ{ipf{Z}nG^M;kHq|Eco8 zoBa>!g0r*E&7}T|q5vsyBozKHcBkNX|4Zfu`R+)e|7(LnZsbo#!ZSIY8=L=4dH`?r zFF_&1+=+tcSKnqgKOp$s`#0JLqVIoMu_0AX6o_mUF;{Rlp8RVsmSKPE#RxGg|IT_; zg#S)wjELU<;F~~aGQz*4?tf(NkaA~gL_0(#k4U6{`}}(j4WZMegHX6&A=><&MmO@f zpd%qJAbx7$rV6JJ0UkIm&?w2Cgk zbB^yhzW)DruJ8Kiy7sWyd(B$U-p{?BynA8bLKs-w)yc^n1lnr_fk2iZkW(T2;DDb$ zgCEH7!yO6!^%w*~Ee3(^ScAadR8o$asc<)Aa1mtQ<3VuosbbV`W0ZlehgrF-;@paH zwN-@wxjqGfP`+GG0YBhVUw^E%uV=_4`p*m?gg@^5^$mV_Jr!c-_3t4^*3?J__faV! z^56;rQE+^T#L=){n$ic^_@dXJ8U<0XAgBm54jVf?H~|X<7UD38-dwDU=Rqw z8ZL|_l9m#W9ER&-3nw+t$F9;A79hAJ6p$E%a^QE8+>ZeFci3>FBsrUz;aHj>-y+1} z?zo7^K_Kb6&7^cr`@a=3?~8UQTu9v;A$xCxWZ=4%60!EDlvsaBsrlH#+~M)Hpwti; zV%6_oSq>77)&Eu+igP_qxHL4^(jY6`NW*k33^MjlVUVwd(Ylsl2U2WK3sFLjBH2Tc zQkBW4>`D;~=%loGxD>F0h;Jc9kP7KnV8AbU7(fdGWxkWGxdr<3$sj_vDDV+(7E!uc zg!y`ry95yHTOt_P|G5qswk8GOE5X1ZC1lW3@Q1h-6+;bzo)#trS&@Pjb>bMhVi6A< zO1qTNdChfo7w)(KG{PbosT8^U90%1Wb9(34NSW?;UQa!H8E6Q3w zk>nhEH>s}+BKatiCGJ9br4^xE!iY+xzHxZCxD65+=SRQCD6563eX#8k&XaYEyf|GRKX|D<|0+r?0gBRy_d9EOdLciGOiHBJTp0 z?w3REK+TJoH0x(~xdUu}oOnU$#9^D?mXH4d5$yPKTf-&jh4|#ax0>)(GQa#W$$p;+ zE@ivpy>k(HMvO1T_4$L;>f$-i-cj&yW+wJcss<{=qq(9zAeqZqtJ;0Re7RoFot>Jf zBBM|5-)yMo3?i(0BDT{~h2FA(rh@g>T=@eo5q5A<1^8WRc!?G;uEydU?<%Er!dm}~ zoqeq_cK1ijF`L5iNz`t!v$siYE$cvGOEg{@PrES6FvTnX47|KZ?m`OY&kO0>ksaf8 zExhwB>VW!*p6wNa&`Qh*0Hb_o7$5j8rMN}X0BfxoYZKx$6O(A_9JhC4g5c1*!+e7Z zshi!3T3p$Qs&xnW4PJhN@|L@+hr5mbH7ZB`!pi6;6)g>tTie!F$#c9ZVFnCxMlyj$ z`W)z*ON0bS_ymGhm2X%SFc=)VlNlCmR0lad#xFwSB@7plr7+ntfC28p9$w| zoa=t=|C&z8MpCOo^fs`u$+Ido&|$-WLqmS30+J?#oa2AWXj~&GvFxixTzYtZyxmQ? zaO#j#9J&jeDa%{VaPOLl>BsJ%bVL!@g>>@Xa0^Gbs|Mo;e|J zEL_rMNHA?CU?qJmeD*cn2*RQ|JJqtwsn1dW81;162lpF$#i7Pj!pTLNq0X0@a95Sf z{F15-ku<#@W`kU3@C;9~i2G=Oq{tE?h z4Qg5%INg$-#xo9xx6tpi2n3Pja7$#NNk2l3(j&#EC6j7;*~|7O-q>s^*m;%xe&9b{>;pB~hfHEqwQ! zQ#aXA3F9kv^7p2;wSvs74l!6NYwJ0iOfNRGBos}UN@M*NL9B~<>-eOMPMLO(5u?#QblONUWhL>F zAYG_gd{8lPO1>6uxl&ZlxxewP;%sJrQ~dP&O5m)h>f@?DCwq7RHo(Q;^EQs+b7Pmw)!o9o zJ5KIHCly00RxPi1dXCP(GfeR@aJ~QY10McQcmcWzJA~EXBmM{n|8OHW!Oh!04aldM zA-14LI6W4gwtI+fHLhrSdb9Fl z4f>)6Gd+l0V39Fu`OeafyF4!u@d-CtHp0)Xi}mFq1UPY59hx^Mw|ky!{dMHCn>*=` zq7=I=KcOl9o;PpUC_z>)5I3#pa|um_5Qx^lgkW3f4(L^$si90?2`u+)MP5NyempN~ z5r{553*j6@tQ=uZPj4!VJTx`skZ=+IlELffRFyS?s~&}FN@#ezk-;W%?7^d(d8Ms) zA)qQ+{}{N3nCQizhCY_JuIb5!-;8M^C7hR9W~8cc{Ej6ugwblc%CfR;DvjzX$<|W{q95m6~8Ii z<{)?zzTrh~bo#e=Rh0UtPH*B>y=$LH&~M*Bg;Jol2P9vwn}$i z%alHJ@VzeIy!k!(2?nPge7_VW9GwmWe8Z`kXbL>i#&pV@zhGsdR zJh|He&{Z679&D~3S?7C&=Nuej66zmlpG9%DEYA~*pFDDSRaWr!v_zgH_GyIHE9xiT~HV@%7NQ{9zBpw~q+GGm3{fY0Mqqcl{@C9{%8?+}NB zZQtlNl*g<~wu($v7qeE1lX3WiwDXkgKCZ`XtCQR!)|dOO%r->E8PEO6AjoC$0fv@J z)ph<2;m@L+-iqSdD6|S(IG(1V@6qldI~Pf(sgF86bRs!UAuJ<&K1Wj<6Y#@w4}afc z6Bu2QnItsg`R0lsp;czp8jNc6K4A4icLf$c2je$O67icP4TkvwOZiP4VO1+}J0W>~ z{B7qRyc;b=W7(F|QMS}kpViLJ@DB8Mg@omHXIj7$pWZjX(Zl;Idc^hriJqId56=fsfZqdHPe@NR#uO}{rI`k*2*QXUV( zQM`(vlQ%6qGB>*W5FMk73CW==>EsGCkv=f8k{kByirmx6TBnOVzDoHHJt?6ugYgm% z2S@CV-NSJd!LClydqA#owPp7ibbJ}?m zTxW9i{tcvvvQ8~lLzFDk)RzagpPIW~KX@!9$0XSdtKHqxhbg4l*NG`;@ z6SQ{C7jKEE61_2{!x$)qO!jKBSKA6BFjL4CNi{O15{SoK^H&Q*v%oy~Mol}`z6iHg#e>|a!FqUoct>VRsCXy*D@A5gb@bN=x zhcYUKZ6A!=e$I1(o&*s#D(BKjrX4_ zw23(xytv!a`YcsKM)#CE={z)l^@ZgwHM4g~wtryyIZO!f)W|p~{(^Z4;+o5Ag~NvG zSJ+5~{}VPhVa68tPoopZV^1JW64EkLS7DTin?gVos>IE@NI=M7C-LHCq;s?!BUOc~ zLIkakNnEGs`~!a}?#ma57c;=I#>P84kI~rLSTH$bb%#(e|OsPp^|*EW`#dX z%}cp{?1W{a^0;x;kF+t*w;pw`d}$cUWmB}LPi*mfx}dvbQ4|r@+u=UwW3Sxqzol&n z(5@!~?ypQvj4!gUeuT^wU+gZNHDCFTN;-Wxbt0d(1wPNJ6#@*ivzPIPKP2C8m>P;! zBO=2(BltKB?U>)h9OGo~IJW9v9!)YPU(uz8D|Nf(0B=1|I`oretiz&R{poRKnPZ<>MFW9;Qjg4 z+pNiZ9tEa385;}2KAu~H!EqheT<}wq;0R&3LCD|Y zP-%Ge?axr-Iu5<@N{4^Np>b{Se3hVE(wc(QYBWv;my>}(nT`q1Ag-~p%mE(_$W>CZ z)VE~d!yF+%rPq7Ts+v-ugLr}HXvmLLj48-5U5jhCoagAVDGu{~fc-*X_W4-@%xf@p zZ?h?inFVz>&!a$MD+$Y^kf=*TS@HGKin`BOQ4A^6AFiC3zM5kB zO*ad$qePt>J>99Txp*cLG#8QOhG)iw?xQ8LyP7V_yZ_{XNzFs3oSi ziw7K|Zn)9jJjRZs#G9l)bo7YG^Aa5vD#XlAKdLM!_aCz(R0x>)c=UX!vX8gw+84PP z|2kk#rkfX`_VIhgYVKZl7smpCSaCSv?lJSU=5gaF_n;>1CEk4Fz_DBiankbgv)4Xx z-t=SsT!eNlf|JZE*s-A>B}QK3&ToZIDZxs4_fn~b>?Zb(;%!$2rEH$g{K!AeVLV4T z?MrNfm)o{~wIs5ie&Ph#!pT$r+5rKmZGV}P7-q)%6WoNIzbLW`SUd#Qz397F=@0SN zUDzGX9G?g9GaRo+@;%pO)k#nu33Eey3Ua@2AuP;nGJO@3e*SHH>d~xcxrDVQXbIcK zGs~p{NYZg#SD-iRdJtKHRce<%JuXVQ(mbuIME!LSrXYxIbmfG2Qf)YPO!XyxrKhmV z^t2^olZneePtjhsBfc4!HDlY_rA>QDg(Mc(lQ=-sa=E00d}VYQ4`ISvn{jMu`0Bo# zmRr#TGlsPkUp8Ne`f#=Nnnqs>Jyxl)^R z%vUCNP?sZ}Gig%kAC|vO&GNCm$`xrU7M?(mO-Ii zk|6fr`$kGi1wl%d(nM|!w0O4^XmUodHAjQ>?i|y46xR~TR{9-!3@A}OQ!6qFyEbQi^yHka52@DNKptaq;1V{GB6x*Z{zo1yMl#WrWl6;Gv&VNl&(G< zU2gUTDHn!^{)|uVRUBo<>Fyp-TYH7%WM1T)IyVSRk$65v-OoCvZI!olFX9^scH(?D zTsaPD)L%TeU>OJ&hxA6ca_}#yh~N7(N$assKJ-od^poZAWMN7#QAJv#tS3shKTVS* z_QhrAIXr28S%|;zkf(Vc2#8?F>#Hb2*;yuCP#|um$R?y|? zVn^5;b{|NrIWa6>wI1mvei^cc9xAKYW&pbV@gcke1+Kha1hM)*od;#l zpNk+ndpJ1U_{<KxmuWwMc z)rLrKCv&5%1cg#Rxn*`L;05_e@^m_AQnXZ4>`6%CXsOul-pge=0WA#t=)jCKsadA& zT;kE9uN8#Lfi?(YTbp6SE8~>_(8vK5)Zthh=d>G0!U>ROZWR|CQQmK2Hk&Or0Njrc zwX{9;yw`es8*-@|BuF*Ll^O%C7ayS{UaO?amKD zwOV-i2|~*+?=q-3J7xl!AV?FjozZLuvXf!8&a%P``^FfuYq2IVa2tlUo7mv0kL6FW0o88vYjM+&0z z5ZYqt?9tD$$x=*`U$l3T1$Ivx7?5T8LK}2V3f|`8Z@R%6LYFt26lK1lg9BlK@~t?} z&Mh!NIt8^!LqKW3DAD<41gpyoie#dxFuGf3oh4>>9x4m%fPbeOnoRtMJ|F#(c>@CJ z)}z(bm!GN{=Hvj+XNMPgkDl=G=$eD(4^e#uG{a6e(O86xo3iP(_>DiU0FEoqKGaE^ zSe zYZpLTzBqmb^(MHpjl}6Q=tGv6?mSGVjhqg)mwX)cwix8QT-h(^Kc6MTIKnt3F$C?} zQ%BN#r+|d>tfl4nd(4>0I;wY=<8U8qt!`>}KX2HAism>jeZFZWmFJ(&OJ zmb=MgJb&u0Tp7OQLat`KeR4E3XmjOp9P%jG^|hr6azaSUBNtp!sR+Pcx!1@B&2S$iI$ZgLm}E_5)PVWC}?*9B~iN7;R# z6WZu-2~#!)@UZ&1))}W3j3fdo_z}pIc9X*>5lZ!+=wN+knHYHk!lU+4w91yezm%uK zHg(kST15Wjy(Q?q0;|bXuX@%F5Fg-Y%nuc5OnCRs;L2Xw&)(zNrFL-(`Gry0g%h&( z^w9o3Nnyf@Kl5?B9l!1tcD{}C#dfv6@k>q><&a#gc?@vn_S0U6&6UA&%JbUuxJRN1 zWEmfiNTt{Ppf+c z1D~g*)$bs&fUkO*}}01aCT-q!|iU61;E>sB3Z zR{z?G@K{ps#(=i|6-q){R4Cw~WD69YmC~DAWX8736t8Q0mX&|*Pznr9eMu(3N z50p-}(QM=}uY^Vdw){?-Q~RIyOK28qSh$#k5`3LZeJr$KfYaW|2j1)5u4Ju7YaEd) ztfet5pA0d0yVU0C?n!N8q*omBI9H_CPMR6;ZAmk zA)F%Ub-`_5yB)}YLV26apsftTVS|`L3nJqDn$8Oc+`!#%&$Vn46 z%uWtM2DkvuFer*EiT>hvupH~M_MA18MdzKw1E{ekvzPLzDzpAZ{p4wE^XQZGQ76We zirw6cSdYoACE<*!=&_QFiZ&SfPR4goyqCk<~t~vuJZ1z1e{c;iDMa2aznxGQ=D`68Q4SGOOfZQ*6xkJ zUNGQ#7Jx<;9^{QDa#gt&RehUGM?CACz~F^K9Ae*l*Wu|#)g9{|=E`U-&HZ`k(_x^O+*y0qJ!iQ6yv(p~%z)_Df z;+9)98p^F((zkTX)WP7pGeb&u0(Sb%0z0WS@1szZbP?uryS$Mgp;pKyN5K#M9JEM@ zM03Y%jnbXw844rI#yH4IC`jf+L*1V*jvC>+q3kg?9w--21D*wgsc+=_DIOf257#T{ zwFtl%*Ax=LYk{>fAf%48l@{7Ke_Z6dzNn)JQFo-OOw~aE6T_beEPpf49~6lj9)V>$ z#KD1_5`OQG4|wud?CVxTc69N9fW2mT5ZV=j9Y$mT}SW+egkh*_AvNPg&lI2Fzgm9W%(wnLLLnrZ}Y3pLY z!zrYEWEYu%U==Cku;V#g6eDX_+7#6nj>P49(>X$%(h;Lk6pVn7IoE1ige^is^tU=* zO(fMPVuC3#nyE!HJnH*ax?=)VvrNeTE~zdxI@LLTbSb4{X|gT z)EH+QhE|%_Cl>=XnHP4Wv?rEJr6_(-=Yt-yU0ccFPw`IGa+9Q+)N>hD`FWR%d{aJ> zZ>}1iZEn4O!-oJE;HIIngm#1u&{O%lS>EJKS9F6;BSrt}r6@!59|`>+lWtBYWH{>u7K%i2US^@1zmwG;MVb*Uc-*YY22n(Lfc?BI&kq@b6^2(-@yvltB&={b3)G$cptt5(B!dNI@_hl; zxV0u2$QV}$#_y58vsc}wD==n%Pv8O_6nrF%SLQ+l-SWFz=>%=|W_ziqjwkmx4*5$Y zmEV}MlyI}F{Z6}lK|=&fGCFOVlVZ-?mPpaceZPBQW|K&@kzucW(Zgw;m^>MrEzjkf zh;ozyhBCymeRe6?k) zg%n@ex1ATs23v|p7^XOwIIp%4j*VV2rFrRpiFMDa>dm?Ec3Kz?)xF1map1oFqknzJ z9knAVe4eJ2v(u;7E;rJN)6&N6L;R-?GmK3{K+MJBBUfrtgWT$vW&feA{tld>ANdk8 zK9`^mdn6wwMV0-MDb`1N3Kd76(OHbqk6HK9o`x3o^j2C%Qf!St#vp-ayz!*gu2O52 zdE{emg{wd8yODRFeA>V8Ug$XLe!Ld%Fx(N?*xshNLoIG@hppeTL1VZ1u1#l!O)r zi%I*KHeI1zK(D}dEo%NPVz3WAY5vp~ev_hgCS2Zc!<*A#Uvp2y=D2nwQz?Fj7sI;0 z@B+x)0Olr)`3aag@E9I`gnyJ^)QxsN3RBHgW&VI|6Th2tl?bmZHc|Kq@xtr*{3L+ z+cfu-a-!+Jv4|W$Qb7O;-<}_z%!&!6%FxAlu^wX6wyjdH=T0r?k+e-ABBaxO+FIZU zr?I&&kY`^(u z8l>-`vSCki!`q$yKDIvlzKGq}ik8SpZR>c%?p{&8_agZ|>cTk`I?xcodf9fD?{e(* zV;rgM=>6qoS?^4LJ7<tNXDLIWF_%?P$avKfsRuM4x}E zz;L2|@Rpa@JJg|BU@JUXaQZ^XORBMHa5HgMQ20sotYY>!3u!TQv&heSS}*EC;Szz- zCFweyW&PE_7=MS5nDm%m*H|(~4@Gs94*RFYYYg9(+g^4B%cC%L2gLET{+$8H`xgVS z?zXMcxyBL=I#_QXn~phB1epW@6!1k2PNhTO`gIvoEQquzCD3pG@Y-H6nV`AQ&dU&U z1`>Q`(Ik{}`2HH*u3Vywh3OMro;imW0!PAe;8<`VXVuFRWI>{H>c(24IET)7<26-c z&LFx$;k^fBGRT61!F!^xfjT#;yMc=&@KP@r87yESCHX;2U5P1WFaFM$u@t8+CT&`& zqG|`sU1ux_I2{oPG{!e0lBcXWWRM=IA4yKH)FLDA6fyheWu!sne^B_;(!z1FcgRak zCV_iWiL`aRW<+T8q`WFNcFk}UsTS93LtG@*@X@66J}}D-698 zS)GurqKq3&X~`04pzD+kRm3_hmum{0QS+g?f_*6c+V3H|2;OzhkWk`SRLVF!W<6E> z>O!|%IHVb+3GY|Ry@qIaGZ(PoQH~UlGZ*G(1r1m(IZZ@X_IG>pKtD!ccyfC?Pwi(< zcISHmkwY8Oi(MoKiVKxT-SVf@EqoVcmPR-WO#@FMK7*W`cRlvh5+f!H$&+j$(Qx}??iis~wV2`?^Vr6rVl8!=;x@f!8pw=e9mdenA z-?HDvi6?KA_?caXQ$B3(7~p6)rWT=ze-9@u{Wk(1i9?(p{r^K?*pwRg@0KyQf+U&$ z|63+*T8$UJ%-w6CBlg`yq1@N;n?)2v%z+FtW6?mACJA!IyLB-~U)Bwn*^Y-I6(y+# zTCx6bcIQuiZaw=}|N~XTjBnmXXpAjB?^aG2uC8G8G zn)W(!o11Jvnu&sh1c_Adxim7;fU_ihAxMEomMmfmX8~k^CodYujFbCXCyauq^K+j~ z*nr2EB83=#nPkd4TsWG%chp|sI1Aa_79a#wIB1gGu&m+l7UlJgRLUS^_{+7{hJ;y* z{=$`|!1yP*nhyUr+-C^84JrQL>BfKK@KOJwD+`zG|3YvEZb;#OGMwR){m%_N_@Mu}Yz`mvKU31+Qpo@}==)p4 z@V`o|@KOJwL<%4E+mhU0@$+>dRUeM)Uu!=A2=w2@ANa7}lpcQWiktGD*7aonqVxbC z_5W&iQU2NOf)D#$E_d6WT=zO>ylGnecfSPwn*T-C@Villbr64ha-N@a8-M@L<6Y+( z*N^vGPu^!wev`%CNCY?E+kc{G(?aT2U7e$vi+ag z^mVci>2aXC8F!QXKU{}w|M~xaa%=XR`+ug@mFkWt2sam$ukExG2i%nK^T5w;alwzY i>ubVN@I&?bOyLIzq)2jort1ekKHU7G(2G9~^nU;(Pqan= diff --git a/Core/include/Acts/Surfaces/LineSurface.hpp b/Core/include/Acts/Surfaces/LineSurface.hpp index 243c25dd22e..261179a8323 100644 --- a/Core/include/Acts/Surfaces/LineSurface.hpp +++ b/Core/include/Acts/Surfaces/LineSurface.hpp @@ -90,12 +90,12 @@ class LineSurface : public Surface { /// @param other is the source surface dor copying LineSurface& operator=(const LineSurface& other); - /// Normal vector return + /// The normal vector is undefined if we do not know the momentum. /// /// @param gctx The current geometry context object, e.g. alignment /// @param lposition is the local position is ignored /// - /// @return a Vector3 by value + /// @return a zero vector Vector3 normal(const GeometryContext& gctx, const Vector2& lposition) const final; diff --git a/Core/src/Surfaces/LineSurface.cpp b/Core/src/Surfaces/LineSurface.cpp index 8af4f5c301f..0cab9c56b5c 100644 --- a/Core/src/Surfaces/LineSurface.cpp +++ b/Core/src/Surfaces/LineSurface.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include namespace Acts { @@ -64,13 +65,12 @@ Acts::LineSurface& Acts::LineSurface::operator=(const LineSurface& other) { Acts::Vector3 Acts::LineSurface::localToGlobal(const GeometryContext& gctx, const Vector2& lposition, const Vector3& direction) const { - const auto& sTransform = transform(gctx); - const auto& tMatrix = sTransform.matrix(); - Vector3 lineDirection(tMatrix(0, 2), tMatrix(1, 2), tMatrix(2, 2)); + Vector3 lineDirection = transform(gctx).rotation() * Vector3::UnitZ(); // get the vector perpendicular to the momentum direction and the straw axis - Vector3 radiusAxisGlobal(lineDirection.cross(direction)); - Vector3 locZinGlobal = sTransform * Vector3(0., 0., lposition[eBoundLoc1]); + Vector3 radiusAxisGlobal = lineDirection.cross(direction); + Vector3 locZinGlobal = + transform(gctx) * Vector3(0., 0., lposition[eBoundLoc1]); // add eBoundLoc0 * radiusAxis return Vector3(locZinGlobal + lposition[eBoundLoc0] * radiusAxisGlobal.normalized()); @@ -80,27 +80,26 @@ Acts::Result Acts::LineSurface::globalToLocal( const GeometryContext& gctx, const Vector3& position, const Vector3& direction, double tolerance) const { using VectorHelpers::perp; - const auto& sTransform = transform(gctx); - const auto& tMatrix = sTransform.matrix(); - Vector3 lineDirection(tMatrix(0, 2), tMatrix(1, 2), tMatrix(2, 2)); - // Bring the global position into the local frame - Vector3 loc3Dframe = sTransform.inverse() * position; - // construct localPosition with sign*perp(candidate) and z.() - Vector2 lposition(perp(loc3Dframe), loc3Dframe.z()); - Vector3 sCenter(tMatrix(0, 3), tMatrix(1, 3), tMatrix(2, 3)); - Vector3 decVec(position - sCenter); - // assign the right sign - double sign = ((lineDirection.cross(direction)).dot(decVec) < 0.) ? -1. : 1.; - lposition[eBoundLoc0] *= sign; - - // TODO make intersection and local<->global tolerances sound. quick fixed - // this with a 50% tolerance increase for now - if ((localToGlobal(gctx, lposition, direction) - position).norm() > - tolerance * 1.5) { + + // Bring the global position into the local frame. First remove the + // translation then the rotation. + Vector3 localPosition = referenceFrame(gctx, position, direction).inverse() * + (position - transform(gctx).translation()); + + // `localPosition.z()` is not the distance to the PCA but the smallest + // distance between `position` and the imaginary plane surface defined by the + // local x,y axes in the global frame and the position of the line surface. + // + // This check is also done for the `PlaneSurface` so I aligned the + // `LineSurface` to do the same thing. + if (std::abs(localPosition.z()) > std::abs(tolerance)) { return Result::failure(SurfaceError::GlobalPositionNotOnSurface); } - return Result::success(lposition); + // Construct result from local x,y + Vector2 localXY = localPosition.head<2>(); + + return Result::success(localXY); } std::string Acts::LineSurface::name() const { @@ -110,16 +109,15 @@ std::string Acts::LineSurface::name() const { Acts::RotationMatrix3 Acts::LineSurface::referenceFrame( const GeometryContext& gctx, const Vector3& /*position*/, const Vector3& direction) const { + Vector3 unitZ0 = transform(gctx).rotation() * Vector3::UnitZ(); + Vector3 unitD0 = unitZ0.cross(direction).normalized(); + Vector3 unitDistance = unitD0.cross(unitZ0); + RotationMatrix3 mFrame; - const auto& tMatrix = transform(gctx).matrix(); - Vector3 measY(tMatrix(0, 2), tMatrix(1, 2), tMatrix(2, 2)); - Vector3 measX(measY.cross(direction).normalized()); - Vector3 measDepth(measX.cross(measY)); - // assign the columnes - mFrame.col(0) = measX; - mFrame.col(1) = measY; - mFrame.col(2) = measDepth; - // return the rotation matrix + mFrame.col(0) = unitD0; + mFrame.col(1) = unitZ0; + mFrame.col(2) = unitDistance; + return mFrame; } @@ -134,10 +132,10 @@ Acts::Vector3 Acts::LineSurface::binningPosition( return center(gctx); } -Acts::Vector3 Acts::LineSurface::normal(const GeometryContext& gctx, +Acts::Vector3 Acts::LineSurface::normal(const GeometryContext& /*gctx*/, const Vector2& /*lpos*/) const { - const auto& tMatrix = transform(gctx).matrix(); - return Vector3(tMatrix(0, 2), tMatrix(1, 2), tMatrix(2, 2)); + throw std::runtime_error( + "LineSurface: normal is undefined without known direction"); } const Acts::SurfaceBounds& Acts::LineSurface::bounds() const { @@ -151,45 +149,51 @@ Acts::SurfaceIntersection Acts::LineSurface::intersect( const GeometryContext& gctx, const Vector3& position, const Vector3& direction, const BoundaryCheck& bcheck, ActsScalar tolerance) const { - // following nominclature found in header file and doxygen documentation - // line one is the straight track + // The nomenclature is following the header file and doxygen documentation + const Vector3& ma = position; const Vector3& ea = direction; - // line two is the line surface - const auto& tMatrix = transform(gctx).matrix(); - Vector3 mb = tMatrix.block<3, 1>(0, 3).transpose(); - Vector3 eb = tMatrix.block<3, 1>(0, 2).transpose(); - // now go ahead and solve for the closest approach - Vector3 mab(mb - ma); + + // Origin of the line surface + Vector3 mb = transform(gctx).translation(); + // Line surface axis + Vector3 eb = transform(gctx).rotation() * Vector3::UnitZ(); + + // Now go ahead and solve for the closest approach + Vector3 mab = mb - ma; double eaTeb = ea.dot(eb); double denom = 1 - eaTeb * eaTeb; - // validity parameter - Intersection3D::Status status = Intersection3D::Status::unreachable; - if (std::abs(denom) > std::abs(tolerance)) { - double u = (mab.dot(ea) - mab.dot(eb) * eaTeb) / denom; - // Check if we are on the surface already - status = std::abs(u) < std::abs(tolerance) - ? Intersection3D::Status::onSurface - : Intersection3D::Status::reachable; - Vector3 result = (ma + u * ea); - // Evaluate the boundary check if requested - // m_bounds == nullptr prevents unnecessary calculations for PerigeeSurface - if (bcheck and m_bounds) { - // At closest approach: check inside R or and inside Z - const Vector3 vecLocal(result - mb); - double cZ = vecLocal.dot(eb); - double hZ = m_bounds->get(LineBounds::eHalfLengthZ) + tolerance; - if ((std::abs(cZ) > std::abs(hZ)) or - ((vecLocal - cZ * eb).norm() > - m_bounds->get(LineBounds::eR) + tolerance)) { - status = Intersection3D::Status::missed; - } + + // `tolerance` does not really have a meaning here it is just a sufficiently + // small number so `u` does not explode + if (std::abs(denom) < std::abs(tolerance)) { + // return a false intersection + return {Intersection3D(position, std::numeric_limits::max(), + Intersection3D::Status::unreachable), + this}; + } + + double u = (mab.dot(ea) - mab.dot(eb) * eaTeb) / denom; + // Check if we are on the surface already + Intersection3D::Status status = std::abs(u) > std::abs(tolerance) + ? Intersection3D::Status::reachable + : Intersection3D::Status::onSurface; + Vector3 result = ma + u * ea; + // Evaluate the boundary check if requested + // m_bounds == nullptr prevents unnecessary calculations for PerigeeSurface + if (bcheck and m_bounds) { + // At closest approach: check inside R or and inside Z + Vector3 vecLocal = result - mb; + double cZ = vecLocal.dot(eb); + double hZ = m_bounds->get(LineBounds::eHalfLengthZ) + tolerance; + if ((std::abs(cZ) > std::abs(hZ)) or + ((vecLocal - cZ * eb).norm() > + m_bounds->get(LineBounds::eR) + tolerance)) { + status = Intersection3D::Status::missed; } - return {Intersection3D(result, u, status), this}; } - // return a false intersection - return {Intersection3D(position, std::numeric_limits::max(), status), - this}; + + return {Intersection3D(result, u, status), this}; } Acts::BoundToFreeMatrix Acts::LineSurface::boundToFreeJacobian( @@ -198,37 +202,39 @@ Acts::BoundToFreeMatrix Acts::LineSurface::boundToFreeJacobian( FreeVector freeParams = detail::transformBoundToFreeParameters(*this, gctx, boundParams); // The global position - const Vector3 position = freeParams.segment<3>(eFreePos0); + Vector3 position = freeParams.segment<3>(eFreePos0); // The direction - const Vector3 direction = freeParams.segment<3>(eFreeDir0); + Vector3 direction = freeParams.segment<3>(eFreeDir0); // Get the sines and cosines directly - const double cos_theta = std::cos(boundParams[eBoundTheta]); - const double sin_theta = std::sin(boundParams[eBoundTheta]); - const double cos_phi = std::cos(boundParams[eBoundPhi]); - const double sin_phi = std::sin(boundParams[eBoundPhi]); + double cosTheta = std::cos(boundParams[eBoundTheta]); + double sinTheta = std::sin(boundParams[eBoundTheta]); + double cosPhi = std::cos(boundParams[eBoundPhi]); + double sinPhi = std::sin(boundParams[eBoundPhi]); // retrieve the reference frame - const auto rframe = referenceFrame(gctx, position, direction); + auto rframe = referenceFrame(gctx, position, direction); + // Initialize the jacobian from local to global BoundToFreeMatrix jacToGlobal = BoundToFreeMatrix::Zero(); + // the local error components - given by the reference frame jacToGlobal.topLeftCorner<3, 2>() = rframe.topLeftCorner<3, 2>(); // the time component jacToGlobal(eFreeTime, eBoundTime) = 1; // the momentum components - jacToGlobal(eFreeDir0, eBoundPhi) = (-sin_theta) * sin_phi; - jacToGlobal(eFreeDir0, eBoundTheta) = cos_theta * cos_phi; - jacToGlobal(eFreeDir1, eBoundPhi) = sin_theta * cos_phi; - jacToGlobal(eFreeDir1, eBoundTheta) = cos_theta * sin_phi; - jacToGlobal(eFreeDir2, eBoundTheta) = (-sin_theta); + jacToGlobal(eFreeDir0, eBoundPhi) = -sinTheta * sinPhi; + jacToGlobal(eFreeDir0, eBoundTheta) = cosTheta * cosPhi; + jacToGlobal(eFreeDir1, eBoundPhi) = sinTheta * cosPhi; + jacToGlobal(eFreeDir1, eBoundTheta) = cosTheta * sinPhi; + jacToGlobal(eFreeDir2, eBoundTheta) = -sinTheta; jacToGlobal(eFreeQOverP, eBoundQOverP) = 1; // the projection of direction onto ref frame normal double ipdn = 1. / direction.dot(rframe.col(2)); // build the cross product of d(D)/d(eBoundPhi) components with y axis - auto dDPhiY = rframe.block<3, 1>(0, 1).cross( + Vector3 dDPhiY = rframe.block<3, 1>(0, 1).cross( jacToGlobal.block<3, 1>(eFreeDir0, eBoundPhi)); // and the same for the d(D)/d(eTheta) components - auto dDThetaY = rframe.block<3, 1>(0, 1).cross( + Vector3 dDThetaY = rframe.block<3, 1>(0, 1).cross( jacToGlobal.block<3, 1>(eFreeDir0, eBoundTheta)); // and correct for the x axis components dDPhiY -= rframe.block<3, 1>(0, 0) * (rframe.block<3, 1>(0, 0).dot(dDPhiY)); @@ -239,34 +245,36 @@ Acts::BoundToFreeMatrix Acts::LineSurface::boundToFreeJacobian( dDPhiY * boundParams[eBoundLoc0] * ipdn; jacToGlobal.block<3, 1>(eFreePos0, eBoundTheta) = dDThetaY * boundParams[eBoundLoc0] * ipdn; + return jacToGlobal; } Acts::FreeToPathMatrix Acts::LineSurface::freeToPathDerivative( const GeometryContext& gctx, const FreeVector& parameters) const { // The global posiiton - const auto position = parameters.segment<3>(eFreePos0); + Vector3 position = parameters.segment<3>(eFreePos0); // The direction - const auto direction = parameters.segment<3>(eFreeDir0); + Vector3 direction = parameters.segment<3>(eFreeDir0); // The vector between position and center - const auto pcRowVec = (position - center(gctx)).transpose().eval(); - // The rotation - const auto& rotation = transform(gctx).rotation(); + Vector3 pcRowVec = position - center(gctx); // The local frame z axis - const auto& localZAxis = rotation.col(2); + Vector3 localZAxis = transform(gctx).rotation() * Vector3::UnitZ(); // The local z coordinate - const auto pz = pcRowVec * localZAxis; + double pz = pcRowVec.dot(localZAxis); // Cosine of angle between momentum direction and local frame z axis - const auto dz = localZAxis.dot(direction); - const auto norm = 1 / (1 - dz * dz); + double dz = localZAxis.dot(direction); + double norm = 1 / (1 - dz * dz); + // Initialize the derivative of propagation path w.r.t. free parameter FreeToPathMatrix freeToPath = FreeToPathMatrix::Zero(); + // The derivative of path w.r.t. position freeToPath.segment<3>(eFreePos0) = norm * (dz * localZAxis.transpose() - direction.transpose()); + // The derivative of path w.r.t. direction freeToPath.segment<3>(eFreeDir0) = - norm * (pz * localZAxis.transpose() - pcRowVec); + norm * (pz * localZAxis.transpose() - pcRowVec.transpose()); return freeToPath; } @@ -274,46 +282,42 @@ Acts::FreeToPathMatrix Acts::LineSurface::freeToPathDerivative( Acts::AlignmentToPathMatrix Acts::LineSurface::alignmentToPathDerivative( const GeometryContext& gctx, const FreeVector& parameters) const { // The global posiiton - const auto position = parameters.segment<3>(eFreePos0); + Vector3 position = parameters.segment<3>(eFreePos0); // The direction - const auto direction = parameters.segment<3>(eFreeDir0); + Vector3 direction = parameters.segment<3>(eFreeDir0); // The vector between position and center - const auto pcRowVec = (position - center(gctx)).transpose().eval(); - // The rotation - const auto& rotation = transform(gctx).rotation(); + Vector3 pcRowVec = position - center(gctx); // The local frame z axis - const Vector3 localZAxis = rotation.col(2); + Vector3 localZAxis = transform(gctx).rotation() * Vector3::UnitZ(); // The local z coordinate - const auto pz = pcRowVec * localZAxis; + double pz = pcRowVec.dot(localZAxis); // Cosine of angle between momentum direction and local frame z axis - const auto dz = localZAxis.dot(direction); - const auto norm = 1 / (1 - dz * dz); + double dz = localZAxis.dot(direction); + double norm = 1 / (1 - dz * dz); // Calculate the derivative of local frame axes w.r.t its rotation - const auto [rotToLocalXAxis, rotToLocalYAxis, rotToLocalZAxis] = - detail::rotationToLocalAxesDerivative(rotation); + auto [rotToLocalXAxis, rotToLocalYAxis, rotToLocalZAxis] = + detail::rotationToLocalAxesDerivative(transform(gctx).rotation()); + // Initialize the derivative of propagation path w.r.t. local frame // translation (origin) and rotation AlignmentToPathMatrix alignToPath = AlignmentToPathMatrix::Zero(); alignToPath.segment<3>(eAlignmentCenter0) = norm * (direction.transpose() - dz * localZAxis.transpose()); alignToPath.segment<3>(eAlignmentRotation0) = - norm * (dz * pcRowVec + pz * direction.transpose()) * rotToLocalZAxis; + norm * (dz * pcRowVec.transpose() + pz * direction.transpose()) * + rotToLocalZAxis; return alignToPath; } Acts::ActsMatrix<2, 3> Acts::LineSurface::localCartesianToBoundLocalDerivative( const GeometryContext& gctx, const Vector3& position) const { - using VectorHelpers::phi; - // The local frame transform - const auto& sTransform = transform(gctx); // calculate the transformation to local coordinates - const Vector3 localPos = sTransform.inverse() * position; - const double lphi = phi(localPos); - const double lcphi = std::cos(lphi); - const double lsphi = std::sin(lphi); + Vector3 localPosition = transform(gctx).inverse() * position; + double localPhi = VectorHelpers::phi(localPosition); + ActsMatrix<2, 3> loc3DToLocBound = ActsMatrix<2, 3>::Zero(); - loc3DToLocBound << lcphi, lsphi, 0, 0, 0, 1; + loc3DToLocBound << std::cos(localPhi), std::sin(localPhi), 0, 0, 0, 1; return loc3DToLocBound; -} \ No newline at end of file +} diff --git a/Examples/Python/tests/root_file_hashes.txt b/Examples/Python/tests/root_file_hashes.txt index eeeebb4126d..076e072825b 100644 --- a/Examples/Python/tests/root_file_hashes.txt +++ b/Examples/Python/tests/root_file_hashes.txt @@ -23,21 +23,21 @@ test_itk_seeding__particles_initial.root: 88315e93ed4cb5d40a8721502048a9d1fc100e test_propagation__propagation_steps.root: 174301b25784dbb881196b658f2d7f99c0a2ea688a0129e6110fc19aa5cf8e54 test_material_recording__geant4_material_tracks.root: e411152d370775463c22b19a351dfc7bfe40b51985e10a7c1a010aebde80715d test_truth_tracking_kalman[generic-0.0]__trackstates_fitter.root: 7e933e0219728ad8c139c2c8d0a7b09d133108a2c3053b0e1972189e34534592 -test_truth_tracking_kalman[generic-0.0]__tracksummary_fitter.root: db6695050d0290e43b3579b8b6f66f013cbc33548c2bfb5ae8f4ff8306c6e400 +test_truth_tracking_kalman[generic-0.0]__tracksummary_fitter.root: e41639378960b2ecbcc31ad432e29eb56b6d900e642fdc53879d0708f8ab9155 test_truth_tracking_kalman[generic-0.0]__performance_track_finder.root: 7fc6f717723c9eddcbf44820b384b373cee6f04b72f79902f938f35e3ff9b470 test_truth_tracking_kalman[generic-1000.0]__trackstates_fitter.root: 6c0313a50148aa71e20d817642cd2dffc85e467915298f00d75bef33ad83cf3c -test_truth_tracking_kalman[generic-1000.0]__tracksummary_fitter.root: 89ea90d7dc0d36e7d7ab2db0f0dfe1c5b14e04533a969684e59195bed037777a +test_truth_tracking_kalman[generic-1000.0]__tracksummary_fitter.root: fd522a5636614a4b987802bbb8c9ff9b5eb0d9b0e876a3066e66354eaa297398 test_truth_tracking_kalman[generic-1000.0]__performance_track_finder.root: 7fc6f717723c9eddcbf44820b384b373cee6f04b72f79902f938f35e3ff9b470 test_truth_tracking_kalman[odd-0.0]__trackstates_fitter.root: 4093d18a84cd01ddec712adadcf8f271e7dd1fa473a734f44b80963477fc4f54 -test_truth_tracking_kalman[odd-0.0]__tracksummary_fitter.root: a515049efc3c1aab2147646dbe0f0977bd72af139fccab054fef3774af84ba90 +test_truth_tracking_kalman[odd-0.0]__tracksummary_fitter.root: d33319894494feb8c0d6f0f90a1d1e59caf9de32eda480ab73d0e40dfcdd1322 test_truth_tracking_kalman[odd-0.0]__performance_track_finder.root: 39aec6316cceb90e314e16b02947faa691c18f57c3a851a25e547a8fc05a4593 test_truth_tracking_kalman[odd-1000.0]__trackstates_fitter.root: 0fcf2020a10ddd7030cd1b58ef7affd60121b83eabb92e9547818e93bab9c777 -test_truth_tracking_kalman[odd-1000.0]__tracksummary_fitter.root: fcad595e4062157fa6f2c871ca31d203aa85dced26ed92dc286b61c571314123 +test_truth_tracking_kalman[odd-1000.0]__tracksummary_fitter.root: 99fe9c3be034549816c73f7f3d2190df7efb0adf6aed16cceb86aa4a9ac271f0 test_truth_tracking_kalman[odd-1000.0]__performance_track_finder.root: 39aec6316cceb90e314e16b02947faa691c18f57c3a851a25e547a8fc05a4593 test_truth_tracking_gsf[generic]__trackstates_gsf.root: 787294f42fadbd14827ae47133ba90d657999fa815df3fa01e3ddc3c0709d880 -test_truth_tracking_gsf[generic]__tracksummary_gsf.root: 3229442f127c05f9f1d9111c85df65147a5ef285ff473f102da4221e6354bd73 +test_truth_tracking_gsf[generic]__tracksummary_gsf.root: abfa600668eda81fa0542df2d9ced6d550d744f1f8f3f4c789d137f7748363e2 test_truth_tracking_gsf[odd]__trackstates_gsf.root: 0c6b46ee55e992c0846abea69d69205cbec358aa48330396ce83955bb2153177 -test_truth_tracking_gsf[odd]__tracksummary_gsf.root: 6ff39a138089c9b246958df1052b73cd4afd453f954ba568336eb988e826fcb7 +test_truth_tracking_gsf[odd]__tracksummary_gsf.root: 2b6f6b95af4bd36d0a1084a6fb79e0d09dac622f81c8fd1a0c5550c4cc458b66 test_particle_gun__particles.root: 8549ba6e20338004ab8ba299fc65e1ee5071985b46df8f77f887cb6fef56a8ec test_material_mapping__material-map_tracks.root: 4e1c866038f0c06b099aa74fd01c3d875f07b89f54898f90debd9b558d8e4025 test_material_mapping__propagation-material.root: 646b8e2bbacec40d0bc4132236f9ab3f03b088e656e6e9b80c47ae03eaf6eab5 @@ -50,21 +50,21 @@ test_digitization_example_input[smeared]__measurements.root: 0f42102396b84a7c563 test_digitization_example_input[geometric]__particles.root: 8549ba6e20338004ab8ba299fc65e1ee5071985b46df8f77f887cb6fef56a8ec test_digitization_example_input[geometric]__measurements.root: 567cb403baaf71e75029d42fae8d10c412cf05536a30d69a8b45de40074fa386 test_ckf_tracks_example[generic-full_seeding]__trackstates_ckf.root: d86c03a97613d6cd0646b25b5fd58a1dccaba704d9af2d88acb651dd54c68106 -test_ckf_tracks_example[generic-full_seeding]__tracksummary_ckf.root: b71bdb12af27c1e94c139ee3264823f048e92d23fe34039797ef78679bf89d9d +test_ckf_tracks_example[generic-full_seeding]__tracksummary_ckf.root: 048e0fdbc420ea7e06d9a92543eaa35d39cc6ed7aafde1ad6160da299fc60a50 test_ckf_tracks_example[generic-full_seeding]__performance_seeding_trees.root: 0e0676ffafdb27112fbda50d1cf627859fa745760f98073261dcf6db3f2f991e test_ckf_tracks_example[generic-truth_estimated]__trackstates_ckf.root: a69c698efe1d6a25c934f6ee2a6ce66a46846321c3c321f39830db3999d6117a -test_ckf_tracks_example[generic-truth_estimated]__tracksummary_ckf.root: ea69b54988175d4ebb2bef25a273b06520a780c98f38d405e655de9b65bbae7f +test_ckf_tracks_example[generic-truth_estimated]__tracksummary_ckf.root: 8f0bd648fb5e35b7a37fb00b7f7534381a06290adc1ccfc1be3d7ed67eecb2fe test_ckf_tracks_example[generic-truth_estimated]__performance_seeding.root: 1facb05c066221f6361b61f015cdf0918e94d9f3fce2269ec7b6a4dffeb2bc7e test_ckf_tracks_example[generic-truth_smeared]__trackstates_ckf.root: 3fa8cc03f5840fb75fb53f3b1ee39021036b22d46f97c41f599269fae4e459b5 -test_ckf_tracks_example[generic-truth_smeared]__tracksummary_ckf.root: fa1dd16d2acb9561f5405c7e7eb270a3508c5ef231a27f01a9aba1d8dd832f87 +test_ckf_tracks_example[generic-truth_smeared]__tracksummary_ckf.root: 47ca8f012d1d5578699372747469719daa185d7a5684500be093f6591b8abb25 test_ckf_tracks_example[odd-full_seeding]__trackstates_ckf.root: af06990ef8be779d00d0e6ba2496af5c97a3e0487d47e017fdae261066473590 -test_ckf_tracks_example[odd-full_seeding]__tracksummary_ckf.root: 2cce165675400e2a830f2c8c03f844ff6f00e3ea17c6278677a3d707a553f46c +test_ckf_tracks_example[odd-full_seeding]__tracksummary_ckf.root: d178b6d22f4789f29d360b0ec838045992c1367b8c93b5ce1980f88096684bca test_ckf_tracks_example[odd-full_seeding]__performance_seeding_trees.root: 43c58577aafe07645e5660c4f43904efadf91d8cda45c5c04c248bbe0f59814f test_ckf_tracks_example[odd-truth_estimated]__trackstates_ckf.root: 20eabea9f47a9fbee54b1251123ee1610195a07140feedb6825bb935a9ea9acf -test_ckf_tracks_example[odd-truth_estimated]__tracksummary_ckf.root: 3e86a6fc0d598e7869f746f1a3104bc06372f4ce4f2b25eb84e44020394aebce +test_ckf_tracks_example[odd-truth_estimated]__tracksummary_ckf.root: e6c9892799b21a24549c343be0c0bcf208f28f7d17724f0bf457757a1df866ff test_ckf_tracks_example[odd-truth_estimated]__performance_seeding.root: 1a36b7017e59f1c08602ef3c2cb0483c51df248f112e3780c66594110719c575 test_ckf_tracks_example[odd-truth_smeared]__trackstates_ckf.root: da5fc4d1d273c249b21b2653e7e66e084159af47919f095778b6959565d1e50a -test_ckf_tracks_example[odd-truth_smeared]__tracksummary_ckf.root: a36d469f019ec3bd093d940a57837ca1acf069f31c226f620b889ffa04c8ac56 +test_ckf_tracks_example[odd-truth_smeared]__tracksummary_ckf.root: ce4d79a569b2c55aa3d26c9216e358db544e4edccd4c8c70304211bcb7649101 test_vertex_fitting_reading[Truth-False-100]__performance_vertexing.root: 76ef6084d758dfdfc0151ddec2170e12d73394424e3dac4ffe46f0f339ec8293 test_vertex_fitting_reading[Iterative-False-100]__performance_vertexing.root: 60372210c830a04f95ceb78c6c68a9b0de217746ff59e8e73053750c837b57eb test_vertex_fitting_reading[Iterative-True-100]__performance_vertexing.root: e34f217d524a5051dbb04a811d3407df3ebe2cc4bb7f54f6bda0847dbd7b52c3 diff --git a/Tests/UnitTests/Core/Surfaces/LineSurfaceTests.cpp b/Tests/UnitTests/Core/Surfaces/LineSurfaceTests.cpp index 22d64f7af78..707c32777c5 100644 --- a/Tests/UnitTests/Core/Surfaces/LineSurfaceTests.cpp +++ b/Tests/UnitTests/Core/Surfaces/LineSurfaceTests.cpp @@ -7,14 +7,18 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. #include +#include #include #include #include "Acts/Definitions/Algebra.hpp" #include "Acts/Definitions/Alignment.hpp" #include "Acts/Definitions/TrackParametrization.hpp" +#include "Acts/EventData/TrackParameters.hpp" #include "Acts/Geometry/GeometryContext.hpp" #include "Acts/Material/HomogeneousSurfaceMaterial.hpp" +#include "Acts/Propagator/Propagator.hpp" +#include "Acts/Propagator/StraightLineStepper.hpp" #include "Acts/Surfaces/BoundaryCheck.hpp" #include "Acts/Surfaces/LineBounds.hpp" #include "Acts/Surfaces/Surface.hpp" @@ -27,11 +31,13 @@ #include "Acts/Utilities/Intersection.hpp" #include "Acts/Utilities/Result.hpp" #include "Acts/Utilities/UnitVectors.hpp" +#include "Acts/Utilities/VectorHelpers.hpp" #include #include #include #include +#include #include #include #include @@ -49,6 +55,7 @@ GeometryContext tgContext = GeometryContext(); // using boost::test_tools::output_test_stream; BOOST_AUTO_TEST_SUITE(Surfaces) + /// Unit test for creating compliant/non-compliant LineSurface object BOOST_AUTO_TEST_CASE(LineSurface_Constructors_test) { // Default ctor is deleted @@ -84,6 +91,7 @@ BOOST_AUTO_TEST_CASE(LineSurface_Constructors_test) { BOOST_TEST_MESSAGE( "All LineSurface constructors are callable without problem"); } + /// Unit tests of all named methods BOOST_AUTO_TEST_CASE(LineSurface_allNamedMethods_test) { // binningPosition() @@ -104,7 +112,8 @@ BOOST_AUTO_TEST_CASE(LineSurface_allNamedMethods_test) { // globalToLocal() Vector3 gpos{0., 1., 0.}; const Vector3 mom{20., 0., 0.}; // needs more realistic parameters - Vector2 localPosition = line.globalToLocal(tgContext, gpos, mom).value(); + Vector2 localPosition = + line.globalToLocal(tgContext, gpos, mom.normalized()).value(); const Vector2 expectedResult{0, -2}; CHECK_CLOSE_ABS(expectedResult, localPosition, 1e-6); // @@ -131,14 +140,14 @@ BOOST_AUTO_TEST_CASE(LineSurface_allNamedMethods_test) { // Vector2 localPosition{0., 0.}; const Vector3 momentum{300., 200., 0.}; // find better values! returnedGlobalPosition = - line.localToGlobal(tgContext, localPosition, momentum); + line.localToGlobal(tgContext, localPosition, momentum.normalized()); const Vector3 expectedGlobalPosition{0, 1, 0}; CHECK_CLOSE_ABS(returnedGlobalPosition, expectedGlobalPosition, 1e-6); // // referenceFrame Vector3 globalPosition{0., 0., 0.}; auto returnedRotationMatrix = - line.referenceFrame(tgContext, globalPosition, momentum); + line.referenceFrame(tgContext, globalPosition, momentum.normalized()); double v0 = std::cos(std::atan(2. / 3.)); double v1 = std::sin(std::atan(2. / 3.)); RotationMatrix3 expectedRotationMatrix; @@ -153,15 +162,15 @@ BOOST_AUTO_TEST_CASE(LineSurface_allNamedMethods_test) { output << line.name(); BOOST_CHECK(output.is_equal("Acts::LineSurface")); // - // normal - Vector3 normalVector{0., 0., 1.}; // line direction is same as normal???? - CHECK_CLOSE_ABS(line.normal(tgContext), normalVector, 1e-6); + // normal does not exist without known momentum direction for line surface + BOOST_CHECK_THROW(line.normal(tgContext), std::runtime_error); // - // pathCorrection - auto any3DVector = normalVector; - CHECK_CLOSE_REL(line.pathCorrection(tgContext, any3DVector, any3DVector), 1., - 1e-6); + // pathCorrection is always 1 for the line surface + CHECK_CLOSE_ABS( + line.pathCorrection(tgContext, Vector3::Zero(), Vector3::UnitX()), 1, + 1e-6); } + /// Unit test for testing LineSurface assignment BOOST_AUTO_TEST_CASE(LineSurface_assignment_test) { Translation3 translation{0., 1., 2.}; @@ -266,6 +275,58 @@ BOOST_AUTO_TEST_CASE(LineSurfaceTransformRoundTripEtaStability) { } } +BOOST_AUTO_TEST_CASE(LineSurfaceIntersection) { + using namespace Acts::UnitLiterals; + + double eps = 1e-10; + + Vector3 direction = Vector3(1, 1, 100).normalized(); + BoundVector boundVector; + boundVector << 1_cm, 1_cm, VectorHelpers::phi(direction), + VectorHelpers::theta(direction), 1, 0; + double pathLimit = 1_cm; + + auto surface = std::make_shared(Transform3::Identity()); + + BoundTrackParameters initialParams{surface, boundVector, 1}; + + Propagator propagator({}); + + CurvilinearTrackParameters displacedParameters{Vector4::Zero(), + Vector3::Zero(), 1}; + { + PropagatorOptions<> options(tgContext, {}); + options.direction = Acts::Direction::Backward; + options.pathLimit = pathLimit; + + auto result = propagator.propagate(initialParams, options); + BOOST_CHECK(result.ok()); + BOOST_CHECK(result.value().endParameters); + + displacedParameters = result.value().endParameters.value(); + } + + auto intersection = + surface->intersect(tgContext, displacedParameters.position(tgContext), + displacedParameters.unitDirection()); + CHECK_CLOSE_ABS(intersection.intersection.pathLength, pathLimit, eps); + + BoundTrackParameters endParameters{surface, BoundVector::Zero(), 1}; + { + PropagatorOptions<> options(tgContext, {}); + options.direction = Acts::Direction::Forward; + options.maxStepSize = 1_mm; + + auto result = propagator.propagate(displacedParameters, *surface, options); + BOOST_CHECK(result.ok()); + BOOST_CHECK(result.value().endParameters); + CHECK_CLOSE_ABS(result.value().pathLength, pathLimit, eps); + endParameters = result.value().endParameters.value(); + } + + CHECK_CLOSE_ABS(initialParams.parameters(), endParameters.parameters(), eps); +} + BOOST_AUTO_TEST_SUITE_END() } // namespace Test From 6b2533fe8fa50b09e7d4c444dafe9ff3a4cf7375 Mon Sep 17 00:00:00 2001 From: Stephen Nicholas Swatman Date: Fri, 28 Jul 2023 10:14:55 +0200 Subject: [PATCH 03/21] fix: Update to Pythia 8.310 API for random engines (#2329) For some odd reason, Pythia 8.310 introduces a new API for the `rndmEnginePtr` method. It now requires a shared pointer rather than a raw pointer. This commit adds support for this behaviour if ACTS is being compiled against a recent version of Pythia. --- .../ActsExamples/Generators/Pythia8ProcessGenerator.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Examples/Algorithms/GeneratorsPythia8/ActsExamples/Generators/Pythia8ProcessGenerator.cpp b/Examples/Algorithms/GeneratorsPythia8/ActsExamples/Generators/Pythia8ProcessGenerator.cpp index 3cbf7eec919..a98e24adbc2 100644 --- a/Examples/Algorithms/GeneratorsPythia8/ActsExamples/Generators/Pythia8ProcessGenerator.cpp +++ b/Examples/Algorithms/GeneratorsPythia8/ActsExamples/Generators/Pythia8ProcessGenerator.cpp @@ -62,9 +62,13 @@ ActsExamples::SimParticleContainer ActsExamples::Pythia8Generator::operator()( // pythia8 is not thread safe and generation needs to be protected std::lock_guard lock(m_pythia8Mutex); - // use per-thread random engine also in pythia +// use per-thread random engine also in pythia +#if PYTHIA_VERSION_INTEGER >= 8310 + m_pythia8->rndm.rndmEnginePtr(std::make_shared(rng)); +#else FrameworkRndmEngine rndmEngine(rng); m_pythia8->rndm.rndmEnginePtr(&rndmEngine); +#endif { Acts::FpeMonitor mon{0}; // disable all FPEs while we're in Pythia8 m_pythia8->next(); From 1a1660e39a38ddb900d533c76ed2ae06666a1d41 Mon Sep 17 00:00:00 2001 From: Paul Gessinger Date: Fri, 28 Jul 2023 10:22:22 +0200 Subject: [PATCH 04/21] ci: Reenable validate on push for policybot This reverts commit d6c23c6ace5d974ac89bd852004efbc4a0c39634. Policybot added a fix in v1.31.0 --- .policy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.policy.yml b/.policy.yml index 96c8f8b0274..ccadc6dccf0 100644 --- a/.policy.yml +++ b/.policy.yml @@ -12,7 +12,7 @@ approval_rules: options: allow_author: false # just for completeness allow_contributor: true # Update button 'contributions' should be ignored - invalidate_on_push: false + invalidate_on_push: true ignore_update_merges: true if: targets_branch: From 75b6eb2d9802bac0add908266865fb3f92edf45f Mon Sep 17 00:00:00 2001 From: Andreas Stefl Date: Fri, 28 Jul 2023 11:45:35 +0200 Subject: [PATCH 05/21] docs: Improve `Units.hpp` doc (#2315) - Improve doc - Use oxygen - Fix definition of `T` - Provide alternative definitions - Make dependencies clear This leaves the numerical values untouched so I do not expect any changes in the outputs. --- Core/include/Acts/Definitions/Units.hpp | 38 +++++++++++++------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/Core/include/Acts/Definitions/Units.hpp b/Core/include/Acts/Definitions/Units.hpp index 9aecd45445c..b61515234a9 100644 --- a/Core/include/Acts/Definitions/Units.hpp +++ b/Core/include/Acts/Definitions/Units.hpp @@ -137,14 +137,14 @@ namespace Acts { namespace UnitConstants { // Length, native unit mm -constexpr double fm = 1e-12; -constexpr double pm = 1e-9; -constexpr double um = 1e-3; -constexpr double nm = 1e-6; constexpr double mm = 1.0; -constexpr double cm = 10.0; -constexpr double m = 1e3; -constexpr double km = 1e6; +constexpr double fm = 1e-12 * mm; +constexpr double pm = 1e-9 * mm; +constexpr double nm = 1e-6 * mm; +constexpr double um = 1e-3 * mm; +constexpr double cm = 1e1 * mm; +constexpr double m = 1e3 * mm; +constexpr double km = 1e6 * mm; // Shortcuts for commonly used area and volume units. This intentionally // contains not all possible combinations to avoid cluttering the namespace. // Missing area or volume units can always be defined on the fly using the @@ -158,7 +158,8 @@ constexpr double mm3 = mm * mm * mm; constexpr double cm3 = cm * cm * cm; constexpr double m3 = m * m * m; // Time, native unit mm = [speed-of-light * time] = mm/s * s -constexpr double s = 299792458000.0; +/// @note Depends on speed of light in SI units +constexpr double s = 299792458000.0; // = 299792458.0 * (m / 1.0) * 1.0; constexpr double fs = 1e-15 * s; constexpr double ps = 1e-12 * s; constexpr double ns = 1e-9 * s; @@ -169,15 +170,15 @@ constexpr double h = 3600.0 * s; // Angles, native unit radian constexpr double mrad = 1e-3; constexpr double rad = 1.0; -constexpr double degree = 0.017453292519943295; // pi / 180 +constexpr double degree = 0.017453292519943295; // = M_PI / 180.0 * rad; // Energy/mass/momentum, native unit GeV -constexpr double eV = 1e-9; -constexpr double keV = 1e-6; -constexpr double MeV = 1e-3; constexpr double GeV = 1.0; -constexpr double TeV = 1e3; +constexpr double eV = 1e-9 * GeV; +constexpr double keV = 1e-6 * GeV; +constexpr double MeV = 1e-3 * GeV; +constexpr double TeV = 1e3 * GeV; constexpr double J = 6241509074.460763 * GeV; -// atomic mass unit u +/// atomic mass unit u constexpr double u = 0.93149410242; // 1eV/c² == 1.782662e-36kg // 1GeV/c² == 1.782662e-27kg @@ -185,13 +186,14 @@ constexpr double u = 0.93149410242; // -> 1g == (1/(1e3*1.782662e-27))GeV/c² constexpr double g = 1.0 / 1.782662e-24; constexpr double kg = 1.0 / 1.782662e-27; -// Charge, native unit e (elementary charge) +/// Charge, native unit e (elementary charge) constexpr double e = 1.0; -// Magnetic field, native unit GeV/(e*mm) -constexpr double T = 0.000299792458; // equivalent to c in appropriate SI units +/// Magnetic field, native unit (eV*s)/(e*m²) +/// @note Depends on speed of light in SI units +constexpr double T = 0.000299792458; // = eV * s / (e * m2); constexpr double Gauss = 1e-4 * T; constexpr double kGauss = 1e-1 * T; -// Amount of substance, native unit mol +/// Amount of substance, native unit mol constexpr double mol = 1.0; } // namespace UnitConstants From a2d0eb55d99e4ae4ef0d194bab90af189f506e63 Mon Sep 17 00:00:00 2001 From: Andreas Stefl Date: Fri, 28 Jul 2023 13:53:20 +0200 Subject: [PATCH 06/21] feat: Introduce particle hypothesis (#2254) In this PR I introduce the concept of a particle hypothesis which will later be used by the track parameters. I also refactored the Charge interface a bit. This is the foundation for upcoming refactor PRs for the particle hypothesis. Pulled out from https://github.com/acts-project/acts/pull/2181. Co-authored-by: Paul Gessinger <1058585+paulgessinger@users.noreply.github.com> --- Core/include/Acts/EventData/Charge.hpp | 162 ++++++++++++++---- Core/include/Acts/EventData/ChargeConcept.hpp | 44 +++++ .../EventData/GenericParticleHypothesis.hpp | 127 ++++++++++++++ .../Acts/EventData/ParticleHypothesis.hpp | 143 ++++++++++++++++ Tests/UnitTests/Core/EventData/CMakeLists.txt | 2 +- .../UnitTests/Core/EventData/ChargeTests.cpp | 52 +++++- .../EventData/ParticleHypothesisTests.cpp | 111 ++++++++++++ 7 files changed, 597 insertions(+), 44 deletions(-) create mode 100644 Core/include/Acts/EventData/ChargeConcept.hpp create mode 100644 Core/include/Acts/EventData/GenericParticleHypothesis.hpp create mode 100644 Core/include/Acts/EventData/ParticleHypothesis.hpp create mode 100644 Tests/UnitTests/Core/EventData/ParticleHypothesisTests.cpp diff --git a/Core/include/Acts/EventData/Charge.hpp b/Core/include/Acts/EventData/Charge.hpp index e6df928ffe1..b7888cc4282 100644 --- a/Core/include/Acts/EventData/Charge.hpp +++ b/Core/include/Acts/EventData/Charge.hpp @@ -9,6 +9,8 @@ #pragma once #include "Acts/Definitions/Units.hpp" +#include "Acts/EventData/ChargeConcept.hpp" +#include "Acts/Utilities/Concepts.hpp" #include #include @@ -51,26 +53,36 @@ namespace Acts { /// Charge and momentum interpretation for neutral particles. struct Neutral { - Neutral() = default; + constexpr Neutral() = default; + + // TODO remove this method after grad refactor; currently track parameters + // depend on it /// Construct and verify the input charge magnitude (in debug builds). /// /// This constructor is only provided to allow consistent construction. - template - constexpr Neutral(T absQ) noexcept { - assert((absQ == static_cast(0)) and "Input charge must be zero"); - // suppress `unused variable` warning in non-debug builds - (void)(absQ); + constexpr Neutral(float absQ) noexcept { + assert((absQ == 0) and "Input charge must be zero"); + (void)absQ; } + constexpr float absQ() const noexcept { return 0; } + template - constexpr T extractCharge(T /* pInv */) const noexcept { - return 0; + constexpr auto extractCharge(T /*qOverP*/) const noexcept { + return 0.0f; } + template - constexpr T extractMomentum(T pInv) const noexcept { - // the abs is not strictly needed. it is added to protect against invalid, - // i.e. negative, 1/p values to ensure that the output is still correct. - return std::abs(1 / pInv); + constexpr auto extractMomentum(T qOverP) const noexcept { + assert(qOverP >= 0 && "qOverP cannot be negative"); + return 1.0f / qOverP; + } + + template + constexpr auto qOverP(P momentum, Q signedQ) const noexcept { + assert((signedQ != 0) and "charge must be 0"); + (void)signedQ; + return 1.0f / momentum; } /// Compare for equality. @@ -82,27 +94,43 @@ struct Neutral { } }; +ACTS_STATIC_CHECK_CONCEPT(ChargeConcept, Neutral); + /// Charge and momentum interpretation for particles with +-e charge. struct SinglyCharged { - SinglyCharged() = default; + constexpr SinglyCharged() = default; + + // TODO remove this method after grad refactor; currently track parameters + // depend on it /// Construct and verify the input charge magnitude (in debug builds). /// /// This constructor is only provided to allow consistent construction. - template - constexpr SinglyCharged(T absQ) noexcept { - assert((absQ == static_cast(UnitConstants::e)) and - "Input charge magnitude must be e"); - // suppress `unused variable` warning in non-debug builds - (void)(absQ); + constexpr SinglyCharged(float absQ) noexcept { + assert((absQ == UnitConstants::e) and "Input charge magnitude must be e"); + (void)absQ; } + constexpr float absQ() const noexcept { return UnitConstants::e; } + template - constexpr T extractCharge(T qOverP) const noexcept { - return std::copysign(static_cast(UnitConstants::e), qOverP); + constexpr auto extractCharge(T qOverP) const noexcept { + // using because of autodiff + using std::copysign; + return copysign(UnitConstants::e, qOverP); } + template - constexpr T extractMomentum(T qOverP) const noexcept { - return std::abs(static_cast(UnitConstants::e) / qOverP); + constexpr auto extractMomentum(T qOverP) const noexcept { + // using because of autodiff + using std::abs; + return extractCharge(qOverP) / qOverP; + } + + template + constexpr auto qOverP(P momentum, Q signedQ) const noexcept { + using std::abs; + assert((abs(signedQ) == UnitConstants::e) && "absolute charge must be e"); + return signedQ / momentum; } /// Compare for equality. @@ -115,6 +143,54 @@ struct SinglyCharged { } }; +ACTS_STATIC_CHECK_CONCEPT(ChargeConcept, SinglyCharged); + +/// Charge and momentum interpretation for arbitrarily charged but not neutral +/// particles. +class NonNeutralCharge { + public: + /// Construct with the magnitude of the input charge. + constexpr NonNeutralCharge(float absQ) noexcept : m_absQ{absQ} { + assert((0 < absQ) and "Input charge magnitude must be positive"); + } + constexpr NonNeutralCharge(SinglyCharged /*unused*/) noexcept + : m_absQ{UnitConstants::e} {} + + constexpr float absQ() const noexcept { return m_absQ; } + + template + constexpr auto extractCharge(T qOverP) const noexcept { + // using because of autodiff + using std::copysign; + return copysign(m_absQ, qOverP); + } + template + constexpr auto extractMomentum(T qOverP) const noexcept { + // using because of autodiff + using std::abs; + return extractCharge(qOverP) / qOverP; + } + + template + constexpr auto qOverP(P momentum, Q signedQ) const noexcept { + // using because of autodiff + using std::abs; + assert(abs(signedQ) == m_absQ && "inconsistent charge"); + return signedQ / momentum; + } + + /// Compare for equality. + friend constexpr bool operator==(NonNeutralCharge lhs, + NonNeutralCharge rhs) noexcept { + return lhs.m_absQ == rhs.m_absQ; + } + + private: + float m_absQ{}; +}; + +ACTS_STATIC_CHECK_CONCEPT(ChargeConcept, NonNeutralCharge); + /// Charge and momentum interpretation for arbitrarily charged particles. /// /// Only a charge magnitude identical to zero is interpreted as representing a @@ -122,34 +198,48 @@ struct SinglyCharged { /// approximate comparison with an arbitrary epsilon. class AnyCharge { public: - /// Delete default constructor to ensure charge is always explicitly given. - AnyCharge() = delete; /// Construct with the magnitude of the input charge. - template - constexpr AnyCharge(T absQ) noexcept : m_magnitude(std::abs(absQ)) { + constexpr AnyCharge(float absQ) noexcept : m_absQ{absQ} { assert((0 <= absQ) and "Input charge magnitude must be zero or positive"); } + constexpr AnyCharge(SinglyCharged /*unused*/) noexcept + : m_absQ{UnitConstants::e} {} + constexpr AnyCharge(Neutral /*unused*/) noexcept {} + + constexpr float absQ() const noexcept { return m_absQ; } template - constexpr T extractCharge(T qOverP) const noexcept { - return std::copysign(static_cast(m_magnitude), qOverP); + constexpr auto extractCharge(T qOverP) const noexcept { + // using because of autodiff + using std::copysign; + return copysign(m_absQ, qOverP); } template - constexpr T extractMomentum(T qOverP) const noexcept { - return (m_magnitude != 0.0f) - ? std::abs(static_cast(m_magnitude) / qOverP) - : std::abs(1 / qOverP); + constexpr auto extractMomentum(T qOverP) const noexcept { + // using because of autodiff + using std::abs; + return (m_absQ != 0.0f) ? extractCharge(qOverP) / qOverP : 1.0f / qOverP; } - private: - float m_magnitude; + template + constexpr auto qOverP(P momentum, Q signedQ) const noexcept { + // using because of autodiff + using std::abs; + assert(abs(signedQ) == m_absQ && "inconsistent charge"); + return (m_absQ != 0.0f) ? signedQ / momentum : 1.0f / momentum; + } /// Compare for equality. friend constexpr bool operator==(AnyCharge lhs, AnyCharge rhs) noexcept { - return lhs.m_magnitude == rhs.m_magnitude; + return lhs.m_absQ == rhs.m_absQ; } + + private: + float m_absQ{}; }; +ACTS_STATIC_CHECK_CONCEPT(ChargeConcept, AnyCharge); + /// @} } // namespace Acts diff --git a/Core/include/Acts/EventData/ChargeConcept.hpp b/Core/include/Acts/EventData/ChargeConcept.hpp new file mode 100644 index 00000000000..9837247971b --- /dev/null +++ b/Core/include/Acts/EventData/ChargeConcept.hpp @@ -0,0 +1,44 @@ +// 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/TrackParametrization.hpp" +#include "Acts/EventData/Types.hpp" +#include "Acts/Surfaces/Surface.hpp" +#include "Acts/Utilities/HashedString.hpp" + +#include +#include + +#if defined(ACTS_CONCEPTS_SUPPORTED) +#include + +namespace Acts { + +template +concept ChargeConcept = requires(C c, float f, double d) { + {C{f}}; + { c.absQ() } -> std::same_as; + + { c.extractCharge(f) } -> std::convertible_to; + { c.extractCharge(d) } -> std::convertible_to; + + { c.extractMomentum(f) } -> std::convertible_to; + { c.extractMomentum(d) } -> std::convertible_to; + + { c.qOverP(f, f) } -> std::same_as; + { c.qOverP(d, d) } -> std::same_as; + + { c == c } -> std::same_as; + { c != c } -> std::same_as; +}; + +} // namespace Acts + +#endif diff --git a/Core/include/Acts/EventData/GenericParticleHypothesis.hpp b/Core/include/Acts/EventData/GenericParticleHypothesis.hpp new file mode 100644 index 00000000000..a2a6d3176ec --- /dev/null +++ b/Core/include/Acts/EventData/GenericParticleHypothesis.hpp @@ -0,0 +1,127 @@ +// 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/ParticleData.hpp" +#include "Acts/Definitions/PdgParticle.hpp" +#include "Acts/EventData/Charge.hpp" +#include "Acts/EventData/ChargeConcept.hpp" +#include "Acts/Utilities/Concepts.hpp" + +#include + +namespace Acts { + +/// @brief Particle hypothesis used in reconstruction +/// +/// The reconstruction hypothesis consists of absolute PDG code, mass and +/// absolute charge. +template +class GenericParticleHypothesis { + public: + using ChargeType = charge_t; + + /// Creates a particle hypothesis using absolute PDG, mass and the charge + /// type. + /// + /// @param absPdg the absolute PDG + /// @param mass the particle mass + /// @param chargeType the type of charge + constexpr GenericParticleHypothesis(PdgParticle absPdg, float mass, + ChargeType chargeType) + : m_absPdg{absPdg}, m_mass{mass}, m_chargeType{std::move(chargeType)} { + assert(absPdg == makeAbsolutePdgParticle(absPdg) && + "pdg is expected to be absolute"); + } + + /// Creates a particle hypothesis using the absolute PDG. + /// The mass and charge is looked up using @ref findMass and @ref findCharge. + /// If the lookup fails an exception is thrown. + /// + /// @param absPdg the absolute PDG + GenericParticleHypothesis(PdgParticle absPdg) + : m_absPdg{absPdg}, + m_mass{findMass(absPdg).value()}, + m_chargeType{std::abs(findCharge(absPdg).value())} { + assert(absPdg == makeAbsolutePdgParticle(absPdg) && + "pdg is expected to be absolute"); + } + + /// Copy from another charge hypothesis. + /// + /// @note This enables implicit conversion. + template + constexpr GenericParticleHypothesis( + const GenericParticleHypothesis& other) + : m_absPdg{other.absPdg()}, + m_mass{other.mass()}, + m_chargeType{other.chargeType()} {} + + /// Get the hypothesized absolute PDG. + constexpr PdgParticle absPdg() const noexcept { return m_absPdg; } + + /// Get the hypothesized mass. + constexpr float mass() const noexcept { return m_mass; } + + /// Get the hypothesized absolute charge. + constexpr float absoluteCharge() const noexcept { + return m_chargeType.absQ(); + } + + /// Extracts the signed charge from the `q over p` track parameter using the + /// charge hypothesis. + /// + /// @param qOverP the `q over p` track parameter. + template + constexpr auto extractCharge(T qOverP) const noexcept { + return m_chargeType.extractCharge(qOverP); + } + + /// Extracts the signed charge from the `q over p` track parameter using the + /// charge hypothesis. + /// + /// @param qOverP the `q over p` track parameter. + template + constexpr auto extractMomentum(T qOverP) const noexcept { + return m_chargeType.extractMomentum(qOverP); + } + + /// Calculate the `q over p` track parameter with the given absolute momentum + /// and charge. + /// + /// @param momentum the absolute momentum. + /// @param signedQ the signed charge. + template + constexpr auto qOverP(P momentum, Q signedQ) const noexcept { + return m_chargeType.qOverP(momentum, signedQ); + } + + /// Get the hypothesized charge type. + constexpr const ChargeType& chargeType() const noexcept { + return m_chargeType; + } + + private: + PdgParticle m_absPdg; + float m_mass; + ChargeType m_chargeType; + + friend bool operator==(const GenericParticleHypothesis& lhs, + const GenericParticleHypothesis& rhs) { + return (lhs.m_absPdg == rhs.m_absPdg) && (lhs.m_mass == rhs.m_mass) && + (lhs.m_chargeType == rhs.m_chargeType); + } + friend bool operator!=(const GenericParticleHypothesis& lhs, + const GenericParticleHypothesis& rhs) { + return (lhs.m_absPdg != rhs.m_absPdg) || (lhs.m_mass != rhs.m_mass) || + (lhs.m_chargeType != rhs.m_chargeType); + } +}; + +} // namespace Acts diff --git a/Core/include/Acts/EventData/ParticleHypothesis.hpp b/Core/include/Acts/EventData/ParticleHypothesis.hpp new file mode 100644 index 00000000000..a4321c77cd5 --- /dev/null +++ b/Core/include/Acts/EventData/ParticleHypothesis.hpp @@ -0,0 +1,143 @@ +// 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/ParticleData.hpp" +#include "Acts/Definitions/PdgParticle.hpp" +#include "Acts/Definitions/Units.hpp" +#include "Acts/EventData/Charge.hpp" +#include "Acts/EventData/GenericParticleHypothesis.hpp" + +namespace Acts { + +// TODO In principle the factory methods could provide a reference to a static +// instance which would avoid copying the particle hypothesis and potentially +// save some memory. But constexpr+static seems to require C++2b extension. + +/// Specialized particle hypothesis for singly charged particles. +/// +/// @note This serves as a factory for common singly charge particles. +class SinglyChargedParticleHypothesis + : public GenericParticleHypothesis { + public: + constexpr SinglyChargedParticleHypothesis(PdgParticle absPdg, float mass) + : GenericParticleHypothesis(absPdg, mass, {}) {} + SinglyChargedParticleHypothesis(PdgParticle absPdg) + : GenericParticleHypothesis(absPdg) {} + + template + constexpr SinglyChargedParticleHypothesis( + const GenericParticleHypothesis& other) + : GenericParticleHypothesis(other) {} + + static SinglyChargedParticleHypothesis muon() { + return SinglyChargedParticleHypothesis(PdgParticle::eMuon); + } + static SinglyChargedParticleHypothesis pion() { + return SinglyChargedParticleHypothesis(PdgParticle::ePionPlus); + } + static SinglyChargedParticleHypothesis electron() { + return SinglyChargedParticleHypothesis(PdgParticle::ePionPlus); + } +}; + +/// Specialized particle hypothesis for neutral particles. +/// +/// @note This serves as a factory for common neutral particles. +class NeutralParticleHypothesis : public GenericParticleHypothesis { + public: + constexpr NeutralParticleHypothesis(PdgParticle absPdg, float mass) + : GenericParticleHypothesis(absPdg, mass, {}) {} + NeutralParticleHypothesis(PdgParticle absPdg) + : GenericParticleHypothesis(absPdg) {} + + template + constexpr NeutralParticleHypothesis( + const GenericParticleHypothesis& other) + : GenericParticleHypothesis(other) {} + + static NeutralParticleHypothesis photon() { + return NeutralParticleHypothesis(PdgParticle::eGamma); + } + static NeutralParticleHypothesis pion0() { + return NeutralParticleHypothesis(PdgParticle::ePionZero); + } +}; + +/// Specialized particle hypothesis for non-neutral particles. +/// +/// @note This serves as a factory for common non-neutral particles. +class NonNeutralChargedParticleHypothesis + : public GenericParticleHypothesis { + public: + constexpr NonNeutralChargedParticleHypothesis(PdgParticle absPdg, float mass, + NonNeutralCharge chargeType) + : GenericParticleHypothesis(absPdg, mass, chargeType) {} + NonNeutralChargedParticleHypothesis(PdgParticle absPdg) + : GenericParticleHypothesis(absPdg) {} + + template + constexpr NonNeutralChargedParticleHypothesis( + const GenericParticleHypothesis& other) + : GenericParticleHypothesis(other) {} + + static NonNeutralChargedParticleHypothesis muon() { + return SinglyChargedParticleHypothesis::muon(); + } + static NonNeutralChargedParticleHypothesis pion() { + return SinglyChargedParticleHypothesis::pion(); + } + static NonNeutralChargedParticleHypothesis electron() { + return SinglyChargedParticleHypothesis::electron(); + } + + static NonNeutralChargedParticleHypothesis pionLike(float absQ) { + return NonNeutralChargedParticleHypothesis(pion().absPdg(), pion().mass(), + absQ); + } +}; + +/// Specialized particle hypothesis for any kind of charged particles. +/// +/// @note This serves as a factory for common particles with any kind of charge. +class ParticleHypothesis : public GenericParticleHypothesis { + public: + constexpr ParticleHypothesis(PdgParticle absPdg, float mass, + AnyCharge chargeType) + : GenericParticleHypothesis(absPdg, mass, chargeType) {} + ParticleHypothesis(PdgParticle absPdg) : GenericParticleHypothesis(absPdg) {} + + template + constexpr ParticleHypothesis( + const GenericParticleHypothesis& other) + : GenericParticleHypothesis(other) {} + + static ParticleHypothesis muon() { + return SinglyChargedParticleHypothesis::muon(); + } + static ParticleHypothesis pion() { + return SinglyChargedParticleHypothesis::pion(); + } + static ParticleHypothesis electron() { + return SinglyChargedParticleHypothesis::electron(); + } + + static ParticleHypothesis photon() { + return NeutralParticleHypothesis::photon(); + } + static ParticleHypothesis pion0() { + return NeutralParticleHypothesis::pion0(); + } + + static ParticleHypothesis pionLike(float absQ) { + return ParticleHypothesis(pion().absPdg(), pion().mass(), absQ); + } +}; + +} // namespace Acts diff --git a/Tests/UnitTests/Core/EventData/CMakeLists.txt b/Tests/UnitTests/Core/EventData/CMakeLists.txt index 50948c86fe9..aa547c8736b 100644 --- a/Tests/UnitTests/Core/EventData/CMakeLists.txt +++ b/Tests/UnitTests/Core/EventData/CMakeLists.txt @@ -12,7 +12,7 @@ add_unittest(CorrectedTransformFreeToBound CorrectedTransformFreeToBoundTests.cp add_unittest(Track TrackTests.cpp) target_sources(ActsUnitTestTrack PUBLIC TrackTestsExtra.cpp) add_unittest(SourceLink SourceLinkTests.cpp) - add_unittest(TrackStatePropMask TrackStatePropMaskTests.cpp) +add_unittest(ParticleHypothesis ParticleHypothesisTests.cpp) add_non_compile_test(MultiTrajectory TrackContainerComplianceTests.cpp) diff --git a/Tests/UnitTests/Core/EventData/ChargeTests.cpp b/Tests/UnitTests/Core/EventData/ChargeTests.cpp index 7276170bfd0..393ff6b3d04 100644 --- a/Tests/UnitTests/Core/EventData/ChargeTests.cpp +++ b/Tests/UnitTests/Core/EventData/ChargeTests.cpp @@ -1,6 +1,6 @@ // This file is part of the Acts project. // -// Copyright (C) 2020 CERN for the benefit of the Acts project +// Copyright (C) 2020-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 @@ -21,6 +21,7 @@ static auto eps = std::numeric_limits::epsilon(); BOOST_TEST_DONT_PRINT_LOG_VALUE(Acts::Neutral) BOOST_TEST_DONT_PRINT_LOG_VALUE(Acts::SinglyCharged) +BOOST_TEST_DONT_PRINT_LOG_VALUE(Acts::NonNeutralCharge) BOOST_TEST_DONT_PRINT_LOG_VALUE(Acts::AnyCharge) BOOST_AUTO_TEST_SUITE(EventDataCharge) @@ -32,9 +33,11 @@ BOOST_AUTO_TEST_CASE(Constructibility) { BOOST_CHECK(std::is_nothrow_default_constructible_v); BOOST_CHECK(std::is_trivially_constructible_v); BOOST_CHECK(std::is_trivially_constructible_v); + // BOOST_CHECK(std::is_trivially_constructible_v); // BOOST_CHECK(std::is_trivially_constructible_v); BOOST_CHECK(std::is_nothrow_constructible_v); BOOST_CHECK(std::is_nothrow_constructible_v); + // BOOST_CHECK(std::is_nothrow_constructible_v); // BOOST_CHECK(std::is_nothrow_constructible_v); } @@ -47,9 +50,11 @@ BOOST_AUTO_TEST_CASE(Neutral) { BOOST_CHECK_EQUAL(q.extractCharge(-2.23), 0_e); CHECK_CLOSE_REL(q.extractMomentum(1 / 64_GeV), 64_GeV, eps); CHECK_CLOSE_REL(q.extractMomentum(1 / 128_MeV), 128_MeV, eps); + // negative inputs should not occur for neutral particles - // ensure that result is positive even in this invalid cases - CHECK_CLOSE_REL(q.extractMomentum(-1 / 128_MeV), 128_MeV, eps); + // the result is not defined but we check it anyways + // update: this is asserted now + // CHECK_CLOSE_REL(q.extractMomentum(-1 / 128_MeV), -128_MeV, eps); BOOST_CHECK_EQUAL(q, Acts::Neutral()); BOOST_CHECK_EQUAL(Acts::Neutral(), q); @@ -74,6 +79,40 @@ BOOST_AUTO_TEST_CASE(SinglyCharged) { BOOST_CHECK_EQUAL(Acts::SinglyCharged(1_e), q); } +BOOST_AUTO_TEST_CASE(NonNeutralChargeSingle) { + Acts::NonNeutralCharge q(1_e); + + BOOST_CHECK_EQUAL(q.extractCharge(1.23), 1_e); + BOOST_CHECK_EQUAL(q.extractCharge(2.54), 1_e); + BOOST_CHECK_EQUAL(q.extractCharge(-1.98), -1_e); + BOOST_CHECK_EQUAL(q.extractCharge(-2.23), -1_e); + CHECK_CLOSE_REL(q.extractMomentum(1_e / 64_GeV), 64_GeV, eps); + CHECK_CLOSE_REL(q.extractMomentum(1_e / 128_MeV), 128_MeV, eps); + CHECK_CLOSE_REL(q.extractMomentum(-1_e / 128_MeV), 128_MeV, eps); + + BOOST_CHECK_EQUAL(q, Acts::NonNeutralCharge(1_e)); + BOOST_CHECK_EQUAL(Acts::NonNeutralCharge(1_e), q); +} + +BOOST_AUTO_TEST_CASE(NonNeutralChargeMultiple) { + Acts::NonNeutralCharge q(3_e); + + BOOST_CHECK_EQUAL(q.extractCharge(1.23), 3_e); + BOOST_CHECK_EQUAL(q.extractCharge(2.54), 3_e); + BOOST_CHECK_EQUAL(q.extractCharge(-1.98), -3_e); + BOOST_CHECK_EQUAL(q.extractCharge(-2.23), -3_e); + CHECK_CLOSE_REL(q.extractMomentum(3_e / 64_GeV), 64_GeV, eps); + CHECK_CLOSE_REL(q.extractMomentum(3_e / 128_MeV), 128_MeV, eps); + CHECK_CLOSE_REL(q.extractMomentum(-3_e / 128_MeV), 128_MeV, eps); + + BOOST_CHECK(!(q == Acts::NonNeutralCharge(1_e))); + BOOST_CHECK(!(Acts::NonNeutralCharge(1_e) == q)); + BOOST_CHECK(!(q == Acts::NonNeutralCharge(2_e))); + BOOST_CHECK(!(Acts::NonNeutralCharge(2_e) == q)); + BOOST_CHECK(q == Acts::NonNeutralCharge(3_e)); + BOOST_CHECK(Acts::NonNeutralCharge(3_e) == q); +} + BOOST_AUTO_TEST_CASE(AnyChargeNeutral) { Acts::AnyCharge q(0_e); @@ -83,9 +122,10 @@ BOOST_AUTO_TEST_CASE(AnyChargeNeutral) { BOOST_CHECK_EQUAL(q.extractCharge(-2.23), 0_e); CHECK_CLOSE_REL(q.extractMomentum(1 / 64_GeV), 64_GeV, eps); CHECK_CLOSE_REL(q.extractMomentum(1 / 128_MeV), 128_MeV, eps); + // negative inputs should not occur for neutral particles - // ensure that result is positive even in this invalid cases - CHECK_CLOSE_REL(q.extractMomentum(-1 / 128_MeV), 128_MeV, eps); + // the result is not defined but we check it anyways + CHECK_CLOSE_REL(q.extractMomentum(-1 / 128_MeV), -128_MeV, eps); BOOST_CHECK(q == Acts::AnyCharge(0_e)); BOOST_CHECK(Acts::AnyCharge(0_e) == q); @@ -96,7 +136,6 @@ BOOST_AUTO_TEST_CASE(AnyChargeNeutral) { } BOOST_AUTO_TEST_CASE(AnyChargeSingle) { - // takes only the charge magnitude as input Acts::AnyCharge q(1_e); BOOST_CHECK_EQUAL(q.extractCharge(1.23), 1_e); @@ -116,7 +155,6 @@ BOOST_AUTO_TEST_CASE(AnyChargeSingle) { } BOOST_AUTO_TEST_CASE(AnyChargeMultiple) { - // takes only the charge magnitude as input Acts::AnyCharge q(3_e); BOOST_CHECK_EQUAL(q.extractCharge(1.23), 3_e); diff --git a/Tests/UnitTests/Core/EventData/ParticleHypothesisTests.cpp b/Tests/UnitTests/Core/EventData/ParticleHypothesisTests.cpp new file mode 100644 index 00000000000..372d364e7f7 --- /dev/null +++ b/Tests/UnitTests/Core/EventData/ParticleHypothesisTests.cpp @@ -0,0 +1,111 @@ +// 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/Units.hpp" +#include "Acts/EventData/Charge.hpp" +#include "Acts/EventData/ParticleHypothesis.hpp" +#include "Acts/Tests/CommonHelpers/FloatComparisons.hpp" + +#include +#include + +using namespace Acts::UnitLiterals; + +static auto eps = std::numeric_limits::epsilon(); + +BOOST_AUTO_TEST_SUITE(EventDataParticleHypothesis) + +BOOST_AUTO_TEST_CASE(Neutral) { + auto p = Acts::NeutralParticleHypothesis::pion0(); + + BOOST_CHECK_EQUAL(p.extractCharge(1.23), 0_e); + BOOST_CHECK_EQUAL(p.extractCharge(2.54), 0_e); + BOOST_CHECK_EQUAL(p.extractCharge(-1.98), 0_e); + BOOST_CHECK_EQUAL(p.extractCharge(-2.23), 0_e); + CHECK_CLOSE_REL(p.extractMomentum(1 / 64_GeV), 64_GeV, eps); + CHECK_CLOSE_REL(p.extractMomentum(1 / 128_MeV), 128_MeV, eps); +} + +BOOST_AUTO_TEST_CASE(SinglyCharged) { + auto p = Acts::SinglyChargedParticleHypothesis::pion(); + + BOOST_CHECK_EQUAL(p.extractCharge(1.23), 1_e); + BOOST_CHECK_EQUAL(p.extractCharge(2.54), 1_e); + BOOST_CHECK_EQUAL(p.extractCharge(-1.98), -1_e); + BOOST_CHECK_EQUAL(p.extractCharge(-2.23), -1_e); + CHECK_CLOSE_REL(p.extractMomentum(1_e / 64_GeV), 64_GeV, eps); + CHECK_CLOSE_REL(p.extractMomentum(1_e / 128_MeV), 128_MeV, eps); + CHECK_CLOSE_REL(p.extractMomentum(-1_e / 128_MeV), 128_MeV, eps); +} + +BOOST_AUTO_TEST_CASE(NonNeutralChargeSingle) { + auto p = Acts::NonNeutralChargedParticleHypothesis::pion(); + + BOOST_CHECK_EQUAL(p.extractCharge(1.23), 1_e); + BOOST_CHECK_EQUAL(p.extractCharge(2.54), 1_e); + BOOST_CHECK_EQUAL(p.extractCharge(-1.98), -1_e); + BOOST_CHECK_EQUAL(p.extractCharge(-2.23), -1_e); + CHECK_CLOSE_REL(p.extractMomentum(1_e / 64_GeV), 64_GeV, eps); + CHECK_CLOSE_REL(p.extractMomentum(1_e / 128_MeV), 128_MeV, eps); + CHECK_CLOSE_REL(p.extractMomentum(-1_e / 128_MeV), 128_MeV, eps); +} + +BOOST_AUTO_TEST_CASE(NonNeutralChargeMultiple) { + auto p = Acts::NonNeutralChargedParticleHypothesis::pionLike(3_e); + + BOOST_CHECK_EQUAL(p.extractCharge(1.23), 3_e); + BOOST_CHECK_EQUAL(p.extractCharge(2.54), 3_e); + BOOST_CHECK_EQUAL(p.extractCharge(-1.98), -3_e); + BOOST_CHECK_EQUAL(p.extractCharge(-2.23), -3_e); + CHECK_CLOSE_REL(p.extractMomentum(3_e / 64_GeV), 64_GeV, eps); + CHECK_CLOSE_REL(p.extractMomentum(3_e / 128_MeV), 128_MeV, eps); + CHECK_CLOSE_REL(p.extractMomentum(-3_e / 128_MeV), 128_MeV, eps); +} + +BOOST_AUTO_TEST_CASE(AnyChargeNeutral) { + auto p = Acts::ParticleHypothesis::pion0(); + + BOOST_CHECK_EQUAL(p.extractCharge(1.23), 0_e); + BOOST_CHECK_EQUAL(p.extractCharge(2.54), 0_e); + BOOST_CHECK_EQUAL(p.extractCharge(-1.98), 0_e); + BOOST_CHECK_EQUAL(p.extractCharge(-2.23), 0_e); + CHECK_CLOSE_REL(p.extractMomentum(1 / 64_GeV), 64_GeV, eps); + CHECK_CLOSE_REL(p.extractMomentum(1 / 128_MeV), 128_MeV, eps); + + // negative inputs should not occur for neutral particles + // the result is not defined but we check it anyways + CHECK_CLOSE_REL(p.extractMomentum(-1 / 128_MeV), -128_MeV, eps); +} + +BOOST_AUTO_TEST_CASE(AnyChargeSingle) { + auto p = Acts::ParticleHypothesis::pion(); + + BOOST_CHECK_EQUAL(p.extractCharge(1.23), 1_e); + BOOST_CHECK_EQUAL(p.extractCharge(2.54), 1_e); + BOOST_CHECK_EQUAL(p.extractCharge(-1.98), -1_e); + BOOST_CHECK_EQUAL(p.extractCharge(-2.23), -1_e); + CHECK_CLOSE_REL(p.extractMomentum(1_e / 64_GeV), 64_GeV, eps); + CHECK_CLOSE_REL(p.extractMomentum(1_e / 128_MeV), 128_MeV, eps); + CHECK_CLOSE_REL(p.extractMomentum(-1_e / 128_MeV), 128_MeV, eps); +} + +BOOST_AUTO_TEST_CASE(AnyChargeMultiple) { + auto p = Acts::ParticleHypothesis::pionLike(3_e); + + BOOST_CHECK_EQUAL(p.extractCharge(1.23), 3_e); + BOOST_CHECK_EQUAL(p.extractCharge(2.54), 3_e); + BOOST_CHECK_EQUAL(p.extractCharge(-1.98), -3_e); + BOOST_CHECK_EQUAL(p.extractCharge(-2.23), -3_e); + CHECK_CLOSE_REL(p.extractMomentum(3_e / 64_GeV), 64_GeV, eps); + CHECK_CLOSE_REL(p.extractMomentum(3_e / 128_MeV), 128_MeV, eps); + CHECK_CLOSE_REL(p.extractMomentum(-3_e / 128_MeV), 128_MeV, eps); +} + +BOOST_AUTO_TEST_SUITE_END() From 4bc20bfb8e7eaf0611de3335cf6f7f41ec5e93ef Mon Sep 17 00:00:00 2001 From: Benjamin Huth <37871400+benjaminhuth@users.noreply.github.com> Date: Fri, 28 Jul 2023 17:04:42 +0200 Subject: [PATCH 07/21] feat: Make patched python constructors accept pathlib Paths (#2324) It always annoyed me a bit, that I need to convert every path manually to string when working with the examples framework in python. This does this implicitly in the patched constructors. --- Examples/Python/python/acts/examples/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Examples/Python/python/acts/examples/__init__.py b/Examples/Python/python/acts/examples/__init__.py index f6f50272922..8a12d3bbd0f 100644 --- a/Examples/Python/python/acts/examples/__init__.py +++ b/Examples/Python/python/acts/examples/__init__.py @@ -370,6 +370,9 @@ def __init__(self, *args, **kwargs): for k, v in kwargs.items(): if not hasattr(cfg, k): raise ValueError(f"Sequencer.Config does not have field {k}") + if isinstance(v, Path): + v = str(v) + setattr(cfg, k, v) super().__init__(cfg) From d85d3fd22fba61e455d3ec7f257872f024b9a3d0 Mon Sep 17 00:00:00 2001 From: Andreas Stefl Date: Sun, 30 Jul 2023 09:35:31 +0200 Subject: [PATCH 08/21] fix: Fix `BinningValue` conversions (#2333) In the ATLAS debug build, we're getting failures in the ActsEventCnv test: ``` ActsTrackingGeometrySvc FATAL in sysInitialize(): standard std::exception is caught ActsTrackingGeometrySvc ERROR [json.exception.type_error.302] type must be number, but is string ServiceManager ERROR Unable to initialize service "ActsTrackingGeometrySvc" ``` This is because some compilation units in Plugins/Json try to convert a BinningValue without including the header UtilitiesJsonConverter.hpp which has the NLOHMANN_JSON_SERIALIZE_ENUM declaration for BinningValue. Thus, the conversion can work differently depending on the compilation unit, and the behavior can also depend on what gets inlined, so can differ between the optimized and debug build. fixes https://github.com/acts-project/acts/issues/2331 Co-authored-by: scott-snyder <21222380+scott-snyder@users.noreply.github.com> --- Plugins/Json/src/IndexedSurfacesJsonConverter.cpp | 1 + Plugins/Json/src/PortalJsonConverter.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/Plugins/Json/src/IndexedSurfacesJsonConverter.cpp b/Plugins/Json/src/IndexedSurfacesJsonConverter.cpp index 84455904de2..5bf4f33c8be 100644 --- a/Plugins/Json/src/IndexedSurfacesJsonConverter.cpp +++ b/Plugins/Json/src/IndexedSurfacesJsonConverter.cpp @@ -11,6 +11,7 @@ #include "Acts/Detector/detail/GridAxisGenerators.hpp" #include "Acts/Navigation/NavigationStateUpdators.hpp" #include "Acts/Plugins/Json/GridJsonConverter.hpp" +#include "Acts/Plugins/Json/UtilitiesJsonConverter.hpp" #include #include diff --git a/Plugins/Json/src/PortalJsonConverter.cpp b/Plugins/Json/src/PortalJsonConverter.cpp index dd0b3dc98c0..82524e69c2a 100644 --- a/Plugins/Json/src/PortalJsonConverter.cpp +++ b/Plugins/Json/src/PortalJsonConverter.cpp @@ -14,6 +14,7 @@ #include "Acts/Navigation/DetectorVolumeUpdators.hpp" #include "Acts/Plugins/Json/DetrayJsonHelper.hpp" #include "Acts/Plugins/Json/SurfaceJsonConverter.hpp" +#include "Acts/Plugins/Json/UtilitiesJsonConverter.hpp" #include "Acts/Surfaces/CylinderBounds.hpp" #include "Acts/Surfaces/CylinderSurface.hpp" #include "Acts/Surfaces/DiscSurface.hpp" From e853b17a7b25d80dce9cbc588d152ebb5f484ac8 Mon Sep 17 00:00:00 2001 From: Paul Gessinger Date: Mon, 31 Jul 2023 10:21:09 +0200 Subject: [PATCH 09/21] ci: Explicitly trigger current commit hash for Athena build --- .github/workflows/trigger_athena.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/trigger_athena.yml b/.github/workflows/trigger_athena.yml index d66775227de..c5696ee7a3b 100644 --- a/.github/workflows/trigger_athena.yml +++ b/.github/workflows/trigger_athena.yml @@ -12,4 +12,5 @@ jobs: curl -X POST --fail -F token=${{ secrets.GITLAB_ATHENA_BUILD_TRIGGER_TOKEN}} -F ref=main + --form variables[ACTS_REF]="${{ github.ref }}" https://gitlab.cern.ch/api/v4/projects/153873/trigger/pipeline From b01a2ddf15ec39a4d20014d621e182029b05efd8 Mon Sep 17 00:00:00 2001 From: Paul Gessinger Date: Mon, 31 Jul 2023 10:26:26 +0200 Subject: [PATCH 10/21] ci: Athena trigger uses github.sha --- .github/workflows/trigger_athena.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/trigger_athena.yml b/.github/workflows/trigger_athena.yml index c5696ee7a3b..e2fc497d171 100644 --- a/.github/workflows/trigger_athena.yml +++ b/.github/workflows/trigger_athena.yml @@ -12,5 +12,5 @@ jobs: curl -X POST --fail -F token=${{ secrets.GITLAB_ATHENA_BUILD_TRIGGER_TOKEN}} -F ref=main - --form variables[ACTS_REF]="${{ github.ref }}" + --form variables[ACTS_REF]="${{ github.sha }}" https://gitlab.cern.ch/api/v4/projects/153873/trigger/pipeline From 9699885f771ca2913128e9e52e0bbdb55435a080 Mon Sep 17 00:00:00 2001 From: Paul Gessinger Date: Mon, 31 Jul 2023 10:33:19 +0200 Subject: [PATCH 11/21] ci: Athena job: use separate variable Sorry for debugging on the main branch. --- .github/workflows/trigger_athena.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/trigger_athena.yml b/.github/workflows/trigger_athena.yml index e2fc497d171..7e01a3dd9f7 100644 --- a/.github/workflows/trigger_athena.yml +++ b/.github/workflows/trigger_athena.yml @@ -12,5 +12,5 @@ jobs: curl -X POST --fail -F token=${{ secrets.GITLAB_ATHENA_BUILD_TRIGGER_TOKEN}} -F ref=main - --form variables[ACTS_REF]="${{ github.sha }}" + --form variables[SOURCE_SHA]="${{ github.sha }}" https://gitlab.cern.ch/api/v4/projects/153873/trigger/pipeline From ced7cb7e8f2c9ddd3190c5c05db59f0b12e0af67 Mon Sep 17 00:00:00 2001 From: Benjamin Huth <37871400+benjaminhuth@users.noreply.github.com> Date: Mon, 31 Jul 2023 13:16:39 +0200 Subject: [PATCH 12/21] feat: Root SimHit Reader and unit test (#2330) Co-authored-by: Andreas Stefl Co-authored-by: Benjamin Huth Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- Examples/Io/Root/CMakeLists.txt | 1 + .../ActsExamples/Io/Root/RootSimHitReader.hpp | 110 ++++++++++++ Examples/Io/Root/src/RootSimHitReader.cpp | 168 ++++++++++++++++++ Examples/Python/src/Input.cpp | 5 + .../CommonHelpers/WhiteBoardUtilities.hpp | 105 +++++++++++ Tests/UnitTests/Examples/Io/CMakeLists.txt | 1 + .../UnitTests/Examples/Io/Root/CMakeLists.txt | 3 + .../Io/Root/SimhitReaderWriterTests.cpp | 113 ++++++++++++ 8 files changed, 506 insertions(+) create mode 100644 Examples/Io/Root/include/ActsExamples/Io/Root/RootSimHitReader.hpp create mode 100644 Examples/Io/Root/src/RootSimHitReader.cpp create mode 100644 Tests/CommonHelpers/Acts/Tests/CommonHelpers/WhiteBoardUtilities.hpp create mode 100644 Tests/UnitTests/Examples/Io/Root/CMakeLists.txt create mode 100644 Tests/UnitTests/Examples/Io/Root/SimhitReaderWriterTests.cpp diff --git a/Examples/Io/Root/CMakeLists.txt b/Examples/Io/Root/CMakeLists.txt index 9814a67b357..a2a82ed1ba7 100644 --- a/Examples/Io/Root/CMakeLists.txt +++ b/Examples/Io/Root/CMakeLists.txt @@ -10,6 +10,7 @@ add_library( src/RootParticleReader.cpp src/RootPropagationStepsWriter.cpp src/RootSimHitWriter.cpp + src/RootSimHitReader.cpp src/RootSpacepointWriter.cpp src/RootTrackParameterWriter.cpp src/RootTrajectoryStatesWriter.cpp diff --git a/Examples/Io/Root/include/ActsExamples/Io/Root/RootSimHitReader.hpp b/Examples/Io/Root/include/ActsExamples/Io/Root/RootSimHitReader.hpp new file mode 100644 index 00000000000..9bd38da6876 --- /dev/null +++ b/Examples/Io/Root/include/ActsExamples/Io/Root/RootSimHitReader.hpp @@ -0,0 +1,110 @@ +// 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/Propagator/MaterialInteractor.hpp" +#include "Acts/Utilities/Logger.hpp" +#include "ActsExamples/EventData/SimHit.hpp" +#include "ActsExamples/Framework/DataHandle.hpp" +#include "ActsExamples/Framework/IReader.hpp" +#include "ActsExamples/Framework/ProcessCode.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +class TChain; + +namespace ActsExamples { +struct AlgorithmContext; + +/// @class RootParticleReader +/// +/// @brief Reads in Particles information from a root file +class RootSimHitReader : public IReader { + public: + /// @brief The nested configuration struct + struct Config { + /// name of the whiteboard entry + std::string simHitCollection = "simhits"; + /// name of the output tree + std::string treeName = "hits"; + ///< The name of the input file + std::string filePath; + /// Whether the events are ordered or not + bool orderedEvents = true; + }; + + RootSimHitReader(const RootSimHitReader &) = delete; + RootSimHitReader(const RootSimHitReader &&) = delete; + + /// Constructor + /// @param config The Configuration struct + RootSimHitReader(const Config &config, Acts::Logging::Level level); + + /// Framework name() method + std::string name() const override { return "RootSimHitReader"; } + + /// Return the available events range. + std::pair availableEvents() const override; + + /// Read out data from the input stream + /// + /// @param context The algorithm context + ProcessCode read(const ActsExamples::AlgorithmContext &context) override; + + /// Readonly access to the config + const Config &config() const { return m_cfg; } + + private: + /// Private access to the logging instance + const Acts::Logger &logger() const { return *m_logger; } + + /// The config class + Config m_cfg; + + WriteDataHandle m_outputSimHits{this, "OutputSimHits"}; + std::unique_ptr m_logger; + + /// mutex used to protect multi-threaded reads + std::mutex m_read_mutex; + + /// Vector of {eventNr, entryMin, entryMax} + std::vector> m_eventMap; + + /// The input tree name + TChain *m_inputChain = nullptr; + + /// The keys we have in the ROOT file + constexpr static std::array m_floatKeys = { + "tx", "ty", "tz", "tt", "tpx", "tpy", + "tpz", "te", "deltapx", "deltapy", "deltapz", "deltae"}; + constexpr static std::array m_uint64Keys = {"geometry_id", + "particle_id"}; + constexpr static std::array m_uint32Keys = { + "event_id", "volume_id", "boundary_id", + "layer_id", "approach_id", "sensitive_id"}; + constexpr static std::array m_int32Keys = {"index"}; + + std::unordered_map m_floatColumns; + std::unordered_map m_uint32Columns; + std::unordered_map m_int32Columns; + + // For some reason I need to use here `unsigned long long` instead of + // `uint64_t` to prevent a internal ROOT type mismatch... + std::unordered_map m_uint64Columns; +}; + +} // namespace ActsExamples diff --git a/Examples/Io/Root/src/RootSimHitReader.cpp b/Examples/Io/Root/src/RootSimHitReader.cpp new file mode 100644 index 00000000000..2cce943f7de --- /dev/null +++ b/Examples/Io/Root/src/RootSimHitReader.cpp @@ -0,0 +1,168 @@ +// 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 "ActsExamples/Io/Root/RootSimHitReader.hpp" + +#include "Acts/Definitions/PdgParticle.hpp" +#include "Acts/Utilities/Logger.hpp" +#include "ActsExamples/EventData/SimParticle.hpp" +#include "ActsExamples/Framework/AlgorithmContext.hpp" +#include "ActsFatras/EventData/ProcessType.hpp" + +#include +#include +#include +#include + +#include +#include + +ActsExamples::RootSimHitReader::RootSimHitReader( + const ActsExamples::RootSimHitReader::Config& config, + Acts::Logging::Level level) + : ActsExamples::IReader(), + m_cfg(config), + m_logger(Acts::getDefaultLogger(name(), level)) { + m_inputChain = new TChain(m_cfg.treeName.c_str()); + + if (m_cfg.filePath.empty()) { + throw std::invalid_argument("Missing input filename"); + } + if (m_cfg.treeName.empty()) { + throw std::invalid_argument("Missing tree name"); + } + + m_outputSimHits.initialize(m_cfg.simHitCollection); + + // Set the branches + int f = 0; + auto setBranches = [&](const auto& keys, auto& columns) { + for (auto key : keys) { + columns.insert({key, f++}); + } + for (auto key : keys) { + m_inputChain->SetBranchAddress(key, &columns.at(key)); + } + }; + + setBranches(m_floatKeys, m_floatColumns); + setBranches(m_uint32Keys, m_uint32Columns); + setBranches(m_uint64Keys, m_uint64Columns); + setBranches(m_int32Keys, m_int32Columns); + + // add file to the input chain + m_inputChain->Add(m_cfg.filePath.c_str()); + m_inputChain->LoadTree(0); + ACTS_DEBUG("Adding File " << m_cfg.filePath << " to tree '" << m_cfg.treeName + << "'."); + + // Because each hit is stored in a single entry in the root file, we need to + // scan the file first for the positions of the events in the file in order to + // efficiently read the events later on. + // TODO change the file format to store one event per entry + + // Disable all branches and only enable event-id for a first scan of the file + m_inputChain->SetBranchStatus("*", false); + m_inputChain->SetBranchStatus("event_id", true); + + auto nEntries = static_cast(m_inputChain->GetEntriesFast()); + + // Add the first entry + m_inputChain->GetEntry(0); + m_eventMap.push_back({m_uint32Columns.at("event_id"), 0ul, 0ul}); + + // Go through all entries and store the position of new events + for (auto i = 1ul; i < nEntries; ++i) { + m_inputChain->GetEntry(i); + const auto evtId = m_uint32Columns.at("event_id"); + + if (evtId != std::get<0>(m_eventMap.back())) { + std::get<2>(m_eventMap.back()) = i; + m_eventMap.push_back({evtId, i, i}); + } + } + + std::get<2>(m_eventMap.back()) = nEntries; + + // Sort by event id + std::sort(m_eventMap.begin(), m_eventMap.end(), + [](const auto& a, const auto& b) { + return std::get<0>(a) < std::get<0>(b); + }); + + // Re-Enable all branches + m_inputChain->SetBranchStatus("*", true); +} + +std::pair ActsExamples::RootSimHitReader::availableEvents() + const { + return {std::get<0>(m_eventMap.front()), std::get<0>(m_eventMap.back())}; +} + +ActsExamples::ProcessCode ActsExamples::RootSimHitReader::read( + const ActsExamples::AlgorithmContext& context) { + auto it = std::find_if( + m_eventMap.begin(), m_eventMap.end(), + [&](const auto& a) { return std::get<0>(a) == context.eventNumber; }); + + if (m_inputChain == nullptr || it == m_eventMap.end()) { + ACTS_ERROR("Cannot read hits of event " << context.eventNumber); + return ActsExamples::ProcessCode::ABORT; + } + + // lock the mutex + std::lock_guard lock(m_read_mutex); + + ACTS_DEBUG("Reading event: " << std::get<0>(*it) + << " stored in entries: " << std::get<1>(*it) + << " - " << std::get<2>(*it)); + + SimHitContainer hits; + for (auto entry = std::get<1>(*it); entry < std::get<2>(*it); ++entry) { + m_inputChain->GetEntry(entry); + + auto eventId = m_uint32Columns.at("event_id"); + if (eventId != context.eventNumber) { + break; + } + + const Acts::GeometryIdentifier geoid = m_uint64Columns.at("geometry_id"); + const SimBarcode pid = m_uint64Columns.at("particle_id"); + const auto index = m_int32Columns.at("index"); + + const Acts::Vector4 pos4 = { + m_floatColumns.at("tx") * Acts::UnitConstants::mm, + m_floatColumns.at("ty") * Acts::UnitConstants::mm, + m_floatColumns.at("tz") * Acts::UnitConstants::mm, + m_floatColumns.at("tt") * Acts::UnitConstants::ns, + }; + + const Acts::Vector4 before4 = { + m_floatColumns.at("tpx") * Acts::UnitConstants::GeV, + m_floatColumns.at("tpy") * Acts::UnitConstants::GeV, + m_floatColumns.at("tpz") * Acts::UnitConstants::GeV, + m_floatColumns.at("te") * Acts::UnitConstants::GeV, + }; + + const Acts::Vector4 delta = { + m_floatColumns.at("deltapx") * Acts::UnitConstants::GeV, + m_floatColumns.at("deltapy") * Acts::UnitConstants::GeV, + m_floatColumns.at("deltapz") * Acts::UnitConstants::GeV, + m_floatColumns.at("deltae") * Acts::UnitConstants::GeV, + }; + + SimHit hit(geoid, pid, pos4, before4, before4 + delta, index); + + hits.insert(hit); + } + + m_outputSimHits(context, std::move(hits)); + + // Return success flag + return ActsExamples::ProcessCode::SUCCESS; +} diff --git a/Examples/Python/src/Input.cpp b/Examples/Python/src/Input.cpp index 1f91610fa4e..c73bed7f28b 100644 --- a/Examples/Python/src/Input.cpp +++ b/Examples/Python/src/Input.cpp @@ -17,6 +17,7 @@ #include "ActsExamples/Io/Root/RootAthenaNTupleReader.hpp" #include "ActsExamples/Io/Root/RootMaterialTrackReader.hpp" #include "ActsExamples/Io/Root/RootParticleReader.hpp" +#include "ActsExamples/Io/Root/RootSimHitReader.hpp" #include "ActsExamples/Io/Root/RootTrajectorySummaryReader.hpp" #include @@ -81,5 +82,9 @@ void addInput(Context& ctx) { inputFilePath, outputTrackParameters, outputTruthVtxParameters, outputRecoVtxParameters, outputBeamspotConstraint); + + ACTS_PYTHON_DECLARE_READER(ActsExamples::RootSimHitReader, mex, + "RootSimHitReader", treeName, filePath, + simHitCollection); } } // namespace Acts::Python diff --git a/Tests/CommonHelpers/Acts/Tests/CommonHelpers/WhiteBoardUtilities.hpp b/Tests/CommonHelpers/Acts/Tests/CommonHelpers/WhiteBoardUtilities.hpp new file mode 100644 index 00000000000..461f90759d5 --- /dev/null +++ b/Tests/CommonHelpers/Acts/Tests/CommonHelpers/WhiteBoardUtilities.hpp @@ -0,0 +1,105 @@ +// 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 "ActsExamples/Framework/DataHandle.hpp" +#include "ActsExamples/Framework/SequenceElement.hpp" +#include "ActsExamples/Framework/WhiteBoard.hpp" + +#include + +namespace Acts::Test { +struct DummySequenceElement : public ActsExamples::SequenceElement { + ActsExamples::ProcessCode initialize() override { return {}; }; + ActsExamples::ProcessCode finalize() override { return {}; }; + ActsExamples::ProcessCode internalExecute( + const ActsExamples::AlgorithmContext & /*context*/) override { + return {}; + }; + std::string name() const override { return {}; }; +}; + +template +void addToWhiteBoard(std::string name, T data, ActsExamples::WhiteBoard &wb) { + DummySequenceElement dummyElement; + + ActsExamples::WriteDataHandle handle(&dummyElement, name + "handle"); + handle.initialize(name); + handle(wb, std::move(data)); +} + +template +T getFromWhiteBoard(std::string name, ActsExamples::WhiteBoard &wb) { + DummySequenceElement dummyElement; + + ActsExamples::ReadDataHandle handle(&dummyElement, name + "handle"); + handle.initialize(name); + return handle(wb); +} + +template , + typename str_tuple_t = std::tuple<>> +struct GenericReadWriteTool { + val_tuple_t tuple; + str_tuple_t strings; + + constexpr static std::size_t kSize = std::tuple_size_v; + static_assert(kSize == std::tuple_size_v); + + template + auto add(const std::string &name, T value) { + auto newTuple = std::tuple_cat(tuple, std::tuple{value}); + auto newStrings = std::tuple_cat(strings, std::tuple{name}); + + GenericReadWriteTool newInstance; + newInstance.tuple = std::move(newTuple); + newInstance.strings = std::move(newStrings); + + return newInstance; + } + + template + auto write(writer_t &writer, std::size_t eventId = 0) { + ActsExamples::WhiteBoard board; + ActsExamples::AlgorithmContext ctx(0, eventId, board); + + auto add = [&](auto &self, auto N) { + if constexpr (N() < kSize) { + addToWhiteBoard(std::get(strings), std::get(tuple), board); + self(self, std::integral_constant{}); + } + }; + + add(add, std::integral_constant{}); + + writer.internalExecute(ctx); + } + + template + auto read(reader_t &reader, std::size_t eventId = 0) { + ActsExamples::WhiteBoard board; + ActsExamples::AlgorithmContext ctx(0, eventId, board); + + reader.internalExecute(ctx); + + auto get = [&](auto &self, auto res, auto N) { + if constexpr (N() < kSize) { + using T = std::tuple_element_t; + auto val = getFromWhiteBoard(std::get(strings), board); + return self(self, std::tuple_cat(res, std::make_tuple(val)), + std::integral_constant{}); + } else { + return res; + } + }; + + return get(get, std::tuple<>{}, std::integral_constant{}); + } +}; +} // namespace Acts::Test diff --git a/Tests/UnitTests/Examples/Io/CMakeLists.txt b/Tests/UnitTests/Examples/Io/CMakeLists.txt index fd13a837780..f4a992fe62c 100644 --- a/Tests/UnitTests/Examples/Io/CMakeLists.txt +++ b/Tests/UnitTests/Examples/Io/CMakeLists.txt @@ -1 +1,2 @@ add_subdirectory_if(Json ACTS_BUILD_PLUGIN_JSON) +add_subdirectory(Root) diff --git a/Tests/UnitTests/Examples/Io/Root/CMakeLists.txt b/Tests/UnitTests/Examples/Io/Root/CMakeLists.txt new file mode 100644 index 00000000000..c94c393d6ca --- /dev/null +++ b/Tests/UnitTests/Examples/Io/Root/CMakeLists.txt @@ -0,0 +1,3 @@ +set(unittest_extra_libraries ActsExamplesIoRoot) + +add_unittest(RootSimhitReaderWriter SimhitReaderWriterTests.cpp) diff --git a/Tests/UnitTests/Examples/Io/Root/SimhitReaderWriterTests.cpp b/Tests/UnitTests/Examples/Io/Root/SimhitReaderWriterTests.cpp new file mode 100644 index 00000000000..ab321185b5b --- /dev/null +++ b/Tests/UnitTests/Examples/Io/Root/SimhitReaderWriterTests.cpp @@ -0,0 +1,113 @@ +// 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/Tests/CommonHelpers/FloatComparisons.hpp" +#include "Acts/Tests/CommonHelpers/WhiteBoardUtilities.hpp" +#include "Acts/Utilities/Zip.hpp" +#include "ActsExamples/EventData/SimHit.hpp" +#include "ActsExamples/Io/Root/RootSimHitReader.hpp" +#include "ActsExamples/Io/Root/RootSimHitWriter.hpp" + +#include +#include +#include + +using namespace ActsExamples; +using namespace Acts::Test; + +std::mt19937 gen(23); + +auto makeTestSimhits(std::size_t nSimHits) { + std::uniform_int_distribution distIds( + 1, std::numeric_limits::max()); + std::uniform_int_distribution distIndex(1, 20); + + SimHitContainer simhits; + for (auto i = 0ul; i < nSimHits; ++i) { + Acts::GeometryIdentifier geoid(distIds(gen)); + SimBarcode pid(distIds(gen)); + + Acts::Vector4 pos4 = Acts::Vector4::Random(); + Acts::Vector4 before4 = Acts::Vector4::Random(); + Acts::Vector4 after4 = Acts::Vector4::Random(); + + auto index = distIndex(gen); + + simhits.insert(SimHit(geoid, pid, pos4, before4, after4, index)); + } + + return simhits; +} + +BOOST_AUTO_TEST_SUITE(RootSimHitReaderWriter) + +BOOST_AUTO_TEST_CASE(RoundTripTest) { + //////////////////////////// + // Create some dummy data // + //////////////////////////// + auto simhits1 = makeTestSimhits(20); + auto simhits2 = makeTestSimhits(15); + + /////////// + // Write // + /////////// + RootSimHitWriter::Config writerConfig; + writerConfig.inputSimHits = "hits"; + writerConfig.filePath = "./testhits.root"; + + RootSimHitWriter writer(writerConfig, Acts::Logging::WARNING); + + auto readWriteTool = + GenericReadWriteTool<>().add(writerConfig.inputSimHits, simhits1); + + // Write two different events + readWriteTool.write(writer, 11); + + std::get<0>(readWriteTool.tuple) = simhits2; + readWriteTool.write(writer, 22); + + writer.finalize(); + + ////////// + // Read // + ////////// + RootSimHitReader::Config readerConfig; + readerConfig.simHitCollection = "hits"; + readerConfig.filePath = "./testhits.root"; + + RootSimHitReader reader(readerConfig, Acts::Logging::WARNING); + // Read two different events + const auto [hitsRead2] = readWriteTool.read(reader, 22); + const auto [hitsRead1] = readWriteTool.read(reader, 11); + reader.finalize(); + + /////////// + // Check // + /////////// + + auto check = [](const auto &testhits, const auto &refhits, auto tol) { + BOOST_CHECK(testhits.size() == refhits.size()); + + for (const auto &[ref, test] : Acts::zip(refhits, testhits)) { + CHECK_CLOSE_ABS(test.fourPosition(), ref.fourPosition(), tol); + CHECK_CLOSE_ABS(test.momentum4After(), ref.momentum4After(), tol); + CHECK_CLOSE_ABS(test.momentum4Before(), ref.momentum4Before(), tol); + + BOOST_CHECK(ref.geometryId() == test.geometryId()); + BOOST_CHECK(ref.particleId() == test.particleId()); + BOOST_CHECK(ref.index() == test.index()); + } + }; + + check(hitsRead1, simhits1, 1.e-6); + check(hitsRead2, simhits2, 1.e-6); +} + +BOOST_AUTO_TEST_SUITE_END() From 61df9453dd70056b9f26d7b59bad967c332445bd Mon Sep 17 00:00:00 2001 From: Benjamin Huth <37871400+benjaminhuth@users.noreply.github.com> Date: Tue, 1 Aug 2023 13:31:07 +0200 Subject: [PATCH 13/21] feat: Really make patched python constructors accept pathlib Paths (#2335) Co-authored-by: Benjamin Huth --- Examples/Python/python/acts/_adapter.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Examples/Python/python/acts/_adapter.py b/Examples/Python/python/acts/_adapter.py index 7654431e5cf..45ded497c49 100644 --- a/Examples/Python/python/acts/_adapter.py +++ b/Examples/Python/python/acts/_adapter.py @@ -1,6 +1,7 @@ import inspect import functools from typing import Optional, Callable, Dict, Any +from pathlib import Path import acts @@ -23,6 +24,9 @@ def wrapped(self, *args, **kwargs): cfg = type(self).Config() _kwargs = {} for k, v in kwargs.items(): + if isinstance(v, Path): + v = str(v) + if hasattr(cfg, k): setattr(cfg, k, v) else: From 6276311f3b9a34a241bd5578b217600858964526 Mon Sep 17 00:00:00 2001 From: Paul Gessinger Date: Tue, 1 Aug 2023 15:18:46 +0200 Subject: [PATCH 14/21] fix: Boundless Portal can be constructed from JSON (#2327) Also adds an assertion to the Portal constructor and the `fromJson` function to check if the deserialization has worked. debar)? --- Core/src/Detector/Portal.cpp | 5 +- .../Plugins/Json/SurfaceJsonConverter.hpp | 10 +- Plugins/Json/src/SurfaceJsonConverter.cpp | 99 +++++++++++-------- .../Plugins/Json/PortalJsonConverterTests.cpp | 9 +- 4 files changed, 79 insertions(+), 44 deletions(-) diff --git a/Core/src/Detector/Portal.cpp b/Core/src/Detector/Portal.cpp index d263c22c7fa..e1191cdb435 100644 --- a/Core/src/Detector/Portal.cpp +++ b/Core/src/Detector/Portal.cpp @@ -12,6 +12,7 @@ #include "Acts/Navigation/NavigationState.hpp" #include "Acts/Surfaces/Surface.hpp" #include "Acts/Utilities/Delegate.hpp" +#include "Acts/Utilities/ThrowAssert.hpp" #include #include @@ -24,7 +25,9 @@ class DetectorVolume; } // namespace Acts Acts::Experimental::Portal::Portal(std::shared_ptr surface) - : m_surface(std::move(surface)) {} + : m_surface(std::move(surface)) { + throw_assert(m_surface, "Portal surface is nullptr"); +} std::shared_ptr Acts::Experimental::Portal::makeShared(std::shared_ptr surface) { diff --git a/Plugins/Json/include/Acts/Plugins/Json/SurfaceJsonConverter.hpp b/Plugins/Json/include/Acts/Plugins/Json/SurfaceJsonConverter.hpp index 92ca72fa955..64b270626ab 100644 --- a/Plugins/Json/include/Acts/Plugins/Json/SurfaceJsonConverter.hpp +++ b/Plugins/Json/include/Acts/Plugins/Json/SurfaceJsonConverter.hpp @@ -73,9 +73,13 @@ template std::shared_ptr surfaceFromJsonT(const nlohmann::json& j) { nlohmann::json jTransform = j["transform"]; Transform3 sTransform = Transform3JsonConverter::fromJson(jTransform); - nlohmann::json jBounds = j["bounds"]; - auto sBounds = SurfaceBoundsJsonConverter::fromJson(jBounds); - return Surface::makeShared(sTransform, std::move(sBounds)); + if constexpr (std::is_same_v) { + return Surface::makeShared(sTransform); + } else { + nlohmann::json jBounds = j["bounds"]; + auto sBounds = SurfaceBoundsJsonConverter::fromJson(jBounds); + return Surface::makeShared(sTransform, std::move(sBounds)); + } } namespace SurfaceJsonConverter { diff --git a/Plugins/Json/src/SurfaceJsonConverter.cpp b/Plugins/Json/src/SurfaceJsonConverter.cpp index 2f85878a72d..2067cf419c7 100644 --- a/Plugins/Json/src/SurfaceJsonConverter.cpp +++ b/Plugins/Json/src/SurfaceJsonConverter.cpp @@ -65,59 +65,80 @@ std::shared_ptr Acts::SurfaceJsonConverter::fromJson( /// Unroll the types switch (sType) { // Surface is a plane surface - case Surface::SurfaceType::Plane: { - if (bType == SurfaceBounds::BoundsType::eEllipse) { - mutableSf = surfaceFromJsonT(j); - } else if (bType == SurfaceBounds::BoundsType::eRectangle) { - mutableSf = surfaceFromJsonT(j); - } else if (bType == SurfaceBounds::BoundsType::eTrapezoid) { - mutableSf = surfaceFromJsonT(j); + case Surface::SurfaceType::Plane: + switch (bType) { + case SurfaceBounds::BoundsType::eEllipse: + mutableSf = surfaceFromJsonT(j); + break; + case SurfaceBounds::BoundsType::eRectangle: + mutableSf = surfaceFromJsonT(j); + break; + case SurfaceBounds::BoundsType::eTrapezoid: + mutableSf = surfaceFromJsonT(j); + break; + + case SurfaceBounds::BoundsType::eBoundless: + mutableSf = surfaceFromJsonT(j); + break; + default: + throw std::invalid_argument("Invalid bounds type " + + std::to_string(bType) + + " for plane surface"); } - } break; + break; // Surface is a disc surface - case Surface::SurfaceType::Disc: { - if (bType == SurfaceBounds::BoundsType::eAnnulus) { - mutableSf = surfaceFromJsonT(j); - } else if (bType == SurfaceBounds::BoundsType::eDisc) { - mutableSf = surfaceFromJsonT(j); - } else if (bType == SurfaceBounds::BoundsType::eDiscTrapezoid) { - mutableSf = surfaceFromJsonT(j); + case Surface::SurfaceType::Disc: + switch (bType) { + case SurfaceBounds::BoundsType::eAnnulus: + mutableSf = surfaceFromJsonT(j); + break; + case SurfaceBounds::BoundsType::eDisc: + mutableSf = surfaceFromJsonT(j); + break; + case SurfaceBounds::BoundsType::eDiscTrapezoid: + mutableSf = surfaceFromJsonT(j); + break; + default: + throw std::invalid_argument("Invalid bounds type " + + std::to_string(bType) + + " for disc surface"); } - - } break; + break; // Surface is a cylinder surface - case Surface::SurfaceType::Cylinder: { + case Surface::SurfaceType::Cylinder: mutableSf = surfaceFromJsonT(j); - } break; + break; // Surface is a cone surface - case Surface::SurfaceType::Cone: { + case Surface::SurfaceType::Cone: mutableSf = surfaceFromJsonT(j); - } break; + break; // Surface is a straw surface - case Surface::SurfaceType::Straw: { + case Surface::SurfaceType::Straw: mutableSf = surfaceFromJsonT(j); - } break; + break; // Surface is a perigee surface - case Surface::SurfaceType::Perigee: { - Transform3 pTransform = Transform3JsonConverter::fromJson(j["transform"]); - mutableSf = Surface::makeShared(pTransform); - } break; - default: + case Surface::SurfaceType::Perigee: + mutableSf = Surface::makeShared( + Transform3JsonConverter::fromJson(j["transform"])); break; + default: + throw std::invalid_argument("Invalid surface type " + + std::to_string(sType)); } - if (mutableSf != nullptr) { - GeometryIdentifier geoID(j["geo_id"]); - mutableSf->assignGeometryId(geoID); - // Add material - if (j.find("material") != j.end() and not j["material"].empty()) { - const ISurfaceMaterial* surfaceMaterial = nullptr; - from_json(j, surfaceMaterial); - std::shared_ptr sharedSurfaceMaterial( - surfaceMaterial); - mutableSf->assignSurfaceMaterial(sharedSurfaceMaterial); - } + throw_assert(mutableSf, "Could not create surface from json"); + + GeometryIdentifier geoID(j["geo_id"]); + mutableSf->assignGeometryId(geoID); + // Add material + if (j.find("material") != j.end() and not j["material"].empty()) { + const ISurfaceMaterial* surfaceMaterial = nullptr; + from_json(j, surfaceMaterial); + std::shared_ptr sharedSurfaceMaterial( + surfaceMaterial); + mutableSf->assignSurfaceMaterial(sharedSurfaceMaterial); } + return mutableSf; } diff --git a/Tests/UnitTests/Plugins/Json/PortalJsonConverterTests.cpp b/Tests/UnitTests/Plugins/Json/PortalJsonConverterTests.cpp index 22ca7e529d0..affe95c7690 100644 --- a/Tests/UnitTests/Plugins/Json/PortalJsonConverterTests.cpp +++ b/Tests/UnitTests/Plugins/Json/PortalJsonConverterTests.cpp @@ -40,6 +40,8 @@ BOOST_AUTO_TEST_CASE(PortalUnconnected) { auto portal = Acts::Experimental::Portal::makeShared(std::move(surface)); + BOOST_CHECK_NE(portal, nullptr); + auto jPortal = Acts::PortalJsonConverter::toJson(tContext, *portal, {}); out.open("portal.json"); @@ -55,6 +57,8 @@ BOOST_AUTO_TEST_CASE(PortalUnconnected) { in.close(); auto portalIn = Acts::PortalJsonConverter::fromJson(tContext, jPortalIn, {}); + + BOOST_CHECK_NE(portalIn, nullptr); } BOOST_AUTO_TEST_CASE(PortalSingleConnected) { @@ -67,6 +71,7 @@ BOOST_AUTO_TEST_CASE(PortalSingleConnected) { Acts::Vector3(0., 0., 0.), Acts::Vector3(0., 1., 0.)); auto portal = Acts::Experimental::Portal::makeShared(std::move(surface)); + BOOST_CHECK_NE(portal, nullptr); // Attaching the portals Acts::Experimental::detail::PortalHelper::attachDetectorVolumeUpdator( *portal, forwardVolume, Acts::Direction::Forward); @@ -75,7 +80,7 @@ BOOST_AUTO_TEST_CASE(PortalSingleConnected) { std::vector detectorVolumes = { forwardVolume.get(), backwardVolume.get()}; - // No voluems provided, must bail + // No volumes provided, must bail BOOST_CHECK_THROW(Acts::PortalJsonConverter::toJson(tContext, *portal, {}), std::runtime_error); auto jPortal = @@ -95,6 +100,7 @@ BOOST_AUTO_TEST_CASE(PortalSingleConnected) { auto portalIn = Acts::PortalJsonConverter::fromJson( tContext, jPortalIn, {forwardVolume, backwardVolume}); + BOOST_CHECK_NE(portalIn, nullptr); } BOOST_AUTO_TEST_CASE(PortalMultiConnected) { @@ -110,6 +116,7 @@ BOOST_AUTO_TEST_CASE(PortalMultiConnected) { Acts::Vector3(0., 0., 0.), Acts::Vector3(0., 1., 0.)); auto portal = Acts::Experimental::Portal::makeShared(std::move(surface)); + BOOST_CHECK_NE(portal, nullptr); // Attaching the portals Acts::Experimental::detail::PortalHelper::attachDetectorVolumeUpdator( From 6527cda2c86816372f14ce732a6899bbf267b334 Mon Sep 17 00:00:00 2001 From: Paul Gessinger Date: Tue, 1 Aug 2023 18:12:09 +0200 Subject: [PATCH 15/21] ci: Switch off forced-assertions in extra Ubuntu builds (#2337) We've had problems in the past that some variables were only ever used by `assert`ions, with this change, the extra builds should fail and tell us when that's the case. --- .github/workflows/builds.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/builds.yml b/.github/workflows/builds.yml index ca2c7d79d1c..86ec980daf5 100644 --- a/.github/workflows/builds.yml +++ b/.github/workflows/builds.yml @@ -401,7 +401,7 @@ jobs: -DACTS_BUILD_ODD=ON -DACTS_BUILD_EXAMPLES_PYTHON_BINDINGS=ON -DACTS_BUILD_EXAMPLES_EDM4HEP=ON - -DACTS_FORCE_ASSERTIONS=ON + -DACTS_FORCE_ASSERTIONS=OFF -DACTS_BUILD_ANALYSIS_APPS=ON -DACTS_BUILD_PLUGIN_ACTSVG=ON From 271cbf8ba0af2dd0902a6f0a21f5dd3ba9879e37 Mon Sep 17 00:00:00 2001 From: Andreas Stefl Date: Wed, 2 Aug 2023 13:32:05 +0200 Subject: [PATCH 16/21] feat: Target surface for filtering phase of CKF (#2319) For inward track finding using the `CombinatorialKalmanFilter` we need an abort condition to stop propagation at the interaction point as we would otherwise go through the whole detector on the other side. In this PR I introduce a `filterTargetSurface` which terminates the track finding when reached. It is optional and therefore should not modify the current behavior of the CKF. I also aligned the naming for the existing `targetSurface` to `smoothingTargetSurface` to disambiguate those two surfaces. --- .../CombinatorialKalmanFilter.hpp | 42 +++++++++++++------ .../TrackFinding/TrackFindingAlgorithm.hpp | 3 ++ .../src/TrackFindingAlgorithm.cpp | 5 ++- .../CombinatorialKalmanFilterTests.cpp | 4 +- 4 files changed, 38 insertions(+), 16 deletions(-) diff --git a/Core/include/Acts/TrackFinding/CombinatorialKalmanFilter.hpp b/Core/include/Acts/TrackFinding/CombinatorialKalmanFilter.hpp index cad4db4f1c5..1164a3144ba 100644 --- a/Core/include/Acts/TrackFinding/CombinatorialKalmanFilter.hpp +++ b/Core/include/Acts/TrackFinding/CombinatorialKalmanFilter.hpp @@ -169,7 +169,7 @@ struct CombinatorialKalmanFilterOptions { sourcelinkAccessor(std::move(accessor_)), extensions(extensions_), propagatorPlainOptions(pOptions), - referenceSurface(rSurface), + smoothingTargetSurface(rSurface), multipleScattering(mScattering), energyLoss(eLoss), smoothing(rSmoothing) {} @@ -193,8 +193,11 @@ struct CombinatorialKalmanFilterOptions { /// The trivial propagator options PropagatorPlainOptions propagatorPlainOptions; - /// The reference Surface - const Surface* referenceSurface = nullptr; + /// The filter target surface + const Surface* filterTargetSurface = nullptr; + + /// The smoothing target surface + const Surface* smoothingTargetSurface = nullptr; /// Whether to consider multiple scattering. bool multipleScattering = true; @@ -323,8 +326,11 @@ class CombinatorialKalmanFilter { /// Broadcast the result_type using result_type = CombinatorialKalmanFilterResult; - /// The target surface - const Surface* targetSurface = nullptr; + /// The filter target surface + const Surface* filterTargetSurface = nullptr; + + /// The smoothing target surface + const Surface* smoothingTargetSurface = nullptr; /// Whether to consider multiple scattering. bool multipleScattering = true; @@ -358,6 +364,13 @@ class CombinatorialKalmanFilter { ACTS_VERBOSE("CombinatorialKalmanFilter step"); + if (not result.filtered and filterTargetSurface != nullptr and + targetReached(state, stepper, navigator, *filterTargetSurface, + logger())) { + navigator.navigationBreak(state.navigation, true); + stepper.releaseStepSize(state.stepping); + } + // Update: // - Waiting for a current surface auto surface = navigator.currentSurface(state.navigation); @@ -500,16 +513,18 @@ class CombinatorialKalmanFilter { // -> then progress to target/reference surface and built the final // track parameters for found track indexed with iSmoothed - if (result.smoothed and (targetSurface == nullptr or - targetReached(state, stepper, navigator, - *targetSurface, logger()))) { + if (result.smoothed and + (smoothingTargetSurface == nullptr or + targetReached(state, stepper, navigator, + *smoothingTargetSurface, logger()))) { ACTS_VERBOSE( "Completing the track with last measurement index = " << result.lastMeasurementIndices.at(result.iSmoothed)); - if (targetSurface != nullptr) { + if (smoothingTargetSurface != nullptr) { // Transport & bind the parameter to the final surface - auto res = stepper.boundState(state.stepping, *targetSurface); + auto res = + stepper.boundState(state.stepping, *smoothingTargetSurface); if (!res.ok()) { ACTS_ERROR("Error in finalize: " << res.error()); result.result = res.error(); @@ -1158,7 +1173,7 @@ class CombinatorialKalmanFilter { } // Return in case no target surface - if (targetSurface == nullptr) { + if (smoothingTargetSurface == nullptr) { return Result::success(); } @@ -1171,7 +1186,7 @@ class CombinatorialKalmanFilter { // Lambda to get the intersection of the free params on the target surface auto target = [&](const FreeVector& freeVector) -> SurfaceIntersection { - return targetSurface->intersect( + return smoothingTargetSurface->intersect( state.geoContext, freeVector.segment<3>(eFreePos0), state.stepping.navDir * freeVector.segment<3>(eFreeDir0), true, state.options.targetTolerance); @@ -1335,7 +1350,8 @@ class CombinatorialKalmanFilter { // Catch the actor auto& combKalmanActor = propOptions.actionList.template get(); - combKalmanActor.targetSurface = tfOptions.referenceSurface; + combKalmanActor.filterTargetSurface = tfOptions.filterTargetSurface; + combKalmanActor.smoothingTargetSurface = tfOptions.smoothingTargetSurface; combKalmanActor.multipleScattering = tfOptions.multipleScattering; combKalmanActor.energyLoss = tfOptions.energyLoss; combKalmanActor.smoothing = tfOptions.smoothing; diff --git a/Examples/Algorithms/TrackFinding/include/ActsExamples/TrackFinding/TrackFindingAlgorithm.hpp b/Examples/Algorithms/TrackFinding/include/ActsExamples/TrackFinding/TrackFindingAlgorithm.hpp index 70536e3be60..e87727e2b9a 100644 --- a/Examples/Algorithms/TrackFinding/include/ActsExamples/TrackFinding/TrackFindingAlgorithm.hpp +++ b/Examples/Algorithms/TrackFinding/include/ActsExamples/TrackFinding/TrackFindingAlgorithm.hpp @@ -86,6 +86,7 @@ class TrackFindingAlgorithm final : public IAlgorithm { std::string inputInitialTrackParameters; /// Output find trajectories collection. std::string outputTracks; + /// Type erased track finder function. std::shared_ptr findTracks; /// CKF measurement selector config @@ -94,6 +95,8 @@ class TrackFindingAlgorithm final : public IAlgorithm { bool computeSharedHits = false; /// Track selector config std::optional trackSelectorCfg = std::nullopt; + /// Run backward finding + bool backward = false; }; /// Constructor of the track finding algorithm diff --git a/Examples/Algorithms/TrackFinding/src/TrackFindingAlgorithm.cpp b/Examples/Algorithms/TrackFinding/src/TrackFindingAlgorithm.cpp index 020ffaa1bef..2e591f69b32 100644 --- a/Examples/Algorithms/TrackFinding/src/TrackFindingAlgorithm.cpp +++ b/Examples/Algorithms/TrackFinding/src/TrackFindingAlgorithm.cpp @@ -9,6 +9,7 @@ #include "ActsExamples/TrackFinding/TrackFindingAlgorithm.hpp" #include "Acts/Definitions/Algebra.hpp" +#include "Acts/Definitions/Direction.hpp" #include "Acts/EventData/MultiTrajectory.hpp" #include "Acts/EventData/TrackContainer.hpp" #include "Acts/EventData/VectorMultiTrajectory.hpp" @@ -81,6 +82,8 @@ ActsExamples::ProcessCode ActsExamples::TrackFindingAlgorithm::execute( Acts::PropagatorPlainOptions pOptions; pOptions.maxSteps = 100000; + pOptions.direction = + m_cfg.backward ? Acts::Direction::Backward : Acts::Direction::Forward; PassThroughCalibrator pcalibrator; MeasurementCalibratorAdapter calibrator(pcalibrator, measurements); @@ -111,7 +114,7 @@ ActsExamples::ProcessCode ActsExamples::TrackFindingAlgorithm::execute( // Set the CombinatorialKalmanFilter options ActsExamples::TrackFindingAlgorithm::TrackFinderOptions options( ctx.geoContext, ctx.magFieldContext, ctx.calibContext, slAccessorDelegate, - extensions, pOptions, &(*pSurface)); + extensions, pOptions, pSurface.get()); // Perform the track finding for all initial parameters ACTS_DEBUG("Invoke track finding with " << initialParameters.size() diff --git a/Tests/UnitTests/Core/TrackFinding/CombinatorialKalmanFilterTests.cpp b/Tests/UnitTests/Core/TrackFinding/CombinatorialKalmanFilterTests.cpp index 22b1eb31e27..cfca100f0f1 100644 --- a/Tests/UnitTests/Core/TrackFinding/CombinatorialKalmanFilterTests.cpp +++ b/Tests/UnitTests/Core/TrackFinding/CombinatorialKalmanFilterTests.cpp @@ -305,7 +305,7 @@ BOOST_AUTO_TEST_CASE(ZeroFieldForward) { auto pSurface = Acts::Surface::makeShared( Acts::Vector3{-3_m, 0., 0.}, Acts::Vector3{1., 0., 0}); // Set the target surface - options.referenceSurface = &(*pSurface); + options.smoothingTargetSurface = pSurface.get(); Fixture::TestSourceLinkAccessor slAccessor; slAccessor.container = &f.sourceLinks; @@ -362,7 +362,7 @@ BOOST_AUTO_TEST_CASE(ZeroFieldBackward) { auto pSurface = Acts::Surface::makeShared( Acts::Vector3{3_m, 0., 0.}, Acts::Vector3{1., 0., 0}); // Set the target surface - options.referenceSurface = &(*pSurface); + options.smoothingTargetSurface = pSurface.get(); Fixture::TestSourceLinkAccessor slAccessor; slAccessor.container = &f.sourceLinks; From b5babcb04bb610b55a02a753b180e5d3893ca972 Mon Sep 17 00:00:00 2001 From: Paul Gessinger Date: Thu, 3 Aug 2023 16:29:25 +0200 Subject: [PATCH 17/21] ci: Create job summaries in python (#2339) --- .github/workflows/builds.yml | 7 +++++ CI/physmon/phys_perf_mon.sh | 5 ++- CI/physmon/summary.py | 60 +++++++++++++++++++++++++----------- 3 files changed, 53 insertions(+), 19 deletions(-) diff --git a/.github/workflows/builds.yml b/.github/workflows/builds.yml index 86ec980daf5..97796a40e63 100644 --- a/.github/workflows/builds.yml +++ b/.github/workflows/builds.yml @@ -257,6 +257,10 @@ jobs: - name: Python level tests shell: bash + env: + PYTEST_MD_REPORT: true + PYTEST_MD_REPORT_VERBOSE: 0 + PYTEST_MD_REPORT_OUTPUT: pytest.md run: > /usr/local/bin/geant4-config --install-datasets && source /usr/local/bin/thisroot.sh @@ -266,7 +270,9 @@ jobs: && export PYTHONPATH=/usr/local/python:$PYTHONPATH && export LD_LIBRARY_PATH=$PWD/build/thirdparty/OpenDataDetector/factory:$LD_LIBRARY_PATH && pip3 install -r Examples/Python/tests/requirements.txt + && pip3 install pytest-md-report && pytest -rFsv -k "not exatrkx" -v + && cat ${PYTEST_MD_REPORT_OUTPUT} >> $GITHUB_STEP_SUMMARY linux_physmon: runs-on: ubuntu-latest @@ -314,6 +320,7 @@ jobs: && echo "::endgroup::" && export PYTHONPATH="${PYTHONPATH}":"${GITHUB_WORKSPACE}/Examples/Scripts/Python" && CI/physmon/phys_perf_mon.sh all physmon + && cat physmon/summary.md >> $GITHUB_STEP_SUMMARY - uses: actions/upload-artifact@v3 if: always() diff --git a/CI/physmon/phys_perf_mon.sh b/CI/physmon/phys_perf_mon.sh index 961bf8461f5..a13de11fcd0 100755 --- a/CI/physmon/phys_perf_mon.sh +++ b/CI/physmon/phys_perf_mon.sh @@ -247,7 +247,10 @@ if [[ "$mode" == "all" || "$mode" == "simulation" ]]; then simulation geant4 fi -CI/physmon/summary.py $outdir/*.html $outdir/summary.html +CI/physmon/summary.py $outdir/*.html \ + --base $outdir \ + --md $outdir/summary.md \ + --html $outdir/summary.html ec=$(($ec | $?)) exit $ec diff --git a/CI/physmon/summary.py b/CI/physmon/summary.py index 9525a3ed44c..75b2d67788b 100755 --- a/CI/physmon/summary.py +++ b/CI/physmon/summary.py @@ -6,10 +6,15 @@ import functools import os +HERALD_URL = "https://herald.dokku.paulgessinger.com/view/{repo}/runs/{run_id}/artifacts/{artifact_name}/{path}" +IS_CI = "GITHUB_ACTIONS" in os.environ + parser = argparse.ArgumentParser() -parser.add_argument("html", nargs="+") -parser.add_argument("output") +parser.add_argument("inputs", nargs="+") +parser.add_argument("--base", required=True) +parser.add_argument("--html") +parser.add_argument("--md") args = parser.parse_args() re_title = re.compile(r'

\s*(.*)\s*<\/p>', re.RegexFlag.MULTILINE) @@ -17,7 +22,7 @@ summary = {} -for h in args.html: +for h in args.inputs: with open(h, mode="r", encoding="utf-8") as f: try: content = f.read() @@ -34,9 +39,10 @@ except Exception as e: print(r"could not parse {h}", e) -with open(args.output, mode="w", encoding="utf-8") as f: - f.write( - """ +if args.html: + with open(args.html, mode="w", encoding="utf-8") as f: + f.write( + """ physmon summary @@ -44,19 +50,37 @@

physmon summary

-
- -""" - ) +if args.md: + with open(args.md, mode="w", encoding="utf-8") as f: + f.write("# physmon summary\n") + for h, s in summary.items(): + path = os.path.relpath(h, args.base) + if IS_CI: + url = HERALD_URL.format( + repo=os.environ["GITHUB_REPOSITORY"], + run_id=os.environ["GITHUB_RUN_ID"], + artifact_name="physmon", + path=path, + ) + else: + url = path + f.write(f" - {'✅' if s['total'] else '🔴'} [{s['title']}]({url})\n") From 0147d07da8dbcaebca9ee2954afbe774b47b77c4 Mon Sep 17 00:00:00 2001 From: Xiaocong Ai Date: Fri, 4 Aug 2023 22:09:46 +0800 Subject: [PATCH 18/21] perf: Add option of stereo angle when building telescope detector (#2240) This PR add options for stereo angles of each telescope plane when building the telescope detector, which is helpful in case that the telescope planes are strip detectors with stereo angles. Co-authored-by: Paul Gessinger <1058585+paulgessinger@users.noreply.github.com> --- .../BuildTelescopeDetector.hpp | 16 +++++--- .../TelescopeDetector/TelescopeDetector.hpp | 1 + .../src/BuildTelescopeDetector.cpp | 15 +++++-- .../src/TelescopeDetector.cpp | 11 +++++- Examples/Python/src/Detector.cpp | 1 + Examples/Python/tests/test_detectors.py | 1 + .../Common/src/TelescopeDetectorOptions.cpp | 39 ------------------- .../src/TelescopeDetectorWithOptions.cpp | 8 ++++ .../Python/truth_tracking_telescope.py | 12 +++--- 9 files changed, 47 insertions(+), 57 deletions(-) delete mode 100644 Examples/Run/Common/src/TelescopeDetectorOptions.cpp diff --git a/Examples/Detectors/TelescopeDetector/include/ActsExamples/TelescopeDetector/BuildTelescopeDetector.hpp b/Examples/Detectors/TelescopeDetector/include/ActsExamples/TelescopeDetector/BuildTelescopeDetector.hpp index eaf2cfc9caf..d54a75b941d 100644 --- a/Examples/Detectors/TelescopeDetector/include/ActsExamples/TelescopeDetector/BuildTelescopeDetector.hpp +++ b/Examples/Detectors/TelescopeDetector/include/ActsExamples/TelescopeDetector/BuildTelescopeDetector.hpp @@ -34,11 +34,14 @@ enum class TelescopeSurfaceType { /// /// @param gctx is the detector element dependent geometry context /// @param detectorStore is the store for the detector element -/// @param positions is the offset w of different layers in the longitudinal -/// direction +/// @param positions are the positions of different layers in the longitudinal +/// direction +/// @param stereoAngles are the stereo angles of different layers, which are +/// rotation angles around the longitudinal (normal) +/// direction /// @param offsets is the offset (u, v) of the layers in the transverse plane /// @param bounds is the surface bound values, i.e. halfX and halfY if plane -/// surface, and minR and maxR if disc surface +/// surface, and minR and maxR if disc surface /// @param thickness is the material thickness of each layer /// @param surfaceType is the detector surface type /// @param binValue indicates which axis the detector surface normals are @@ -46,9 +49,10 @@ enum class TelescopeSurfaceType { std::unique_ptr buildDetector( const typename TelescopeDetectorElement::ContextType& gctx, std::vector>& detectorStore, - const std::vector& positions, const std::array& offsets, - const std::array& bounds, double thickness, - TelescopeSurfaceType surfaceType, + const std::vector& positions, + const std::vector& stereoAngles, + const std::array& offsets, const std::array& bounds, + double thickness, TelescopeSurfaceType surfaceType, Acts::BinningValue binValue = Acts::BinningValue::binZ); } // end of namespace Telescope diff --git a/Examples/Detectors/TelescopeDetector/include/ActsExamples/TelescopeDetector/TelescopeDetector.hpp b/Examples/Detectors/TelescopeDetector/include/ActsExamples/TelescopeDetector/TelescopeDetector.hpp index 1869094d60b..e1d94db9604 100644 --- a/Examples/Detectors/TelescopeDetector/include/ActsExamples/TelescopeDetector/TelescopeDetector.hpp +++ b/Examples/Detectors/TelescopeDetector/include/ActsExamples/TelescopeDetector/TelescopeDetector.hpp @@ -45,6 +45,7 @@ struct TelescopeDetector { struct Config { std::vector positions{{0, 30, 60, 120, 150, 180}}; + std::vector stereos{{0, 0, 0, 0, 0, 0}}; std::array offsets{{0, 0}}; std::array bounds{{25, 100}}; double thickness{80_um}; diff --git a/Examples/Detectors/TelescopeDetector/src/BuildTelescopeDetector.cpp b/Examples/Detectors/TelescopeDetector/src/BuildTelescopeDetector.cpp index 85328c82aba..ce0d834cea3 100644 --- a/Examples/Detectors/TelescopeDetector/src/BuildTelescopeDetector.cpp +++ b/Examples/Detectors/TelescopeDetector/src/BuildTelescopeDetector.cpp @@ -40,9 +40,10 @@ ActsExamples::Telescope::buildDetector( std::vector< std::shared_ptr>& detectorStore, - const std::vector& positions, const std::array& offsets, - const std::array& bounds, double thickness, - ActsExamples::Telescope::TelescopeSurfaceType surfaceType, + const std::vector& positions, + const std::vector& stereoAngles, + const std::array& offsets, const std::array& bounds, + double thickness, ActsExamples::Telescope::TelescopeSurfaceType surfaceType, Acts::BinningValue binValue) { using namespace Acts::UnitLiterals; @@ -80,8 +81,14 @@ ActsExamples::Telescope::buildDetector( for (unsigned int i = 0; i < nLayers; ++i) { // The translation without rotation yet Acts::Translation3 trans(offsets[0], offsets[1], positions[i]); - // The transform + // The entire transformation (the coordinate system, whose center is defined + // by trans, will be rotated as well) Acts::Transform3 trafo(rotation * trans); + + // rotate around local z axis by stereo angle + auto stereo = stereoAngles[i]; + trafo *= Acts::AngleAxis3(stereo, Acts::Vector3::UnitZ()); + // Create the detector element std::shared_ptr detElement = nullptr; if (surfaceType == TelescopeSurfaceType::Plane) { diff --git a/Examples/Detectors/TelescopeDetector/src/TelescopeDetector.cpp b/Examples/Detectors/TelescopeDetector/src/TelescopeDetector.cpp index ceb76c5bf62..2bc269f48ab 100644 --- a/Examples/Detectors/TelescopeDetector/src/TelescopeDetector.cpp +++ b/Examples/Detectors/TelescopeDetector/src/TelescopeDetector.cpp @@ -35,16 +35,23 @@ auto ActsExamples::Telescope::TelescopeDetector::finalize( "The minR should be smaller than the maxR for disc surface bounds."); } + if (cfg.positions.size() != cfg.stereos.size()) { + throw std::invalid_argument( + "The number of provided positions must match the number of " + "provided stereo angles."); + } + config = cfg; // Sort the provided distances std::vector positions = cfg.positions; + std::vector stereos = cfg.stereos; std::sort(positions.begin(), positions.end()); /// Return the telescope detector TrackingGeometryPtr gGeometry = ActsExamples::Telescope::buildDetector( - nominalContext, detectorStore, positions, cfg.offsets, cfg.bounds, - cfg.thickness, + nominalContext, detectorStore, positions, stereos, cfg.offsets, + cfg.bounds, cfg.thickness, static_cast( cfg.surfaceType), static_cast(cfg.binValue)); diff --git a/Examples/Python/src/Detector.cpp b/Examples/Python/src/Detector.cpp index 677daab9555..524aa867383 100644 --- a/Examples/Python/src/Detector.cpp +++ b/Examples/Python/src/Detector.cpp @@ -81,6 +81,7 @@ void addDetector(Context& ctx) { py::class_(td, "Config") .def(py::init<>()) .def_readwrite("positions", &Config::positions) + .def_readwrite("stereos", &Config::stereos) .def_readwrite("offsets", &Config::offsets) .def_readwrite("bounds", &Config::bounds) .def_readwrite("thickness", &Config::thickness) diff --git a/Examples/Python/tests/test_detectors.py b/Examples/Python/tests/test_detectors.py index f5d76095235..1a01809e5dd 100644 --- a/Examples/Python/tests/test_detectors.py +++ b/Examples/Python/tests/test_detectors.py @@ -44,6 +44,7 @@ def test_telescope_geometry(): detector, geo, contextDecorators = acts.examples.TelescopeDetector.create( bounds=[100, 100], positions=[10 * i for i in range(n_surfaces)], + stereos=[0] * n_surfaces, binValue=0, ) diff --git a/Examples/Run/Common/src/TelescopeDetectorOptions.cpp b/Examples/Run/Common/src/TelescopeDetectorOptions.cpp deleted file mode 100644 index 72eea3444a4..00000000000 --- a/Examples/Run/Common/src/TelescopeDetectorOptions.cpp +++ /dev/null @@ -1,39 +0,0 @@ -// This file is part of the Acts project. -// -// Copyright (C) 2020-2021 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 "ActsExamples/TelescopeDetector/TelescopeDetectorOptions.hpp" - -#include "ActsExamples/Utilities/Options.hpp" - -#include - -void ActsExamples::Options::addTelescopeGeometryOptions( - ActsExamples::Options::Description& desc) { - using boost::program_options::value; - auto opt = desc.add_options(); - opt("geo-tele-positions", - value()->default_value({{0, 30, 60, 120, 150, 180}}), - "Telescope detector Input: the layers positions in the longidutinal " - "direction in mm"); - opt("geo-tele-offsets", value>()->default_value({{0, 0}}), - "Telescope detector Input: the layers offsets in the transverse plane in " - "mm. Same values for " - "all layers"); - opt("geo-tele-bounds", value>()->default_value({{25, 100}}), - "Telescope detector Input: the values for surface bounds in mm: " - "(halfX, halfY) - plane surface, (minR, maxR) - disc surface"); - opt("geo-tele-thickness", value()->default_value(80), - "Telescope detector Input: the silicon material thickness of " - "each layer in um. Same value for all layers"); - opt("geo-tele-surface", value()->default_value(0), - "Telescope detector Input: the detector surface type: 0 - plane surface, " - "1 - disc surface"); - opt("geo-tele-alignaxis", value()->default_value(2), - "Telescope detector Input: the detector is placed along which " - "axis: 0 - x axis, 1 - y axis, 2 - z axis"); -} diff --git a/Examples/Run/Common/src/TelescopeDetectorWithOptions.cpp b/Examples/Run/Common/src/TelescopeDetectorWithOptions.cpp index ab7c8e7da9a..7d0665ad789 100644 --- a/Examples/Run/Common/src/TelescopeDetectorWithOptions.cpp +++ b/Examples/Run/Common/src/TelescopeDetectorWithOptions.cpp @@ -24,6 +24,11 @@ void TelescopeDetectorWithOptions::addOptions( value()->default_value({{0, 30, 60, 120, 150, 180}}), "Telescope detector Input: the layers positions in the longidutinal " "direction in mm"); + opt("geo-tele-stereos", + value()->default_value({{0, 0, 0, 0, 0, 0}}), + "Telescope detector Input: the layers stereo angle around the " + "longitudinal " + "direction in rad"); opt("geo-tele-offsets", value>()->default_value({{0, 0}}), "Telescope detector Input: the layers offsets in the transverse plane " "in " @@ -53,6 +58,9 @@ auto TelescopeDetectorWithOptions::finalize( cfg.positions = vm["geo-tele-positions"] .template as() .values; + cfg.stereos = vm["geo-tele-stereos"] + .template as() + .values; cfg.offsets = vm["geo-tele-offsets"].template as>(); // The bounds values are taken as (halfX, halfY) for plane surface and diff --git a/Examples/Scripts/Python/truth_tracking_telescope.py b/Examples/Scripts/Python/truth_tracking_telescope.py index 87df981b8bd..b179fb9afa7 100755 --- a/Examples/Scripts/Python/truth_tracking_telescope.py +++ b/Examples/Scripts/Python/truth_tracking_telescope.py @@ -11,19 +11,19 @@ if "__main__" == __name__: detector, trackingGeometry, decorators = acts.examples.TelescopeDetector.create( - bounds=[200, 200], positions=[30, 60, 90, 120, 150, 180, 210, 240, 270] + bounds=[200, 200], + positions=[30, 60, 90, 120, 150, 180, 210, 240, 270], + stereos=[0] * 9, ) - digiConfigFile = ( - Path(__file__).resolve().parent.parent.parent.parent - / "Examples/Algorithms/Digitization/share/default-smearing-config-telescope.json", - ) + srcdir = Path(__file__).resolve().parent.parent.parent.parent field = acts.ConstantBField(acts.Vector3(0, 0, 2 * u.T)) runTruthTrackingKalman( trackingGeometry, field, - digiConfigFile=digiConfigFile, + digiConfigFile=srcdir + / "Examples/Algorithms/Digitization/share/default-smearing-config-telescope.json", outputDir=Path.cwd(), ).run() From 3c69bf252e40caf835ab4ef8e288d513b906284c Mon Sep 17 00:00:00 2001 From: Andreas Stefl Date: Fri, 4 Aug 2023 18:12:43 +0200 Subject: [PATCH 19/21] refactor: Remove direction from stepper (#2312) The propagation direction is currently stored in multiple places which makes it tedious to change it consistently during propagation which can easily result in bugs. In this PR I propose to remove the `navDir` from the stepper interface and replace it consistently with the propagation option for the direction. This should not effect any physics performance as it is a code refactor of how we pass down the propagation direction to the stepper. In a follow up PR I want to do the same thing for the navigation. --- .../Acts/Material/MaterialCollector.hpp | 4 +- .../Acts/Navigation/DetectorNavigator.hpp | 8 +- Core/include/Acts/Propagator/AtlasStepper.hpp | 30 +++---- .../Acts/Propagator/DirectNavigator.hpp | 10 ++- Core/include/Acts/Propagator/EigenStepper.hpp | 22 ++---- Core/include/Acts/Propagator/EigenStepper.ipp | 12 ++- .../Acts/Propagator/MaterialInteractor.hpp | 2 +- .../Acts/Propagator/MultiEigenStepperLoop.hpp | 39 +++++---- .../Acts/Propagator/MultiStepperAborters.hpp | 2 +- Core/include/Acts/Propagator/Navigator.hpp | 43 +++++----- Core/include/Acts/Propagator/Propagator.ipp | 4 +- .../Acts/Propagator/StandardAborters.hpp | 2 +- .../Acts/Propagator/StepperConcept.hpp | 8 +- .../Acts/Propagator/StraightLineStepper.hpp | 26 +++--- .../Acts/Propagator/detail/LoopProtection.hpp | 2 +- .../detail/PointwiseMaterialInteraction.hpp | 2 +- .../Acts/Propagator/detail/SteppingHelper.hpp | 8 +- .../Acts/Propagator/detail/SteppingLogger.hpp | 2 +- .../detail/VolumeMaterialInteraction.hpp | 2 +- .../CombinatorialKalmanFilter.hpp | 10 +-- .../Acts/TrackFitting/KalmanFitter.hpp | 17 ++-- .../Acts/TrackFitting/detail/GsfActor.hpp | 6 +- .../Acts/TrackFitting/detail/GsfUtils.hpp | 2 +- .../detail/KalmanUpdateHelpers.hpp | 2 +- Core/src/Propagator/StraightLineStepper.cpp | 2 - .../Core/Propagator/AtlasStepperTests.cpp | 67 ++++++++-------- .../Core/Propagator/EigenStepperTests.cpp | 40 +++++----- .../Core/Propagator/LoopProtectionTests.cpp | 3 +- .../Core/Propagator/MultiStepperTests.cpp | 79 +++++++++---------- .../Core/Propagator/NavigatorTests.cpp | 17 ++-- .../Core/Propagator/PropagatorTests.cpp | 2 +- .../Propagator/StraightLineStepperTests.cpp | 60 +++++++------- .../VolumeMaterialInteractionTests.cpp | 6 +- 33 files changed, 255 insertions(+), 286 deletions(-) diff --git a/Core/include/Acts/Material/MaterialCollector.hpp b/Core/include/Acts/Material/MaterialCollector.hpp index b26b431c221..c4748ea6a2a 100644 --- a/Core/include/Acts/Material/MaterialCollector.hpp +++ b/Core/include/Acts/Material/MaterialCollector.hpp @@ -89,14 +89,14 @@ struct MaterialCollector { ACTS_VERBOSE("Update on start surface: post-update mode."); prepofu = navigator.currentSurface(state.navigation) ->surfaceMaterial() - ->factor(state.stepping.navDir, + ->factor(state.options.direction, MaterialUpdateStage::PostUpdate); } else if (navigator.targetSurface(state.navigation) == navigator.currentSurface(state.navigation)) { ACTS_VERBOSE("Update on target surface: pre-update mode"); prepofu = navigator.currentSurface(state.navigation) ->surfaceMaterial() - ->factor(state.stepping.navDir, + ->factor(state.options.direction, MaterialUpdateStage::PreUpdate); } else { ACTS_VERBOSE("Update while pass through: full mode."); diff --git a/Core/include/Acts/Navigation/DetectorNavigator.hpp b/Core/include/Acts/Navigation/DetectorNavigator.hpp index f537ebf179f..2124211df40 100644 --- a/Core/include/Acts/Navigation/DetectorNavigator.hpp +++ b/Core/include/Acts/Navigation/DetectorNavigator.hpp @@ -222,8 +222,9 @@ class DetectorNavigator { << surface.geometryId()); // Estimate the surface status bool boundaryCheck = c.boundaryCheck; - auto surfaceStatus = stepper.updateSurfaceStatus(state.stepping, surface, - boundaryCheck, logger()); + auto surfaceStatus = stepper.updateSurfaceStatus( + state.stepping, surface, state.options.direction, boundaryCheck, + state.options.targetTolerance, logger()); if (surfaceStatus == Intersection3D::Status::reachable) { ACTS_VERBOSE(volInfo(state) << posInfo(state, stepper) @@ -290,7 +291,8 @@ class DetectorNavigator { // TODO not sure about the boundary check auto surfaceStatus = stepper.updateSurfaceStatus( - state.stepping, *nextSurface, boundaryCheck, logger()); + state.stepping, *nextSurface, state.options.direction, boundaryCheck, + state.options.targetTolerance, logger()); // Check if we are at a surface if (surfaceStatus == Intersection3D::Status::onSurface) { diff --git a/Core/include/Acts/Propagator/AtlasStepper.hpp b/Core/include/Acts/Propagator/AtlasStepper.hpp index 7574fd1ec93..3ee641a1301 100644 --- a/Core/include/Acts/Propagator/AtlasStepper.hpp +++ b/Core/include/Acts/Propagator/AtlasStepper.hpp @@ -52,17 +52,14 @@ class AtlasStepper { /// @param [in] gctx The geometry context tof this call /// @param [in] fieldCacheIn The magnetic field cache for this call /// @param [in] pars Input parameters - /// @param [in] ndir The navigation direction w.r.t. parameters /// @param [in] ssize the steps size limitation /// @param [in] stolerance is the stepping tolerance template State(const GeometryContext& gctx, MagneticFieldProvider::Cache fieldCacheIn, const Parameters& pars, - Direction ndir = Direction::Forward, double ssize = std::numeric_limits::max(), double stolerance = s_onSurfaceTolerance) - : navDir(ndir), - field(0., 0., 0.), + : field(0., 0., 0.), stepSize(ssize), tolerance(stolerance), fieldCache(std::move(fieldCacheIn)), @@ -233,7 +230,6 @@ class AtlasStepper { // optimisation that init is not called twice bool state_ready = false; // configuration - Direction navDir = Direction::Forward; bool useJacobian = false; double step = 0; double maxPathLength = 0; @@ -298,11 +294,9 @@ class AtlasStepper { State makeState(std::reference_wrapper gctx, std::reference_wrapper mctx, const GenericBoundTrackParameters& par, - Direction navDir = Direction::Forward, double ssize = std::numeric_limits::max(), double stolerance = s_onSurfaceTolerance) const { - return State{gctx, m_bField->makeCache(mctx), par, navDir, ssize, - stolerance}; + return State{gctx, m_bField->makeCache(mctx), par, ssize, stolerance}; } /// @brief Resets the state @@ -311,18 +305,16 @@ class AtlasStepper { /// @param [in] boundParams Parameters in bound parametrisation /// @param [in] cov Covariance matrix /// @param [in] surface Reset state will be on this surface - /// @param [in] navDir Navigation direction /// @param [in] stepSize Step size void resetState( State& state, const BoundVector& boundParams, const BoundSymMatrix& cov, - const Surface& surface, const Direction navDir = Direction::Forward, + const Surface& surface, const double stepSize = std::numeric_limits::max()) const { // Update the stepping state update(state, detail::transformBoundToFreeParameters(surface, state.geoContext, boundParams), boundParams, cov, surface); - state.navDir = navDir; state.stepSize = ConstrainedStep(stepSize); state.pathAccumulated = 0.; @@ -383,15 +375,17 @@ class AtlasStepper { /// /// @param [in,out] state The stepping state (thread-local cache) /// @param [in] surface The surface provided + /// @param [in] navDir The navigation direction /// @param [in] bcheck The boundary check for this status update - /// @param [in] logger Logger instance to use /// @param [in] surfaceTolerance Surface tolerance used for intersection + /// @param [in] logger Logger instance to use Intersection3D::Status updateSurfaceStatus( - State& state, const Surface& surface, const BoundaryCheck& bcheck, - const Logger& logger = getDummyLogger(), - ActsScalar surfaceTolerance = s_onSurfaceTolerance) const { + State& state, const Surface& surface, Direction navDir, + const BoundaryCheck& bcheck, + ActsScalar surfaceTolerance = s_onSurfaceTolerance, + const Logger& logger = getDummyLogger()) const { return detail::updateSingleSurfaceStatus( - *this, state, surface, bcheck, logger, surfaceTolerance); + *this, state, surface, navDir, bcheck, surfaceTolerance, logger); } /// Update step size @@ -1113,7 +1107,7 @@ class AtlasStepper { Result step(propagator_state_t& state, const navigator_t& /*navigator*/) const { // we use h for keeping the nominclature with the original atlas code - auto h = state.stepping.stepSize.value() * state.stepping.navDir; + auto h = state.stepping.stepSize.value() * state.options.direction; bool Jac = state.stepping.useJacobian; double* R = &(state.stepping.pVector[0]); // Coordinates @@ -1225,7 +1219,7 @@ class AtlasStepper { if (std::abs(EST) > std::abs(state.options.tolerance)) { h = h * .5; // neutralize the sign of h again - state.stepping.stepSize.setValue(h * state.stepping.navDir); + state.stepping.stepSize.setValue(h * state.options.direction); // dltm = 0.; nStepTrials++; continue; diff --git a/Core/include/Acts/Propagator/DirectNavigator.hpp b/Core/include/Acts/Propagator/DirectNavigator.hpp index 8b91dc67336..3c2a4cfe9c8 100644 --- a/Core/include/Acts/Propagator/DirectNavigator.hpp +++ b/Core/include/Acts/Propagator/DirectNavigator.hpp @@ -221,8 +221,9 @@ class DirectNavigator { if (state.navigation.navSurfaceIter != state.navigation.navSurfaces.end()) { // Establish & update the surface status auto surfaceStatus = stepper.updateSurfaceStatus( - state.stepping, **state.navigation.navSurfaceIter, false, *m_logger, - state.options.targetTolerance); + state.stepping, **state.navigation.navSurfaceIter, + state.options.direction, false, state.options.targetTolerance, + *m_logger); if (surfaceStatus == Intersection3D::Status::unreachable) { ACTS_VERBOSE( "Surface not reachable anymore, switching to next one in " @@ -269,8 +270,9 @@ class DirectNavigator { if (state.navigation.navSurfaceIter != state.navigation.navSurfaces.end()) { // Establish the surface status auto surfaceStatus = stepper.updateSurfaceStatus( - state.stepping, **state.navigation.navSurfaceIter, false, *m_logger, - state.options.targetTolerance); + state.stepping, **state.navigation.navSurfaceIter, + state.options.direction, false, state.options.targetTolerance, + *m_logger); if (surfaceStatus == Intersection3D::Status::onSurface) { // Set the current surface state.navigation.currentSurface = *state.navigation.navSurfaceIter; diff --git a/Core/include/Acts/Propagator/EigenStepper.hpp b/Core/include/Acts/Propagator/EigenStepper.hpp index 7ca2e54c60c..9a140d49e3e 100644 --- a/Core/include/Acts/Propagator/EigenStepper.hpp +++ b/Core/include/Acts/Propagator/EigenStepper.hpp @@ -69,7 +69,6 @@ class EigenStepper { /// @param [in] gctx is the context object for the geometry /// @param [in] fieldCacheIn is the cache object for the magnetic field /// @param [in] par The track parameters at start - /// @param [in] ndir The navigation direction w.r.t momentum /// @param [in] ssize is the maximum step size /// /// @note the covariance matrix is copied when needed @@ -77,10 +76,8 @@ class EigenStepper { explicit State(const GeometryContext& gctx, MagneticFieldProvider::Cache fieldCacheIn, const GenericBoundTrackParameters& par, - Direction ndir = Direction::Forward, double ssize = std::numeric_limits::max()) : absCharge(std::abs(par.charge())), - navDir(ndir), stepSize(ssize), fieldCache(std::move(fieldCacheIn)), geoContext(gctx) { @@ -111,9 +108,6 @@ class EigenStepper { bool covTransport = false; Covariance cov = Covariance::Zero(); - /// Navigation direction, this is needed for searching - Direction navDir; - /// The full jacobian of the transport entire transport Jacobian jacobian = Jacobian::Identity(); @@ -168,7 +162,6 @@ class EigenStepper { State makeState(std::reference_wrapper gctx, std::reference_wrapper mctx, const GenericBoundTrackParameters& par, - Direction navDir = Direction::Forward, double ssize = std::numeric_limits::max()) const; /// @brief Resets the state @@ -177,11 +170,10 @@ class EigenStepper { /// @param [in] boundParams Parameters in bound parametrisation /// @param [in] cov Covariance matrix /// @param [in] surface The reference surface of the bound parameters - /// @param [in] navDir Navigation direction /// @param [in] stepSize Step size void resetState( State& state, const BoundVector& boundParams, const BoundSymMatrix& cov, - const Surface& surface, const Direction navDir = Direction::Forward, + const Surface& surface, const double stepSize = std::numeric_limits::max()) const; /// Get the field for the stepping, it checks first if the access is still @@ -248,15 +240,17 @@ class EigenStepper { /// /// @param [in,out] state The stepping state (thread-local cache) /// @param [in] surface The surface provided + /// @param [in] navDir The navigation direction /// @param [in] bcheck The boundary check for this status update - /// @param [in] logger A @c Logger instance /// @param [in] surfaceTolerance Surface tolerance used for intersection + /// @param [in] logger A @c Logger instance Intersection3D::Status updateSurfaceStatus( - State& state, const Surface& surface, const BoundaryCheck& bcheck, - const Logger& logger = getDummyLogger(), - ActsScalar surfaceTolerance = s_onSurfaceTolerance) const { + State& state, const Surface& surface, Direction navDir, + const BoundaryCheck& bcheck, + ActsScalar surfaceTolerance = s_onSurfaceTolerance, + const Logger& logger = getDummyLogger()) const { return detail::updateSingleSurfaceStatus( - *this, state, surface, bcheck, logger, surfaceTolerance); + *this, state, surface, navDir, bcheck, surfaceTolerance, logger); } /// Update step size diff --git a/Core/include/Acts/Propagator/EigenStepper.ipp b/Core/include/Acts/Propagator/EigenStepper.ipp index b5a60342d59..cdf1a9e0c67 100644 --- a/Core/include/Acts/Propagator/EigenStepper.ipp +++ b/Core/include/Acts/Propagator/EigenStepper.ipp @@ -19,9 +19,9 @@ template auto Acts::EigenStepper::makeState( std::reference_wrapper gctx, std::reference_wrapper mctx, - const GenericBoundTrackParameters& par, Direction navDir, - double ssize) const -> State { - return State{gctx, m_bField->makeCache(mctx), par, navDir, ssize}; + const GenericBoundTrackParameters& par, double ssize) const + -> State { + return State{gctx, m_bField->makeCache(mctx), par, ssize}; } template @@ -29,14 +29,12 @@ void Acts::EigenStepper::resetState(State& state, const BoundVector& boundParams, const BoundSymMatrix& cov, const Surface& surface, - const Direction navDir, const double stepSize) const { // Update the stepping state update(state, detail::transformBoundToFreeParameters(surface, state.geoContext, boundParams), boundParams, cov, surface); - state.navDir = navDir; state.stepSize = ConstrainedStep(stepSize); state.pathAccumulated = 0.; @@ -147,7 +145,7 @@ Acts::Result Acts::EigenStepper::step( constexpr auto success = &Result::success; constexpr auto failure = &Result::failure; - const double h = step.value() * state.stepping.navDir; + const double h = step.value() * state.options.direction; // State the square and half of the step size h2 = h * h; half_h = h * 0.5; @@ -230,7 +228,7 @@ Acts::Result Acts::EigenStepper::step( } // use the adjusted step size - const double h = state.stepping.stepSize.value() * state.stepping.navDir; + const double h = state.stepping.stepSize.value() * state.options.direction; // When doing error propagation, update the associated Jacobian matrix if (state.stepping.covTransport) { diff --git a/Core/include/Acts/Propagator/MaterialInteractor.hpp b/Core/include/Acts/Propagator/MaterialInteractor.hpp index ebcc2579c56..52ce7f2966f 100644 --- a/Core/include/Acts/Propagator/MaterialInteractor.hpp +++ b/Core/include/Acts/Propagator/MaterialInteractor.hpp @@ -106,7 +106,7 @@ struct MaterialInteractor { stepper.transportCovarianceToCurvilinear(state.stepping); } // Change the noise updater depending on the navigation direction - NoiseUpdateMode mode = (state.stepping.navDir == Direction::Forward) + NoiseUpdateMode mode = (state.options.direction == Direction::Forward) ? addNoise : removeNoise; // Apply the material interactions diff --git a/Core/include/Acts/Propagator/MultiEigenStepperLoop.hpp b/Core/include/Acts/Propagator/MultiEigenStepperLoop.hpp index 41dea12c979..8b0c02af1e7 100644 --- a/Core/include/Acts/Propagator/MultiEigenStepperLoop.hpp +++ b/Core/include/Acts/Propagator/MultiEigenStepperLoop.hpp @@ -12,6 +12,7 @@ #include "Acts/Utilities/detail/ReferenceWrapperAnyCompat.hpp" #include "Acts/Definitions/Algebra.hpp" +#include "Acts/Definitions/Direction.hpp" #include "Acts/Definitions/TrackParametrization.hpp" #include "Acts/Definitions/Units.hpp" #include "Acts/EventData/MultiComponentBoundTrackParameters.hpp" @@ -273,7 +274,6 @@ class MultiEigenStepperLoop SmallVector components; bool covTransport = false; - Direction navDir; double pathAccumulated = 0.; std::size_t steps = 0; @@ -298,7 +298,6 @@ class MultiEigenStepperLoop /// @param [in] mctx is the context object for the magnetic field /// @param [in] bfield the shared magnetic filed provider /// @param [in] multipars The track multi-component track-parameters at start - /// @param [in] ndir The navigation direction w.r.t momentum /// @param [in] ssize is the maximum step size /// /// @note the covariance matrix is copied when needed @@ -307,9 +306,8 @@ class MultiEigenStepperLoop const GeometryContext& gctx, const MagneticFieldContext& mctx, const std::shared_ptr& bfield, const MultiComponentBoundTrackParameters& multipars, - Direction ndir = Direction::Forward, double ssize = std::numeric_limits::max()) - : navDir(ndir), geoContext(gctx), magContext(mctx) { + : geoContext(gctx), magContext(mctx) { if (multipars.components().empty()) { throw std::invalid_argument( "Cannot construct MultiEigenStepperLoop::State with empty " @@ -321,7 +319,7 @@ class MultiEigenStepperLoop for (auto i = 0ul; i < multipars.components().size(); ++i) { const auto [weight, singlePars] = multipars[i]; components.push_back({SingleState(gctx, bfield->makeCache(mctx), - std::move(singlePars), ndir, ssize), + std::move(singlePars), ssize), weight, Intersection3D::Status::onSurface}); } @@ -336,9 +334,8 @@ class MultiEigenStepperLoop State makeState(std::reference_wrapper gctx, std::reference_wrapper mctx, const MultiComponentBoundTrackParameters& par, - Direction navDir = Direction::Forward, double ssize = std::numeric_limits::max()) const { - return State(gctx, mctx, SingleStepper::m_bField, par, navDir, ssize); + return State(gctx, mctx, SingleStepper::m_bField, par, ssize); } /// @brief Resets the state @@ -347,15 +344,14 @@ class MultiEigenStepperLoop /// @param [in] boundParams Parameters in bound parametrisation /// @param [in] cov Covariance matrix /// @param [in] surface The reference surface of the bound parameters - /// @param [in] navDir Navigation direction /// @param [in] stepSize Step size void resetState( State& state, const BoundVector& boundParams, const BoundSymMatrix& cov, - const Surface& surface, const Direction navDir = Direction::Forward, + const Surface& surface, const double stepSize = std::numeric_limits::max()) const { for (auto& component : state.components) { SingleStepper::resetState(component.state, boundParams, cov, surface, - navDir, stepSize); + stepSize); } } @@ -616,8 +612,8 @@ class MultiEigenStepperLoop double weight) const { state.components.push_back( {SingleState(state.geoContext, - SingleStepper::m_bField->makeCache(state.magContext), pars, - state.navDir), + SingleStepper::m_bField->makeCache(state.magContext), + pars), weight, Intersection3D::Status::onSurface}); return ComponentProxy{state.components.back(), state}; @@ -683,22 +679,25 @@ class MultiEigenStepperLoop /// It checks the status to the reference surface & updates /// the step size accordingly /// - /// @param state [in,out] The stepping state (thread-local cache) - /// @param surface [in] The surface provided - /// @param bcheck [in] The boundary check for this status update - /// @param logger [in] A @c Logger instance + /// @param [in,out] state The stepping state (thread-local cache) + /// @param [in] surface The surface provided + /// @param [in] navDir The navigation direction + /// @param [in] bcheck The boundary check for this status update /// @param [in] surfaceTolerance Surface tolerance used for intersection + /// @param [in] logger A @c Logger instance Intersection3D::Status updateSurfaceStatus( - State& state, const Surface& surface, const BoundaryCheck& bcheck, - const Logger& logger = getDummyLogger(), - ActsScalar surfaceTolerance = s_onSurfaceTolerance) const { + State& state, const Surface& surface, Direction navDir, + const BoundaryCheck& bcheck, + ActsScalar surfaceTolerance = s_onSurfaceTolerance, + const Logger& logger = getDummyLogger()) const { using Status = Intersection3D::Status; std::array counts = {0, 0, 0, 0}; for (auto& component : state.components) { component.status = detail::updateSingleSurfaceStatus( - *this, component.state, surface, bcheck, logger, surfaceTolerance); + *this, component.state, surface, navDir, bcheck, surfaceTolerance, + logger); ++counts[static_cast(component.status)]; } diff --git a/Core/include/Acts/Propagator/MultiStepperAborters.hpp b/Core/include/Acts/Propagator/MultiStepperAborters.hpp index 5b691995900..ac8f3806dfe 100644 --- a/Core/include/Acts/Propagator/MultiStepperAborters.hpp +++ b/Core/include/Acts/Propagator/MultiStepperAborters.hpp @@ -80,7 +80,7 @@ struct MultiStepperSurfaceReached { if (averageOnSurface) { const auto sIntersection = targetSurface.intersect( state.geoContext, stepper.position(state.stepping), - state.stepping.navDir * stepper.direction(state.stepping), true, + state.options.direction * stepper.direction(state.stepping), true, averageOnSurfaceTolerance); if (sIntersection.intersection.status == diff --git a/Core/include/Acts/Propagator/Navigator.hpp b/Core/include/Acts/Propagator/Navigator.hpp index 6f7b561c5fb..44754c76a6d 100644 --- a/Core/include/Acts/Propagator/Navigator.hpp +++ b/Core/include/Acts/Propagator/Navigator.hpp @@ -551,7 +551,7 @@ class Navigator { auto boundary = state.navigation.navBoundary().object; state.navigation.currentVolume = boundary->attachedVolume( state.geoContext, stepper.position(state.stepping), - stepper.direction(state.stepping), state.stepping.navDir); + stepper.direction(state.stepping), state.options.direction); // No volume anymore : end of known world if (!state.navigation.currentVolume) { ACTS_VERBOSE( @@ -622,9 +622,9 @@ class Navigator { // Check if we are at a surface // If we are on the surface pointed at by the index, we can make // it the current one to pass it to the other actors - auto surfaceStatus = - stepper.updateSurfaceStatus(state.stepping, *surface, true, logger(), - state.options.targetTolerance); + auto surfaceStatus = stepper.updateSurfaceStatus( + state.stepping, *surface, state.options.direction, true, + state.options.targetTolerance, logger()); if (surfaceStatus == Intersection3D::Status::onSurface) { ACTS_VERBOSE(volInfo(state) << "Status Surface successfully hit, storing it."); @@ -713,9 +713,9 @@ class Navigator { break; } } - auto surfaceStatus = - stepper.updateSurfaceStatus(state.stepping, *surface, boundaryCheck, - logger(), state.options.targetTolerance); + auto surfaceStatus = stepper.updateSurfaceStatus( + state.stepping, *surface, state.options.direction, boundaryCheck, + state.options.targetTolerance, logger()); if (surfaceStatus == Intersection3D::Status::reachable) { ACTS_VERBOSE(volInfo(state) << "Surface reachable, step size updated to " @@ -787,7 +787,7 @@ class Navigator { if (state.navigation.currentVolume->hasBoundingVolumeHierarchy()) { // has hierarchy, use that, skip layer resolution NavigationOptions navOpts( - state.stepping.navDir, true, m_cfg.resolveSensitive, + state.options.direction, true, m_cfg.resolveSensitive, m_cfg.resolveMaterial, m_cfg.resolvePassive, nullptr, state.navigation.targetSurface); navOpts.overstepLimit = stepper.overstepLimit(state.stepping); @@ -806,7 +806,7 @@ class Navigator { // ~ non-zero field double ir = (dir.cross(B).norm()) * q / mom; double s; - if (state.stepping.navDir == Direction::Forward) { + if (state.options.direction == Direction::Forward) { s = state.stepping.stepSize.max(); } else { s = state.stepping.stepSize.min(); @@ -878,9 +878,9 @@ class Navigator { } } // Try to step towards it - auto layerStatus = - stepper.updateSurfaceStatus(state.stepping, *layerSurface, true, - logger(), state.options.targetTolerance); + auto layerStatus = stepper.updateSurfaceStatus( + state.stepping, *layerSurface, state.options.direction, true, + state.options.targetTolerance, logger()); if (layerStatus == Intersection3D::Status::reachable) { ACTS_VERBOSE(volInfo(state) << "Layer reachable, step size updated to " << stepper.outputStepSize(state.stepping)); @@ -967,7 +967,7 @@ class Navigator { // Helper function to find boundaries auto findBoundaries = [&]() -> bool { // The navigation options - NavigationOptions navOpts(state.stepping.navDir, true); + NavigationOptions navOpts(state.options.direction, true); navOpts.pathLimit = stepper.getStepSize(state.stepping, ConstrainedStep::aborter); navOpts.overstepLimit = stepper.overstepLimit(state.stepping); @@ -1020,9 +1020,9 @@ class Navigator { // That is the current boundary surface auto boundarySurface = state.navigation.navBoundary().representation; // Step towards the boundary surfrace - auto boundaryStatus = - stepper.updateSurfaceStatus(state.stepping, *boundarySurface, true, - logger(), state.options.targetTolerance); + auto boundaryStatus = stepper.updateSurfaceStatus( + state.stepping, *boundarySurface, state.options.direction, true, + state.options.targetTolerance, logger()); if (boundaryStatus == Intersection3D::Status::reachable) { ACTS_VERBOSE(volInfo(state) << "Boundary reachable, step size updated to " @@ -1103,7 +1103,7 @@ class Navigator { // target volume and layer search through global search auto targetIntersection = state.navigation.targetSurface->intersect( state.geoContext, stepper.position(state.stepping), - state.stepping.navDir * stepper.direction(state.stepping), false, + state.options.direction * stepper.direction(state.stepping), false, state.options.targetTolerance); if (targetIntersection) { ACTS_VERBOSE(volInfo(state) @@ -1152,7 +1152,7 @@ class Navigator { auto startSurface = onStart ? state.navigation.startSurface : layerSurface; // Use navigation parameters and NavigationOptions NavigationOptions navOpts( - state.stepping.navDir, true, m_cfg.resolveSensitive, + state.options.direction, true, m_cfg.resolveSensitive, m_cfg.resolveMaterial, m_cfg.resolvePassive, startSurface, state.navigation.targetSurface); @@ -1233,7 +1233,7 @@ class Navigator { // Create the navigation options // - and get the compatible layers, start layer will be excluded NavigationOptions navOpts( - state.stepping.navDir, m_cfg.boundaryCheckLayerResolving, + state.options.direction, m_cfg.boundaryCheckLayerResolving, m_cfg.resolveSensitive, m_cfg.resolveMaterial, m_cfg.resolvePassive, startLayer, nullptr); // Set also the target surface @@ -1319,8 +1319,9 @@ class Navigator { return true; } auto targetStatus = stepper.updateSurfaceStatus( - state.stepping, *state.navigation.targetSurface, true, logger(), - state.options.targetTolerance); + state.stepping, *state.navigation.targetSurface, + state.options.direction, true, state.options.targetTolerance, + logger()); // the only advance could have been to the target if (targetStatus == Intersection3D::Status::onSurface) { // set the target surface diff --git a/Core/include/Acts/Propagator/Propagator.ipp b/Core/include/Acts/Propagator/Propagator.ipp index 6bd69a628d7..718d03dcc96 100644 --- a/Core/include/Acts/Propagator/Propagator.ipp +++ b/Core/include/Acts/Propagator/Propagator.ipp @@ -146,7 +146,7 @@ auto Acts::Propagator::propagate( StateType state{ eOptions, m_stepper.makeState(eOptions.geoContext, eOptions.magFieldContext, start, - eOptions.direction, eOptions.maxStepSize), + eOptions.maxStepSize), m_navigator.makeState(&start.referenceSurface(), nullptr)}; static_assert( @@ -232,7 +232,7 @@ auto Acts::Propagator::propagate( StateType state{ eOptions, m_stepper.makeState(eOptions.geoContext, eOptions.magFieldContext, start, - eOptions.direction, eOptions.maxStepSize), + eOptions.maxStepSize), m_navigator.makeState(&start.referenceSurface(), &target)}; static_assert( diff --git a/Core/include/Acts/Propagator/StandardAborters.hpp b/Core/include/Acts/Propagator/StandardAborters.hpp index 8d6a6137cc8..fe126f8a685 100644 --- a/Core/include/Acts/Propagator/StandardAborters.hpp +++ b/Core/include/Acts/Propagator/StandardAborters.hpp @@ -140,7 +140,7 @@ struct SurfaceReached { const double tolerance = state.options.targetTolerance; const auto sIntersection = targetSurface.intersect( state.geoContext, stepper.position(state.stepping), - state.stepping.navDir * stepper.direction(state.stepping), true, + state.options.direction * stepper.direction(state.stepping), true, tolerance); // The target is reached diff --git a/Core/include/Acts/Propagator/StepperConcept.hpp b/Core/include/Acts/Propagator/StepperConcept.hpp index 88430a44171..511cbf836a9 100644 --- a/Core/include/Acts/Propagator/StepperConcept.hpp +++ b/Core/include/Acts/Propagator/StepperConcept.hpp @@ -64,8 +64,6 @@ using cov_transport_t = decltype(std::declval().covTransport); template using cov_t = decltype(std::declval().cov); template -using nav_dir_t = decltype(std::declval().navDir); -template using path_accumulated_t = decltype(std::declval().pathAccumulated); template using step_size_t = decltype(std::declval().stepSize); @@ -75,7 +73,6 @@ using step_size_t = decltype(std::declval().stepSize); constexpr bool StepperStateConcept = require, has_member, - has_member, has_member//, // has_member >; @@ -85,7 +82,6 @@ using step_size_t = decltype(std::declval().stepSize); template constexpr bool MultiStepperStateConcept= require< has_member, - has_member, has_member >; // clang-format on @@ -103,7 +99,7 @@ constexpr bool MultiStepperStateConcept= require< static_assert(bound_state_exists, "BoundState type not found"); constexpr static bool curvilinear_state_exists = exists; static_assert(curvilinear_state_exists, "CurvilinearState type not found"); - constexpr static bool reset_state_exists = has_method; + constexpr static bool reset_state_exists = has_method; static_assert(reset_state_exists, "resetState method not found"); constexpr static bool position_exists = has_method; static_assert(position_exists, "position method not found"); @@ -128,7 +124,7 @@ constexpr bool MultiStepperStateConcept= require< constexpr static bool covariance_transport_exists = require, has_method>; static_assert(covariance_transport_exists, "covarianceTransport method not found"); - constexpr static bool update_surface_exists = has_method; + constexpr static bool update_surface_exists = has_method; static_assert(update_surface_exists, "updateSurfaceStatus method not found"); constexpr static bool set_step_size_exists = has_method; static_assert(set_step_size_exists, "setStepSize method not found"); diff --git a/Core/include/Acts/Propagator/StraightLineStepper.hpp b/Core/include/Acts/Propagator/StraightLineStepper.hpp index ad475d40c5b..434d572184e 100644 --- a/Core/include/Acts/Propagator/StraightLineStepper.hpp +++ b/Core/include/Acts/Propagator/StraightLineStepper.hpp @@ -65,7 +65,6 @@ class StraightLineStepper { /// @param [in] gctx is the context object for the geometry /// @param [in] mctx is the context object for the magnetic field /// @param [in] par The track parameters at start - /// @param [in] ndir The navigation direction w.r.t momentum /// @param [in] ssize is the maximum step size /// @param [in] stolerance is the stepping tolerance /// @@ -74,11 +73,9 @@ class StraightLineStepper { explicit State(const GeometryContext& gctx, const MagneticFieldContext& mctx, const GenericBoundTrackParameters& par, - Direction ndir = Direction::Forward, double ssize = std::numeric_limits::max(), double stolerance = s_onSurfaceTolerance) : absCharge(std::abs(par.charge())), - navDir(ndir), stepSize(ssize), tolerance(stolerance), geoContext(gctx) { @@ -119,9 +116,6 @@ class StraightLineStepper { bool covTransport = false; Covariance cov = Covariance::Zero(); - /// Navigation direction, this is needed for searching - Direction navDir; - /// accummulated path length state double pathAccumulated = 0.; @@ -148,10 +142,9 @@ class StraightLineStepper { State makeState(std::reference_wrapper gctx, std::reference_wrapper mctx, const GenericBoundTrackParameters& par, - Direction navDir = Direction::Forward, double ssize = std::numeric_limits::max(), double stolerance = s_onSurfaceTolerance) const { - return State{gctx, mctx, par, navDir, ssize, stolerance}; + return State{gctx, mctx, par, ssize, stolerance}; } /// @brief Resets the state @@ -160,11 +153,10 @@ class StraightLineStepper { /// @param [in] boundParams Parameters in bound parametrisation /// @param [in] cov Covariance matrix /// @param [in] surface The reset @c State will be on this surface - /// @param [in] navDir Navigation direction /// @param [in] stepSize Step size void resetState( State& state, const BoundVector& boundParams, const BoundSymMatrix& cov, - const Surface& surface, const Direction navDir = Direction::Forward, + const Surface& surface, const double stepSize = std::numeric_limits::max()) const; /// Get the field for the stepping, this gives back a zero field @@ -242,15 +234,17 @@ class StraightLineStepper { /// /// @param [in,out] state The stepping state (thread-local cache) /// @param [in] surface The surface provided + /// @param [in] navDir The navigation direction /// @param [in] bcheck The boundary check for this status update - /// @param [in] logger A logger instance /// @param [in] surfaceTolerance Surface tolerance used for intersection + /// @param [in] logger A logger instance Intersection3D::Status updateSurfaceStatus( - State& state, const Surface& surface, const BoundaryCheck& bcheck, - const Logger& logger = getDummyLogger(), - ActsScalar surfaceTolerance = s_onSurfaceTolerance) const { + State& state, const Surface& surface, Direction navDir, + const BoundaryCheck& bcheck, + ActsScalar surfaceTolerance = s_onSurfaceTolerance, + const Logger& logger = getDummyLogger()) const { return detail::updateSingleSurfaceStatus( - *this, state, surface, bcheck, logger, surfaceTolerance); + *this, state, surface, navDir, bcheck, surfaceTolerance, logger); } /// Update step size @@ -395,7 +389,7 @@ class StraightLineStepper { Result step(propagator_state_t& state, const navigator_t& /*navigator*/) const { // use the adjusted step size - const auto h = state.stepping.stepSize.value() * state.stepping.navDir; + const auto h = state.stepping.stepSize.value() * state.options.direction; const double p = absoluteMomentum(state.stepping); // time propagates along distance as 1/b = sqrt(1 + m²/p²) const auto dtds = std::hypot(1., state.options.mass / p); diff --git a/Core/include/Acts/Propagator/detail/LoopProtection.hpp b/Core/include/Acts/Propagator/detail/LoopProtection.hpp index ff8f365b992..ddff0556eb8 100644 --- a/Core/include/Acts/Propagator/detail/LoopProtection.hpp +++ b/Core/include/Acts/Propagator/detail/LoopProtection.hpp @@ -41,7 +41,7 @@ void setupLoopProtection(propagator_state_t& state, const stepper_t& stepper, // Transverse component at start is taken for the loop protection const double p = stepper.absoluteMomentum(state.stepping); // Calculate the full helix path - const double helixPath = state.stepping.navDir * 2 * M_PI * p / B; + const double helixPath = state.options.direction * 2 * M_PI * p / B; // And set it as the loop limit if it overwrites the internal limit double loopLimit = state.options.loopFraction * helixPath; double pathLimit = pathAborter.internalLimit; diff --git a/Core/include/Acts/Propagator/detail/PointwiseMaterialInteraction.hpp b/Core/include/Acts/Propagator/detail/PointwiseMaterialInteraction.hpp index b7b9c5cfd2b..86247a9239c 100644 --- a/Core/include/Acts/Propagator/detail/PointwiseMaterialInteraction.hpp +++ b/Core/include/Acts/Propagator/detail/PointwiseMaterialInteraction.hpp @@ -85,7 +85,7 @@ struct PointwiseMaterialInteraction { mass(state.options.mass), pdg(state.options.absPdgCode), performCovarianceTransport(state.stepping.covTransport), - navDir(state.stepping.navDir) {} + navDir(state.options.direction) {} /// @brief This function evaluates the material properties to interact with /// diff --git a/Core/include/Acts/Propagator/detail/SteppingHelper.hpp b/Core/include/Acts/Propagator/detail/SteppingHelper.hpp index 33d1c30ad48..865aac8b11c 100644 --- a/Core/include/Acts/Propagator/detail/SteppingHelper.hpp +++ b/Core/include/Acts/Propagator/detail/SteppingHelper.hpp @@ -9,6 +9,7 @@ #pragma once #include "Acts/Definitions/Algebra.hpp" +#include "Acts/Definitions/Direction.hpp" #include "Acts/Definitions/Tolerance.hpp" #include "Acts/Propagator/ConstrainedStep.hpp" #include "Acts/Surfaces/BoundaryCheck.hpp" @@ -17,7 +18,6 @@ #include "Acts/Utilities/Logger.hpp" namespace Acts { - namespace detail { /// Update surface status - Single component @@ -33,14 +33,14 @@ namespace detail { template Acts::Intersection3D::Status updateSingleSurfaceStatus( const stepper_t& stepper, typename stepper_t::State& state, - const Surface& surface, const BoundaryCheck& bcheck, const Logger& logger, - ActsScalar surfaceTolerance) { + const Surface& surface, Direction navDir, const BoundaryCheck& bcheck, + ActsScalar surfaceTolerance, const Logger& logger) { ACTS_VERBOSE( "Update single surface status for surface: " << surface.geometryId()); auto sIntersection = surface.intersect( state.geoContext, stepper.position(state), - state.navDir * stepper.direction(state), bcheck, surfaceTolerance); + navDir * stepper.direction(state), bcheck, surfaceTolerance); // The intersection is on surface already if (sIntersection.intersection.status == Intersection3D::Status::onSurface) { diff --git a/Core/include/Acts/Propagator/detail/SteppingLogger.hpp b/Core/include/Acts/Propagator/detail/SteppingLogger.hpp index 42ba231d807..ef67180b300 100644 --- a/Core/include/Acts/Propagator/detail/SteppingLogger.hpp +++ b/Core/include/Acts/Propagator/detail/SteppingLogger.hpp @@ -72,7 +72,7 @@ struct SteppingLogger { // record the propagation state Step step; step.stepSize = state.stepping.stepSize; - step.navDir = state.stepping.navDir; + step.navDir = state.options.direction; step.position = stepper.position(state.stepping); step.momentum = stepper.momentum(state.stepping); diff --git a/Core/include/Acts/Propagator/detail/VolumeMaterialInteraction.hpp b/Core/include/Acts/Propagator/detail/VolumeMaterialInteraction.hpp index 6a954c506d5..340eddeb2c2 100644 --- a/Core/include/Acts/Propagator/detail/VolumeMaterialInteraction.hpp +++ b/Core/include/Acts/Propagator/detail/VolumeMaterialInteraction.hpp @@ -69,7 +69,7 @@ struct VolumeMaterialInteraction { mass(state.options.mass), pdg(state.options.absPdgCode), performCovarianceTransport(state.stepping.covTransport), - navDir(state.stepping.navDir) {} + navDir(state.options.direction) {} /// @brief This function evaluates the material properties to interact with /// diff --git a/Core/include/Acts/TrackFinding/CombinatorialKalmanFilter.hpp b/Core/include/Acts/TrackFinding/CombinatorialKalmanFilter.hpp index 1164a3144ba..a7f6d194536 100644 --- a/Core/include/Acts/TrackFinding/CombinatorialKalmanFilter.hpp +++ b/Core/include/Acts/TrackFinding/CombinatorialKalmanFilter.hpp @@ -547,7 +547,7 @@ class CombinatorialKalmanFilter { result.iSmoothed++; // Reverse navigation direction to start targeting for the rest // tracks - state.stepping.navDir = state.stepping.navDir.invert(); + state.options.direction = state.options.direction.invert(); // TODO this is kinda silly but I dont see a better solution // with the current CKF control flow @@ -583,7 +583,7 @@ class CombinatorialKalmanFilter { // Update the stepping state stepper.resetState(state.stepping, currentState.filtered(), currentState.filteredCovariance(), - currentState.referenceSurface(), state.stepping.navDir, + currentState.referenceSurface(), state.options.maxStepSize); // Reset the navigation state @@ -591,7 +591,7 @@ class CombinatorialKalmanFilter { // after smoothing navigator.resetState( state.navigation, state.geoContext, stepper.position(state.stepping), - stepper.direction(state.stepping), state.stepping.navDir, + stepper.direction(state.stepping), state.options.direction, ¤tState.referenceSurface(), nullptr); // No Kalman filtering for the starting surface, but still need @@ -1188,7 +1188,7 @@ class CombinatorialKalmanFilter { auto target = [&](const FreeVector& freeVector) -> SurfaceIntersection { return smoothingTargetSurface->intersect( state.geoContext, freeVector.segment<3>(eFreePos0), - state.stepping.navDir * freeVector.segment<3>(eFreeDir0), true, + state.options.direction * freeVector.segment<3>(eFreeDir0), true, state.options.targetTolerance); }; @@ -1240,7 +1240,7 @@ class CombinatorialKalmanFilter { ACTS_VERBOSE( "Reverse navigation direction after smoothing for reaching the " "target surface"); - state.stepping.navDir = state.stepping.navDir.invert(); + state.options.direction = state.options.direction.invert(); } // Reinitialize the stepping jacobian state.stepping.jacobian = BoundMatrix::Identity(); diff --git a/Core/include/Acts/TrackFitting/KalmanFitter.hpp b/Core/include/Acts/TrackFitting/KalmanFitter.hpp index 29effee9f69..3e7419296d1 100644 --- a/Core/include/Acts/TrackFitting/KalmanFitter.hpp +++ b/Core/include/Acts/TrackFitting/KalmanFitter.hpp @@ -362,7 +362,7 @@ class KalmanFitter { // Update: // - Waiting for a current surface auto surface = navigator.currentSurface(state.navigation); - std::string direction = state.stepping.navDir.toString(); + std::string direction = state.options.direction.toString(); if (surface != nullptr) { // Check if the surface is in the measurement map // -> Get the measurement / calibrate @@ -512,12 +512,12 @@ class KalmanFitter { result.reversed = true; // Reverse navigation direction - state.stepping.navDir = state.stepping.navDir.invert(); + state.options.direction = state.options.direction.invert(); // Reset propagator options // TODO Not sure if reset of pathLimit during propagation makes any sense state.options.pathLimit = - state.stepping.navDir * std::abs(state.options.pathLimit); + state.options.direction * std::abs(state.options.pathLimit); // Get the last measurement state and reset navigation&stepping state // based on information on this state @@ -527,8 +527,7 @@ class KalmanFitter { stepper.resetState( state.stepping, st.filtered(), reversedFilteringCovarianceScaling * st.filteredCovariance(), - st.referenceSurface(), state.stepping.navDir, - state.options.maxStepSize); + st.referenceSurface(), state.options.maxStepSize); // For the last measurement state, smoothed is filtered st.smoothed() = st.filtered(); @@ -538,7 +537,7 @@ class KalmanFitter { // Reset navigation state navigator.resetState( state.navigation, state.geoContext, stepper.position(state.stepping), - stepper.direction(state.stepping), state.stepping.navDir, + stepper.direction(state.stepping), state.options.direction, &st.referenceSurface(), targetSurface); // Update material effects for last measurement state in reversed @@ -732,7 +731,7 @@ class KalmanFitter { // If the update is successful, set covariance and auto updateRes = extensions.updater(state.geoContext, trackStateProxy, - state.stepping.navDir, logger()); + state.options.direction, logger()); if (!updateRes.ok()) { ACTS_ERROR("Backward update step failed: " << updateRes.error()); return updateRes.error(); @@ -922,7 +921,7 @@ class KalmanFitter { auto target = [&](const FreeVector& freeVector) -> SurfaceIntersection { return targetSurface->intersect( state.geoContext, freeVector.segment<3>(eFreePos0), - state.stepping.navDir * freeVector.segment<3>(eFreeDir0), true, + state.options.direction * freeVector.segment<3>(eFreeDir0), true, state.options.targetTolerance); }; @@ -973,7 +972,7 @@ class KalmanFitter { ACTS_VERBOSE( "Reverse navigation direction after smoothing for reaching the " "target surface"); - state.stepping.navDir = state.stepping.navDir.invert(); + state.options.direction = state.options.direction.invert(); } // Reset the step size state.stepping.stepSize = ConstrainedStep(state.options.maxStepSize); diff --git a/Core/include/Acts/TrackFitting/detail/GsfActor.hpp b/Core/include/Acts/TrackFitting/detail/GsfActor.hpp index bcc10fd6304..f75c547e350 100644 --- a/Core/include/Acts/TrackFitting/detail/GsfActor.hpp +++ b/Core/include/Acts/TrackFitting/detail/GsfActor.hpp @@ -366,7 +366,7 @@ struct GsfActor { // Evaluate material slab auto slab = surface.surfaceMaterial()->materialSlab( - old_bound.position(state.stepping.geoContext), state.stepping.navDir, + old_bound.position(state.stepping.geoContext), state.options.direction, MaterialUpdateStage::FullUpdate); auto pathCorrection = @@ -407,7 +407,7 @@ struct GsfActor { auto new_pars = old_bound.parameters(); const auto delta_p = [&]() { - if (state.stepping.navDir == Direction::Forward) { + if (state.options.direction == Direction::Forward) { return p_prev * (gaussian.mean - 1.); } else { return p_prev * (1. / gaussian.mean - 1.); @@ -421,7 +421,7 @@ struct GsfActor { auto new_cov = old_bound.covariance().value(); const auto varInvP = [&]() { - if (state.stepping.navDir == Direction::Forward) { + if (state.options.direction == Direction::Forward) { const auto f = 1. / (p_prev * gaussian.mean); return f * f * gaussian.var; } else { diff --git a/Core/include/Acts/TrackFitting/detail/GsfUtils.hpp b/Core/include/Acts/TrackFitting/detail/GsfUtils.hpp index 35f9ca761bb..919d591abd0 100644 --- a/Core/include/Acts/TrackFitting/detail/GsfUtils.hpp +++ b/Core/include/Acts/TrackFitting/detail/GsfUtils.hpp @@ -134,7 +134,7 @@ class ScopedGsfInfoPrinterAndChecker { << stepper.direction(state.stepping).transpose() << " and momentum " << stepper.absoluteMomentum(state.stepping) << " and charge " << stepper.charge(state.stepping)); - ACTS_VERBOSE("Propagation is in " << state.stepping.navDir << " mode"); + ACTS_VERBOSE("Propagation is in " << state.options.direction << " mode"); print_component_stats(); } diff --git a/Core/include/Acts/TrackFitting/detail/KalmanUpdateHelpers.hpp b/Core/include/Acts/TrackFitting/detail/KalmanUpdateHelpers.hpp index 10875956624..a726be9077f 100644 --- a/Core/include/Acts/TrackFitting/detail/KalmanUpdateHelpers.hpp +++ b/Core/include/Acts/TrackFitting/detail/KalmanUpdateHelpers.hpp @@ -89,7 +89,7 @@ auto kalmanHandleMeasurement( if (not extensions.outlierFinder(trackStateProxy)) { // Run Kalman update auto updateRes = extensions.updater(state.geoContext, trackStateProxy, - state.stepping.navDir, logger); + state.options.direction, logger); if (!updateRes.ok()) { ACTS_ERROR("Update step failed: " << updateRes.error()); return updateRes.error(); diff --git a/Core/src/Propagator/StraightLineStepper.cpp b/Core/src/Propagator/StraightLineStepper.cpp index bcb53e953c4..7d062a3436b 100644 --- a/Core/src/Propagator/StraightLineStepper.cpp +++ b/Core/src/Propagator/StraightLineStepper.cpp @@ -70,14 +70,12 @@ void StraightLineStepper::resetState(State& state, const BoundVector& boundParams, const BoundSymMatrix& cov, const Surface& surface, - const Direction navDir, const double stepSize) const { // Update the stepping state update(state, detail::transformBoundToFreeParameters(surface, state.geoContext, boundParams), boundParams, cov, surface); - state.navDir = navDir; state.stepSize = ConstrainedStep(stepSize); state.pathAccumulated = 0.; diff --git a/Tests/UnitTests/Core/Propagator/AtlasStepperTests.cpp b/Tests/UnitTests/Core/Propagator/AtlasStepperTests.cpp index 4df6a9e09aa..9a803f76522 100644 --- a/Tests/UnitTests/Core/Propagator/AtlasStepperTests.cpp +++ b/Tests/UnitTests/Core/Propagator/AtlasStepperTests.cpp @@ -67,6 +67,7 @@ struct MockPropagatorState { struct { double mass = 1_GeV; double tolerance = 10_um; + Direction direction = Direction::Backward; } options; }; @@ -103,8 +104,8 @@ BOOST_AUTO_TEST_SUITE(AtlasStepper) BOOST_AUTO_TEST_CASE(ConstructState) { Stepper::State state( geoCtx, magneticField->makeCache(magCtx), - CurvilinearTrackParameters(pos4, unitDir, absMom, charge), navDir, - stepSize, tolerance); + CurvilinearTrackParameters(pos4, unitDir, absMom, charge), stepSize, + tolerance); BOOST_CHECK(!state.covTransport); BOOST_CHECK_EQUAL(state.covariance, nullptr); @@ -116,7 +117,6 @@ BOOST_AUTO_TEST_CASE(ConstructState) { CHECK_CLOSE_ABS(state.pVector[5], unitDir.y(), eps); CHECK_CLOSE_ABS(state.pVector[6], unitDir.z(), eps); BOOST_CHECK_EQUAL(state.pVector[7], charge / absMom); - BOOST_CHECK_EQUAL(state.navDir, navDir); BOOST_CHECK_EQUAL(state.pathAccumulated, 0.); BOOST_CHECK_EQUAL(state.stepSize.value(), stepSize); BOOST_CHECK_EQUAL(state.previousStepSize, 0.); @@ -127,8 +127,8 @@ BOOST_AUTO_TEST_CASE(ConstructState) { BOOST_AUTO_TEST_CASE(ConstructStateWithCovariance) { Stepper::State state( geoCtx, magneticField->makeCache(magCtx), - CurvilinearTrackParameters(pos4, unitDir, absMom, charge, cov), navDir, - stepSize, tolerance); + CurvilinearTrackParameters(pos4, unitDir, absMom, charge, cov), stepSize, + tolerance); BOOST_CHECK(state.covTransport); BOOST_CHECK_EQUAL(*state.covariance, cov); @@ -140,7 +140,6 @@ BOOST_AUTO_TEST_CASE(ConstructStateWithCovariance) { CHECK_CLOSE_ABS(state.pVector[5], unitDir.y(), eps); CHECK_CLOSE_ABS(state.pVector[6], unitDir.z(), eps); BOOST_CHECK_EQUAL(state.pVector[7], charge / absMom); - BOOST_CHECK_EQUAL(state.navDir, navDir); BOOST_CHECK_EQUAL(state.pathAccumulated, 0.); BOOST_CHECK_EQUAL(state.stepSize.value(), stepSize); BOOST_CHECK_EQUAL(state.previousStepSize, 0.); @@ -152,8 +151,8 @@ BOOST_AUTO_TEST_CASE(Getters) { Stepper stepper(magneticField); Stepper::State state( geoCtx, magneticField->makeCache(magCtx), - CurvilinearTrackParameters(pos4, unitDir, absMom, charge, cov), navDir, - stepSize, tolerance); + CurvilinearTrackParameters(pos4, unitDir, absMom, charge, cov), stepSize, + tolerance); CHECK_CLOSE_ABS(stepper.position(state), pos, eps); CHECK_CLOSE_ABS(stepper.time(state), time, eps); @@ -167,8 +166,8 @@ BOOST_AUTO_TEST_CASE(UpdateFromBound) { Stepper stepper(magneticField); Stepper::State state( geoCtx, magneticField->makeCache(magCtx), - CurvilinearTrackParameters(pos4, unitDir, absMom, charge, cov), navDir, - stepSize, tolerance); + CurvilinearTrackParameters(pos4, unitDir, absMom, charge, cov), stepSize, + tolerance); auto newPos4 = (pos4 + Vector4(1_mm, 2_mm, 3_mm, 20_ns)).eval(); auto newPos = newPos4.segment<3>(ePos0); @@ -209,8 +208,8 @@ BOOST_AUTO_TEST_CASE(UpdateFromComponents) { Stepper stepper(magneticField); Stepper::State state( geoCtx, magneticField->makeCache(magCtx), - CurvilinearTrackParameters(pos4, unitDir, absMom, charge, cov), navDir, - stepSize, tolerance); + CurvilinearTrackParameters(pos4, unitDir, absMom, charge, cov), stepSize, + tolerance); auto newPos = (pos + Vector3(1_mm, 2_mm, 3_mm)).eval(); auto newTime = time + 20_ns; @@ -230,8 +229,8 @@ BOOST_AUTO_TEST_CASE(BuildBound) { Stepper stepper(magneticField); Stepper::State state( geoCtx, magneticField->makeCache(magCtx), - CurvilinearTrackParameters(pos4, unitDir, absMom, charge, cov), navDir, - stepSize, tolerance); + CurvilinearTrackParameters(pos4, unitDir, absMom, charge, cov), stepSize, + tolerance); // example surface at the current state position auto plane = Surface::makeShared(pos, unitDir); @@ -254,8 +253,8 @@ BOOST_AUTO_TEST_CASE(BuildCurvilinear) { Stepper stepper(magneticField); Stepper::State state( geoCtx, magneticField->makeCache(magCtx), - CurvilinearTrackParameters(pos4, unitDir, absMom, charge, cov), navDir, - stepSize, tolerance); + CurvilinearTrackParameters(pos4, unitDir, absMom, charge, cov), stepSize, + tolerance); auto&& [pars, jac, pathLength] = stepper.curvilinearState(state); // check parameters @@ -276,8 +275,8 @@ BOOST_AUTO_TEST_CASE(Step) { Stepper stepper(magneticField); MockPropagatorState state(Stepper::State( geoCtx, magneticField->makeCache(magCtx), - CurvilinearTrackParameters(pos4, unitDir, absMom, charge, cov), navDir, - stepSize, tolerance)); + CurvilinearTrackParameters(pos4, unitDir, absMom, charge, cov), stepSize, + tolerance)); state.stepping.covTransport = false; // ensure step does not result in an error @@ -309,8 +308,8 @@ BOOST_AUTO_TEST_CASE(StepWithCovariance) { Stepper stepper(magneticField); MockPropagatorState state(Stepper::State( geoCtx, magneticField->makeCache(magCtx), - CurvilinearTrackParameters(pos4, unitDir, absMom, charge, cov), navDir, - stepSize, tolerance)); + CurvilinearTrackParameters(pos4, unitDir, absMom, charge, cov), stepSize, + tolerance)); state.stepping.covTransport = true; // ensure step does not result in an error @@ -345,8 +344,8 @@ BOOST_AUTO_TEST_CASE(Reset) { Stepper stepper(magneticField); MockPropagatorState state(Stepper::State( geoCtx, magneticField->makeCache(magCtx), - CurvilinearTrackParameters(pos4, unitDir, absMom, charge, cov), navDir, - stepSize, tolerance)); + CurvilinearTrackParameters(pos4, unitDir, absMom, charge, cov), stepSize, + tolerance)); state.stepping.covTransport = true; // ensure step does not result in an error @@ -368,10 +367,9 @@ BOOST_AUTO_TEST_CASE(Reset) { auto copyState = [&](auto& field, const auto& other) { using field_t = std::decay_t; std::decay_t copy(geoCtx, field.makeCache(magCtx), cp, - navDir, stepSize, tolerance); + stepSize, tolerance); copy.state_ready = other.state_ready; - copy.navDir = other.navDir; copy.useJacobian = other.useJacobian; copy.step = other.step; copy.maxPathLength = other.maxPathLength; @@ -408,7 +406,7 @@ BOOST_AUTO_TEST_CASE(Reset) { Stepper::State stateCopy(copyState(*magneticField, state.stepping)); BOOST_CHECK(cp.covariance().has_value()); stepper.resetState(stateCopy, cp.parameters(), *cp.covariance(), - cp.referenceSurface(), navDir, stepSize); + cp.referenceSurface(), stepSize); // Test all components BOOST_CHECK(stateCopy.covTransport); BOOST_CHECK_EQUAL(*stateCopy.covariance, newCov); @@ -420,7 +418,6 @@ BOOST_AUTO_TEST_CASE(Reset) { std::abs(1. / freeParams[eFreeQOverP])); BOOST_CHECK_EQUAL(stepper.charge(stateCopy), stepper.charge(state.stepping)); BOOST_CHECK_EQUAL(stepper.time(stateCopy), freeParams[eFreeTime]); - BOOST_CHECK_EQUAL(stateCopy.navDir, navDir); BOOST_CHECK_EQUAL(stateCopy.pathAccumulated, 0.); BOOST_CHECK_EQUAL(stateCopy.stepSize.value(), navDir * stepSize); BOOST_CHECK_EQUAL(stateCopy.previousStepSize, @@ -430,7 +427,7 @@ BOOST_AUTO_TEST_CASE(Reset) { // Reset all possible parameters except the step size stateCopy = copyState(*magneticField, state.stepping); stepper.resetState(stateCopy, cp.parameters(), *cp.covariance(), - cp.referenceSurface(), navDir); + cp.referenceSurface()); // Test all components BOOST_CHECK(stateCopy.covTransport); BOOST_CHECK_EQUAL(*stateCopy.covariance, newCov); @@ -442,7 +439,6 @@ BOOST_AUTO_TEST_CASE(Reset) { std::abs(1. / freeParams[eFreeQOverP])); BOOST_CHECK_EQUAL(stepper.charge(stateCopy), stepper.charge(state.stepping)); BOOST_CHECK_EQUAL(stepper.time(stateCopy), freeParams[eFreeTime]); - BOOST_CHECK_EQUAL(stateCopy.navDir, navDir); BOOST_CHECK_EQUAL(stateCopy.pathAccumulated, 0.); BOOST_CHECK_EQUAL(stateCopy.stepSize.value(), std::numeric_limits::max()); @@ -465,7 +461,6 @@ BOOST_AUTO_TEST_CASE(Reset) { std::abs(1. / freeParams[eFreeQOverP])); BOOST_CHECK_EQUAL(stepper.charge(stateCopy), stepper.charge(state.stepping)); BOOST_CHECK_EQUAL(stepper.time(stateCopy), freeParams[eFreeTime]); - BOOST_CHECK_EQUAL(stateCopy.navDir, Direction::Forward); BOOST_CHECK_EQUAL(stateCopy.pathAccumulated, 0.); BOOST_CHECK_EQUAL(stateCopy.stepSize.value(), std::numeric_limits::max()); @@ -545,8 +540,8 @@ BOOST_AUTO_TEST_CASE(StepSize) { Stepper stepper(magneticField); Stepper::State state( geoCtx, magneticField->makeCache(magCtx), - CurvilinearTrackParameters(pos4, unitDir, absMom, charge, cov), navDir, - stepSize, tolerance); + CurvilinearTrackParameters(pos4, unitDir, absMom, charge, cov), stepSize, + tolerance); // TODO figure out why this fails and what it should be // BOOST_CHECK_EQUAL(stepper.overstepLimit(state), tolerance); @@ -564,21 +559,21 @@ BOOST_AUTO_TEST_CASE(StepSizeSurface) { Stepper stepper(magneticField); Stepper::State state( geoCtx, magneticField->makeCache(magCtx), - CurvilinearTrackParameters(pos4, unitDir, absMom, charge, cov), navDir, - stepSize, tolerance); + CurvilinearTrackParameters(pos4, unitDir, absMom, charge, cov), stepSize, + tolerance); auto distance = 10_mm; auto target = Surface::makeShared( pos + navDir * distance * unitDir, unitDir); - stepper.updateSurfaceStatus(state, *target, BoundaryCheck(false)); + stepper.updateSurfaceStatus(state, *target, navDir, BoundaryCheck(false)); BOOST_CHECK_EQUAL(state.stepSize.value(ConstrainedStep::actor), distance); // test the step size modification in the context of a surface stepper.updateStepSize( state, target->intersect(state.geoContext, stepper.position(state), - state.navDir * stepper.direction(state), false), + navDir * stepper.direction(state), false), false); BOOST_CHECK_EQUAL(state.stepSize.value(), distance); @@ -587,7 +582,7 @@ BOOST_AUTO_TEST_CASE(StepSizeSurface) { stepper.updateStepSize( state, target->intersect(state.geoContext, stepper.position(state), - state.navDir * stepper.direction(state), false), + navDir * stepper.direction(state), false), true); BOOST_CHECK_EQUAL(state.stepSize.value(), navDir * stepSize); } diff --git a/Tests/UnitTests/Core/Propagator/EigenStepperTests.cpp b/Tests/UnitTests/Core/Propagator/EigenStepperTests.cpp index cf3ce2c6fc7..99b5321acaf 100644 --- a/Tests/UnitTests/Core/Propagator/EigenStepperTests.cpp +++ b/Tests/UnitTests/Core/Propagator/EigenStepperTests.cpp @@ -89,7 +89,10 @@ MagneticFieldContext mfContext = MagneticFieldContext(); template struct PropState { /// @brief Constructor - PropState(stepper_state_t sState) : stepping(std::move(sState)) {} + PropState(Direction direction, stepper_state_t sState) + : stepping(std::move(sState)) { + options.direction = direction; + } /// State of the eigen stepper stepper_state_t stepping; /// Propagator options which only carry the relevant components @@ -98,6 +101,7 @@ struct PropState { double tolerance = 1e-4; double stepSizeCutOff = 0.; unsigned int maxRungeKuttaStepTrials = 10000; + Direction direction = Direction::Forward; } options; }; @@ -181,7 +185,6 @@ struct StepCollector { /// These tests are aiming to test whether the state setup is working properly BOOST_AUTO_TEST_CASE(eigen_stepper_state_test) { // Set up some variables - Direction navDir = Direction::Backward; double stepSize = 123.; auto bField = std::make_shared(Vector3(1., 2.5, 33.33)); @@ -194,7 +197,7 @@ BOOST_AUTO_TEST_CASE(eigen_stepper_state_test) { // Test charged parameters without covariance matrix CurvilinearTrackParameters cp(makeVector4(pos, time), dir, charge / absMom); EigenStepper<>::State esState(tgContext, bField->makeCache(mfContext), cp, - navDir, stepSize); + stepSize); EigenStepper<> es(bField); @@ -204,7 +207,6 @@ BOOST_AUTO_TEST_CASE(eigen_stepper_state_test) { BOOST_CHECK_EQUAL(esState.derivative, FreeVector::Zero()); BOOST_CHECK(!esState.covTransport); BOOST_CHECK_EQUAL(esState.cov, Covariance::Zero()); - BOOST_CHECK_EQUAL(esState.navDir, navDir); BOOST_CHECK_EQUAL(esState.pathAccumulated, 0.); BOOST_CHECK_EQUAL(esState.stepSize.value(), stepSize); BOOST_CHECK_EQUAL(esState.previousStepSize, 0.); @@ -213,7 +215,7 @@ BOOST_AUTO_TEST_CASE(eigen_stepper_state_test) { NeutralCurvilinearTrackParameters ncp(makeVector4(pos, time), dir, 1 / absMom); esState = EigenStepper<>::State(tgContext, bField->makeCache(mfContext), ncp, - navDir, stepSize); + stepSize); BOOST_CHECK_EQUAL(es.charge(esState), 0.); // Test with covariance matrix @@ -221,7 +223,7 @@ BOOST_AUTO_TEST_CASE(eigen_stepper_state_test) { ncp = NeutralCurvilinearTrackParameters(makeVector4(pos, time), dir, 1 / absMom, cov); esState = EigenStepper<>::State(tgContext, bField->makeCache(mfContext), ncp, - navDir, stepSize); + stepSize); BOOST_CHECK_NE(esState.jacToGlobal, BoundToFreeMatrix::Zero()); BOOST_CHECK(esState.covTransport); BOOST_CHECK_EQUAL(esState.cov, cov); @@ -248,7 +250,7 @@ BOOST_AUTO_TEST_CASE(eigen_stepper_test) { // Build the state and the stepper EigenStepper<>::State esState(tgContext, bField->makeCache(mfContext), cp, - navDir, stepSize); + stepSize); EigenStepper<> es(bField); // Test the getters @@ -306,7 +308,7 @@ BOOST_AUTO_TEST_CASE(eigen_stepper_test) { // Perform a step without and with covariance transport esState.cov = cov; - PropState ps(std::move(esState)); + PropState ps(navDir, std::move(esState)); ps.stepping.covTransport = false; es.step(ps, mockNavigator).value(); @@ -346,12 +348,11 @@ BOOST_AUTO_TEST_CASE(eigen_stepper_test) { auto copyState = [&](auto& field, const auto& state) { using field_t = std::decay_t; std::decay_t copy(tgContext, field.makeCache(mfContext), - cp, navDir, stepSize); + cp, stepSize); copy.pars = state.pars; copy.absCharge = state.absCharge; copy.covTransport = state.covTransport; copy.cov = state.cov; - copy.navDir = state.navDir; copy.jacobian = state.jacobian; copy.jacToGlobal = state.jacToGlobal; copy.jacTransport = state.jacTransport; @@ -376,7 +377,7 @@ BOOST_AUTO_TEST_CASE(eigen_stepper_test) { EigenStepper<>::State esStateCopy(copyState(*bField, ps.stepping)); BOOST_CHECK(cp2.covariance().has_value()); es.resetState(esStateCopy, cp2.parameters(), *cp2.covariance(), - cp2.referenceSurface(), navDir, stepSize2); + cp2.referenceSurface(), stepSize2); // Test all components BOOST_CHECK_NE(esStateCopy.jacToGlobal, BoundToFreeMatrix::Zero()); BOOST_CHECK_NE(esStateCopy.jacToGlobal, ps.stepping.jacToGlobal); @@ -392,7 +393,6 @@ BOOST_AUTO_TEST_CASE(eigen_stepper_test) { std::abs(1. / freeParams[eFreeQOverP])); BOOST_CHECK_EQUAL(es.charge(esStateCopy), -es.charge(ps.stepping)); BOOST_CHECK_EQUAL(es.time(esStateCopy), freeParams[eFreeTime]); - BOOST_CHECK_EQUAL(esStateCopy.navDir, navDir); BOOST_CHECK_EQUAL(esStateCopy.pathAccumulated, 0.); BOOST_CHECK_EQUAL(esStateCopy.stepSize.value(), navDir * stepSize2); BOOST_CHECK_EQUAL(esStateCopy.previousStepSize, ps.stepping.previousStepSize); @@ -400,7 +400,7 @@ BOOST_AUTO_TEST_CASE(eigen_stepper_test) { // Reset all possible parameters except the step size esStateCopy = copyState(*bField, ps.stepping); es.resetState(esStateCopy, cp2.parameters(), *cp2.covariance(), - cp2.referenceSurface(), navDir); + cp2.referenceSurface()); // Test all components BOOST_CHECK_NE(esStateCopy.jacToGlobal, BoundToFreeMatrix::Zero()); BOOST_CHECK_NE(esStateCopy.jacToGlobal, ps.stepping.jacToGlobal); @@ -416,7 +416,6 @@ BOOST_AUTO_TEST_CASE(eigen_stepper_test) { std::abs(1. / freeParams[eFreeQOverP])); BOOST_CHECK_EQUAL(es.charge(esStateCopy), -es.charge(ps.stepping)); BOOST_CHECK_EQUAL(es.time(esStateCopy), freeParams[eFreeTime]); - BOOST_CHECK_EQUAL(esStateCopy.navDir, navDir); BOOST_CHECK_EQUAL(esStateCopy.pathAccumulated, 0.); BOOST_CHECK_EQUAL(esStateCopy.stepSize.value(), std::numeric_limits::max()); @@ -441,7 +440,6 @@ BOOST_AUTO_TEST_CASE(eigen_stepper_test) { std::abs(1. / freeParams[eFreeQOverP])); BOOST_CHECK_EQUAL(es.charge(esStateCopy), -es.charge(ps.stepping)); BOOST_CHECK_EQUAL(es.time(esStateCopy), freeParams[eFreeTime]); - BOOST_CHECK_EQUAL(esStateCopy.navDir, Direction::Forward); BOOST_CHECK_EQUAL(esStateCopy.pathAccumulated, 0.); BOOST_CHECK_EQUAL(esStateCopy.stepSize.value(), std::numeric_limits::max()); @@ -454,12 +452,12 @@ BOOST_AUTO_TEST_CASE(eigen_stepper_test) { dir, charge / absMom, cov) .value(); esState = EigenStepper<>::State(tgContext, bField->makeCache(mfContext), cp, - navDir, stepSize); + stepSize); // Test the intersection in the context of a surface auto targetSurface = Surface::makeShared(pos + navDir * 2. * dir, dir); - es.updateSurfaceStatus(esState, *targetSurface, BoundaryCheck(false)); + es.updateSurfaceStatus(esState, *targetSurface, navDir, BoundaryCheck(false)); CHECK_CLOSE_ABS(esState.stepSize.value(ConstrainedStep::actor), navDir * 2., eps); @@ -467,14 +465,14 @@ BOOST_AUTO_TEST_CASE(eigen_stepper_test) { es.updateStepSize( esState, targetSurface->intersect(esState.geoContext, es.position(esState), - esState.navDir * es.direction(esState), false), + navDir * es.direction(esState), false), false); CHECK_CLOSE_ABS(esState.stepSize.value(), 2., eps); esState.stepSize.setValue(navDir * stepSize); es.updateStepSize( esState, targetSurface->intersect(esState.geoContext, es.position(esState), - esState.navDir * es.direction(esState), false), + navDir * es.direction(esState), false), true); CHECK_CLOSE_ABS(esState.stepSize.value(), 2., eps); @@ -524,8 +522,8 @@ BOOST_AUTO_TEST_CASE(eigen_stepper_test) { auto nBfield = std::make_shared(); EigenStepper<> nes(nBfield); EigenStepper<>::State nesState(tgContext, nBfield->makeCache(mfContext), cp, - navDir, stepSize); - PropState nps(copyState(*nBfield, nesState)); + stepSize); + PropState nps(navDir, copyState(*nBfield, nesState)); // Test that we can reach the minimum step size nps.options.tolerance = 1e-21; nps.options.stepSizeCutOff = 1e20; diff --git a/Tests/UnitTests/Core/Propagator/LoopProtectionTests.cpp b/Tests/UnitTests/Core/Propagator/LoopProtectionTests.cpp index 978d55d17dc..9ad5747de66 100644 --- a/Tests/UnitTests/Core/Propagator/LoopProtectionTests.cpp +++ b/Tests/UnitTests/Core/Propagator/LoopProtectionTests.cpp @@ -60,8 +60,6 @@ struct SteppingState { Vector3 pos = Vector3(0., 0., 0.); Vector3 dir = Vector3(0., 0., 1); double p = 100_MeV; - - Direction navDir = Direction::Forward; }; /// @brief mockup of stepping state @@ -102,6 +100,7 @@ struct Options { double pathLimit = std::numeric_limits::max(); bool loopProtection = true; double loopFraction = 0.5; + Direction direction = Direction::Forward; bool debug = false; std::string debugString; diff --git a/Tests/UnitTests/Core/Propagator/MultiStepperTests.cpp b/Tests/UnitTests/Core/Propagator/MultiStepperTests.cpp index 12865ee271c..235c21c5bce 100644 --- a/Tests/UnitTests/Core/Propagator/MultiStepperTests.cpp +++ b/Tests/UnitTests/Core/Propagator/MultiStepperTests.cpp @@ -74,6 +74,7 @@ struct Options { double stepSizeCutOff = 0.0; std::size_t maxRungeKuttaStepTrials = 10; double mass = 1.0; + Direction direction = defaultNDir; const Acts::Logger &logger = Acts::getDummyLogger(); }; @@ -90,11 +91,13 @@ struct DummyPropState { Navigation navigation; GeometryContext geoContext; - DummyPropState(stepper_state_t &ss) + DummyPropState(Direction direction, stepper_state_t &ss) : stepping(ss), options(Options{}), navigation(Navigation{}), - geoContext(geoCtx) {} + geoContext(geoCtx) { + options.direction = direction; + } }; template @@ -142,8 +145,7 @@ void test_multi_stepper_state() { const auto multi_pars = makeDefaultBoundPars(Cov, N, BoundVector::Ones()); - MultiState state(geoCtx, magCtx, defaultBField, multi_pars, defaultNDir, - defaultStepSize); + MultiState state(geoCtx, magCtx, defaultBField, multi_pars, defaultStepSize); MultiStepper ms(defaultBField); @@ -165,7 +167,7 @@ void test_multi_stepper_state() { BOOST_CHECK_EQUAL(cmp.pathAccumulated(), 0.); } - // navDir and the covTransport in the MultiEigenStepperLoop are redundant and + // covTransport in the MultiEigenStepperLoop is redundant and // thus not part of the interface. However, we want to check them for // consistency. if constexpr (Acts::Concepts::exists) { @@ -173,11 +175,6 @@ void test_multi_stepper_state() { for (const auto &cmp : state.components) { BOOST_CHECK(cmp.state.covTransport == Cov); } - - BOOST_CHECK_EQUAL(state.navDir, defaultNDir); - for (const auto &cmp : state.components) { - BOOST_CHECK_EQUAL(cmp.state.navDir, defaultNDir); - } } } @@ -200,9 +197,9 @@ void test_multi_stepper_state_invalid() { // Empty component vector const auto multi_pars = makeDefaultBoundPars(false, 0); - BOOST_CHECK_THROW(MultiState(geoCtx, magCtx, defaultBField, multi_pars, - defaultNDir, defaultStepSize), - std::invalid_argument); + BOOST_CHECK_THROW( + MultiState(geoCtx, magCtx, defaultBField, multi_pars, defaultStepSize), + std::invalid_argument); } BOOST_AUTO_TEST_CASE(multi_eigen_stepper_state_invalid) { @@ -229,10 +226,10 @@ void test_multi_stepper_vs_eigen_stepper() { MultiComponentBoundTrackParameters multi_pars(surface, cmps); GenericBoundTrackParameters single_pars(surface, pars, cov); - MultiState multi_state(geoCtx, magCtx, defaultBField, multi_pars, defaultNDir, + MultiState multi_state(geoCtx, magCtx, defaultBField, multi_pars, defaultStepSize); SingleStepper::State single_state(geoCtx, defaultBField->makeCache(magCtx), - single_pars, defaultNDir, defaultStepSize); + single_pars, defaultStepSize); MultiStepper multi_stepper(defaultBField); SingleStepper single_stepper(defaultBField); @@ -244,12 +241,12 @@ void test_multi_stepper_vs_eigen_stepper() { // Do some steps and check that the results match for (int i = 0; i < 10; ++i) { // Single stepper - auto single_prop_state = DummyPropState(single_state); + auto single_prop_state = DummyPropState(defaultNDir, single_state); auto single_result = single_stepper.step(single_prop_state, mockNavigator); single_stepper.transportCovarianceToCurvilinear(single_state); // Multi stepper; - auto multi_prop_state = DummyPropState(multi_state); + auto multi_prop_state = DummyPropState(defaultNDir, multi_state); auto multi_result = multi_stepper.step(multi_prop_state, mockNavigator); multi_stepper.transportCovarianceToCurvilinear(multi_state); @@ -292,9 +289,9 @@ void test_components_modifying_accessors() { const auto multi_pars = makeDefaultBoundPars(); MultiState mutable_multi_state(geoCtx, magCtx, defaultBField, multi_pars, - defaultNDir, defaultStepSize); + defaultStepSize); const MultiState const_multi_state(geoCtx, magCtx, defaultBField, multi_pars, - defaultNDir, defaultStepSize); + defaultStepSize); MultiStepper multi_stepper(defaultBField); @@ -396,18 +393,18 @@ void test_multi_stepper_surface_status_update() { .isApprox(Vector3{-1.0, 0.0, 0.0}, 1.e-10)); MultiState multi_state(geoCtx, magCtx, defaultNullBField, multi_pars, - Direction::Forward, defaultStepSize); + defaultStepSize); SingleStepper::State single_state( geoCtx, defaultNullBField->makeCache(magCtx), std::get<1>(multi_pars[0]), - Direction::Forward, defaultStepSize); + defaultStepSize); MultiStepper multi_stepper(defaultNullBField); SingleStepper single_stepper(defaultNullBField); // Update surface status and check { - auto status = - multi_stepper.updateSurfaceStatus(multi_state, *right_surface, false); + auto status = multi_stepper.updateSurfaceStatus(multi_state, *right_surface, + Direction::Forward, false); BOOST_CHECK(status == Intersection3D::Status::reachable); @@ -421,18 +418,18 @@ void test_multi_stepper_surface_status_update() { // Step forward now { - auto multi_prop_state = DummyPropState(multi_state); + auto multi_prop_state = DummyPropState(Direction::Forward, multi_state); multi_stepper.step(multi_prop_state, mockNavigator); // Single stepper - auto single_prop_state = DummyPropState(single_state); + auto single_prop_state = DummyPropState(Direction::Forward, single_state); single_stepper.step(single_prop_state, mockNavigator); } // Update surface status and check again { - auto status = - multi_stepper.updateSurfaceStatus(multi_state, *right_surface, false); + auto status = multi_stepper.updateSurfaceStatus(multi_state, *right_surface, + Direction::Forward, false); BOOST_CHECK(status == Intersection3D::Status::onSurface); @@ -446,8 +443,8 @@ void test_multi_stepper_surface_status_update() { // Start surface should be unreachable { - auto status = - multi_stepper.updateSurfaceStatus(multi_state, *start_surface, false); + auto status = multi_stepper.updateSurfaceStatus(multi_state, *start_surface, + Direction::Forward, false); BOOST_CHECK(status == Intersection3D::Status::unreachable); @@ -496,23 +493,25 @@ void test_component_bound_state() { .isApprox(Vector3{-1.0, 0.0, 0.0}, 1.e-10)); MultiState multi_state(geoCtx, magCtx, defaultNullBField, multi_pars, - Direction::Forward, defaultStepSize); + defaultStepSize); SingleStepper::State single_state( geoCtx, defaultNullBField->makeCache(magCtx), std::get<1>(multi_pars[0]), - Direction::Forward, defaultStepSize); + defaultStepSize); MultiStepper multi_stepper(defaultNullBField); SingleStepper single_stepper(defaultNullBField); // Step forward now { - multi_stepper.updateSurfaceStatus(multi_state, *right_surface, false); - auto multi_prop_state = DummyPropState(multi_state); + multi_stepper.updateSurfaceStatus(multi_state, *right_surface, + Direction::Forward, false); + auto multi_prop_state = DummyPropState(Direction::Forward, multi_state); multi_stepper.step(multi_prop_state, mockNavigator); // Single stepper - single_stepper.updateSurfaceStatus(single_state, *right_surface, false); - auto single_prop_state = DummyPropState(single_state); + single_stepper.updateSurfaceStatus(single_state, *right_surface, + Direction::Forward, false); + auto single_prop_state = DummyPropState(Direction::Forward, single_state); single_stepper.step(single_prop_state, mockNavigator); } @@ -561,7 +560,7 @@ void test_combined_bound_state_function() { cmps(4, {0.25, pars, cov}); MultiComponentBoundTrackParameters multi_pars(surface, cmps); - MultiState multi_state(geoCtx, magCtx, defaultBField, multi_pars, defaultNDir, + MultiState multi_state(geoCtx, magCtx, defaultBField, multi_pars, defaultStepSize); MultiStepper multi_stepper(defaultBField); @@ -606,7 +605,7 @@ void test_combined_curvilinear_state_function() { GenericBoundTrackParameters check_pars(surface, pars, cov); MultiComponentBoundTrackParameters multi_pars(surface, cmps); - MultiState multi_state(geoCtx, magCtx, defaultBField, multi_pars, defaultNDir, + MultiState multi_state(geoCtx, magCtx, defaultBField, multi_pars, defaultStepSize); MultiStepper multi_stepper(defaultBField); @@ -647,12 +646,12 @@ void test_single_component_interface_function() { MultiComponentBoundTrackParameters multi_pars(surface, cmps); - MultiState multi_state(geoCtx, magCtx, defaultBField, multi_pars, defaultNDir, + MultiState multi_state(geoCtx, magCtx, defaultBField, multi_pars, defaultStepSize); MultiStepper multi_stepper(defaultBField); - DummyPropState multi_prop_state(multi_state); + DummyPropState multi_prop_state(defaultNDir, multi_state); // Check at least some properties at the moment auto check = [&](auto cmp) { @@ -692,7 +691,7 @@ void remove_add_components_function() { const auto multi_pars = makeDefaultBoundPars(4); - MultiState multi_state(geoCtx, magCtx, defaultBField, multi_pars, defaultNDir, + MultiState multi_state(geoCtx, magCtx, defaultBField, multi_pars, defaultStepSize); MultiStepper multi_stepper(defaultBField); diff --git a/Tests/UnitTests/Core/Propagator/NavigatorTests.cpp b/Tests/UnitTests/Core/Propagator/NavigatorTests.cpp index 896bf8c5f2b..40e0087f68e 100644 --- a/Tests/UnitTests/Core/Propagator/NavigatorTests.cpp +++ b/Tests/UnitTests/Core/Propagator/NavigatorTests.cpp @@ -91,9 +91,6 @@ struct PropagatorState { /// Charge double q = 0; - /// the navigation direction - Direction navDir = Direction::Forward; - // accummulated path length cache double pathAccumulated = 0.; @@ -112,7 +109,6 @@ struct PropagatorState { /// State resetter void resetState(State& /*state*/, const BoundVector& /*boundParams*/, const BoundSymMatrix& /*cov*/, const Surface& /*surface*/, - const Direction /*navDir*/, const double /*stepSize*/) const {} /// Global particle position accessor @@ -145,11 +141,14 @@ struct PropagatorState { return s_onSurfaceTolerance; } - Intersection3D::Status updateSurfaceStatus( - State& state, const Surface& surface, const BoundaryCheck& bcheck, - const Logger& logger, ActsScalar surfaceTolerance) const { + Intersection3D::Status updateSurfaceStatus(State& state, + const Surface& surface, + Direction navDir, + const BoundaryCheck& bcheck, + ActsScalar surfaceTolerance, + const Logger& logger) const { return detail::updateSingleSurfaceStatus( - *this, state, surface, bcheck, logger, surfaceTolerance); + *this, state, surface, navDir, bcheck, surfaceTolerance, logger); } template @@ -236,6 +235,8 @@ struct PropagatorState { size_t debugPfxWidth = 30; size_t debugMsgWidth = 50; + Direction direction = Direction::Forward; + const Acts::Logger& logger = Acts::getDummyLogger(); ActsScalar targetTolerance = s_onSurfaceTolerance; diff --git a/Tests/UnitTests/Core/Propagator/PropagatorTests.cpp b/Tests/UnitTests/Core/Propagator/PropagatorTests.cpp index 51c1af0abda..33e29572b4e 100644 --- a/Tests/UnitTests/Core/Propagator/PropagatorTests.cpp +++ b/Tests/UnitTests/Core/Propagator/PropagatorTests.cpp @@ -114,7 +114,7 @@ struct SurfaceObserver { stepper.direction(state.stepping), true) .intersection.pathLength; // Adjust the step size so that we cannot cross the target surface - state.stepping.stepSize.update(distance * state.stepping.navDir, + state.stepping.stepSize.update(distance * state.options.direction, ConstrainedStep::actor); // return true if you fall below tolerance if (std::abs(distance) <= tolerance) { diff --git a/Tests/UnitTests/Core/Propagator/StraightLineStepperTests.cpp b/Tests/UnitTests/Core/Propagator/StraightLineStepperTests.cpp index 820a19a550a..11d7825ea47 100644 --- a/Tests/UnitTests/Core/Propagator/StraightLineStepperTests.cpp +++ b/Tests/UnitTests/Core/Propagator/StraightLineStepperTests.cpp @@ -48,12 +48,16 @@ using Jacobian = BoundMatrix; /// @brief Simplified propagator state struct PropState { /// @brief Constructor - PropState(StraightLineStepper::State sState) : stepping(std::move(sState)) {} + PropState(Direction direction, StraightLineStepper::State sState) + : stepping(std::move(sState)) { + options.direction = direction; + } /// State of the straight line stepper StraightLineStepper::State stepping; /// Propagator options which only carry the particle's mass struct { double mass = 42.; + Direction direction = Direction::Forward; } options; }; @@ -68,7 +72,6 @@ BOOST_AUTO_TEST_CASE(straight_line_stepper_state_test) { // Set up some variables GeometryContext tgContext = GeometryContext(); MagneticFieldContext mfContext = MagneticFieldContext(); - Direction navDir = Direction::Backward; double stepSize = 123.; double tolerance = 234.; @@ -80,8 +83,8 @@ BOOST_AUTO_TEST_CASE(straight_line_stepper_state_test) { // Test charged parameters without covariance matrix CurvilinearTrackParameters cp(makeVector4(pos, time), dir, absMom, charge); - StraightLineStepper::State slsState(tgContext, mfContext, cp, navDir, - stepSize, tolerance); + StraightLineStepper::State slsState(tgContext, mfContext, cp, stepSize, + tolerance); StraightLineStepper sls; @@ -96,7 +99,6 @@ BOOST_AUTO_TEST_CASE(straight_line_stepper_state_test) { CHECK_CLOSE_REL(sls.absoluteMomentum(slsState), absMom, eps); BOOST_CHECK_EQUAL(sls.charge(slsState), charge); CHECK_CLOSE_OR_SMALL(sls.time(slsState), time, eps, eps); - BOOST_CHECK_EQUAL(slsState.navDir, navDir); BOOST_CHECK_EQUAL(slsState.pathAccumulated, 0.); BOOST_CHECK_EQUAL(slsState.stepSize.value(), stepSize); BOOST_CHECK_EQUAL(slsState.previousStepSize, 0.); @@ -105,16 +107,16 @@ BOOST_AUTO_TEST_CASE(straight_line_stepper_state_test) { // Test without charge and covariance matrix NeutralCurvilinearTrackParameters ncp(makeVector4(pos, time), dir, 1 / absMom); - slsState = StraightLineStepper::State(tgContext, mfContext, ncp, navDir, - stepSize, tolerance); + slsState = StraightLineStepper::State(tgContext, mfContext, ncp, stepSize, + tolerance); BOOST_CHECK_EQUAL(slsState.absCharge, 0.); // Test with covariance matrix Covariance cov = 8. * Covariance::Identity(); ncp = NeutralCurvilinearTrackParameters(makeVector4(pos, time), dir, 1 / absMom, cov); - slsState = StraightLineStepper::State(tgContext, mfContext, ncp, navDir, - stepSize, tolerance); + slsState = StraightLineStepper::State(tgContext, mfContext, ncp, stepSize, + tolerance); BOOST_CHECK_NE(slsState.jacToGlobal, BoundToFreeMatrix::Zero()); BOOST_CHECK(slsState.covTransport); BOOST_CHECK_EQUAL(slsState.cov, cov); @@ -141,8 +143,8 @@ BOOST_AUTO_TEST_CASE(straight_line_stepper_test) { cov); // Build the state and the stepper - StraightLineStepper::State slsState(tgContext, mfContext, cp, navDir, - stepSize, tolerance); + StraightLineStepper::State slsState(tgContext, mfContext, cp, stepSize, + tolerance); StraightLineStepper sls; // Test the getters @@ -200,7 +202,7 @@ BOOST_AUTO_TEST_CASE(straight_line_stepper_test) { // Perform a step without and with covariance transport slsState.cov = cov; - PropState ps(slsState); + PropState ps(navDir, slsState); ps.stepping.covTransport = false; double h = sls.step(ps, mockNavigator).value(); @@ -247,7 +249,7 @@ BOOST_AUTO_TEST_CASE(straight_line_stepper_test) { // Reset all possible parameters StraightLineStepper::State slsStateCopy(ps.stepping); sls.resetState(slsStateCopy, cp2.parameters(), *cp2.covariance(), - cp2.referenceSurface(), navDir, stepSize2); + cp2.referenceSurface(), stepSize2); // Test all components BOOST_CHECK_NE(slsStateCopy.jacToGlobal, BoundToFreeMatrix::Zero()); BOOST_CHECK_NE(slsStateCopy.jacToGlobal, ps.stepping.jacToGlobal); @@ -263,7 +265,6 @@ BOOST_AUTO_TEST_CASE(straight_line_stepper_test) { std::abs(1. / freeParams[eFreeQOverP]), 1e-6); CHECK_CLOSE_ABS(sls.charge(slsStateCopy), -sls.charge(ps.stepping), 1e-6); CHECK_CLOSE_ABS(sls.time(slsStateCopy), freeParams[eFreeTime], 1e-6); - BOOST_CHECK_EQUAL(slsStateCopy.navDir, navDir); BOOST_CHECK_EQUAL(slsStateCopy.pathAccumulated, 0.); BOOST_CHECK_EQUAL(slsStateCopy.stepSize.value(), navDir * stepSize2); BOOST_CHECK_EQUAL(slsStateCopy.previousStepSize, @@ -273,7 +274,7 @@ BOOST_AUTO_TEST_CASE(straight_line_stepper_test) { // Reset all possible parameters except the step size slsStateCopy = ps.stepping; sls.resetState(slsStateCopy, cp2.parameters(), *cp2.covariance(), - cp2.referenceSurface(), navDir); + cp2.referenceSurface()); // Test all components BOOST_CHECK_NE(slsStateCopy.jacToGlobal, BoundToFreeMatrix::Zero()); BOOST_CHECK_NE(slsStateCopy.jacToGlobal, ps.stepping.jacToGlobal); @@ -289,7 +290,6 @@ BOOST_AUTO_TEST_CASE(straight_line_stepper_test) { std::abs(1. / freeParams[eFreeQOverP]), 1e-6); CHECK_CLOSE_ABS(sls.charge(slsStateCopy), -sls.charge(ps.stepping), 1e-6); CHECK_CLOSE_ABS(sls.time(slsStateCopy), freeParams[eFreeTime], 1e-6); - BOOST_CHECK_EQUAL(slsStateCopy.navDir, navDir); BOOST_CHECK_EQUAL(slsStateCopy.pathAccumulated, 0.); BOOST_CHECK_EQUAL(slsStateCopy.stepSize.value(), std::numeric_limits::max()); @@ -316,7 +316,6 @@ BOOST_AUTO_TEST_CASE(straight_line_stepper_test) { std::abs(1. / freeParams[eFreeQOverP]), 1e-6); CHECK_CLOSE_ABS(sls.charge(slsStateCopy), -sls.charge(ps.stepping), 1e-6); CHECK_CLOSE_ABS(sls.time(slsStateCopy), freeParams[eFreeTime], 1e-6); - BOOST_CHECK_EQUAL(slsStateCopy.navDir, Direction::Forward); BOOST_CHECK_EQUAL(slsStateCopy.pathAccumulated, 0.); BOOST_CHECK_EQUAL(slsStateCopy.stepSize.value(), std::numeric_limits::max()); @@ -330,29 +329,30 @@ BOOST_AUTO_TEST_CASE(straight_line_stepper_test) { BoundTrackParameters::create(plane, tgContext, makeVector4(pos, time), dir, charge / absMom, cov) .value(); - slsState = StraightLineStepper::State(tgContext, mfContext, cp, navDir, - stepSize, tolerance); + slsState = + StraightLineStepper::State(tgContext, mfContext, cp, stepSize, tolerance); // Test the intersection in the context of a surface auto targetSurface = Surface::makeShared(pos + navDir * 2. * dir, dir); - sls.updateSurfaceStatus(slsState, *targetSurface, BoundaryCheck(false)); + sls.updateSurfaceStatus(slsState, *targetSurface, navDir, + BoundaryCheck(false)); CHECK_CLOSE_ABS(slsState.stepSize.value(ConstrainedStep::actor), navDir * 2., 1e-6); // Test the step size modification in the context of a surface - sls.updateStepSize(slsState, - targetSurface->intersect( - slsState.geoContext, sls.position(slsState), - slsState.navDir * sls.direction(slsState), false), - false); + sls.updateStepSize( + slsState, + targetSurface->intersect(slsState.geoContext, sls.position(slsState), + navDir * sls.direction(slsState), false), + false); CHECK_CLOSE_ABS(slsState.stepSize.value(), 2, 1e-6); slsState.stepSize.setValue(navDir * stepSize); - sls.updateStepSize(slsState, - targetSurface->intersect( - slsState.geoContext, sls.position(slsState), - slsState.navDir * sls.direction(slsState), false), - true); + sls.updateStepSize( + slsState, + targetSurface->intersect(slsState.geoContext, sls.position(slsState), + navDir * sls.direction(slsState), false), + true); CHECK_CLOSE_ABS(slsState.stepSize.value(), 2, 1e-6); // Test the bound state construction diff --git a/Tests/UnitTests/Core/Propagator/VolumeMaterialInteractionTests.cpp b/Tests/UnitTests/Core/Propagator/VolumeMaterialInteractionTests.cpp index 1c4c6d21f29..750908bab58 100644 --- a/Tests/UnitTests/Core/Propagator/VolumeMaterialInteractionTests.cpp +++ b/Tests/UnitTests/Core/Propagator/VolumeMaterialInteractionTests.cpp @@ -32,7 +32,6 @@ struct StepperState { Vector3 pos, dir; double t = 0, p = 0, q = 0; bool covTransport = false; - Direction navDir = Direction::Forward; }; /// @brief Simplified navigator @@ -45,6 +44,7 @@ struct State { struct { double mass = 0; int absPdgCode = 0; + Direction direction = Direction::Forward; } options; StepperState stepping; @@ -91,9 +91,9 @@ BOOST_AUTO_TEST_CASE(volume_material_interaction_test) { state.stepping.p = 8.; state.stepping.q = 9.; state.stepping.covTransport = true; - state.stepping.navDir = Direction::Backward; state.options.mass = 10.; state.options.absPdgCode = 11; + state.options.direction = Direction::Backward; state.navigation.currentVolume = volume.get(); Stepper stepper; @@ -113,7 +113,7 @@ BOOST_AUTO_TEST_CASE(volume_material_interaction_test) { BOOST_CHECK_EQUAL(volMatInt.pdg, state.options.absPdgCode); BOOST_CHECK_EQUAL(volMatInt.performCovarianceTransport, state.stepping.covTransport); - BOOST_CHECK_EQUAL(volMatInt.navDir, state.stepping.navDir); + BOOST_CHECK_EQUAL(volMatInt.navDir, state.options.direction); // Evaluate the material bool result = volMatInt.evaluateMaterialSlab(state, navigator); From b5acca18e8eee8125329111879125c9cd5fbe7c6 Mon Sep 17 00:00:00 2001 From: Paul Gessinger Date: Fri, 4 Aug 2023 22:19:38 +0200 Subject: [PATCH 20/21] ci: Add ttbar pu 200 job to physmon (#2282) --- CI/physmon/phys_perf_mon.sh | 55 +++++- .../reference/performance_ambi_ttbar.root | Bin 0 -> 31423 bytes .../performance_amvf_orthogonal_hist.root | Bin 29106 -> 29471 bytes .../performance_amvf_seeded_hist.root | Bin 29068 -> 29436 bytes ...performance_amvf_truth_estimated_hist.root | Bin 29057 -> 29416 bytes .../performance_amvf_truth_smeared_hist.root | Bin 28616 -> 28972 bytes .../performance_amvf_ttbar_hist.root | Bin 0 -> 27043 bytes .../reference/performance_ckf_ttbar.root | Bin 0 -> 32139 bytes .../performance_ivf_orthogonal_hist.root | Bin 29508 -> 29540 bytes .../performance_ivf_seeded_hist.root | Bin 29491 -> 29858 bytes .../performance_ivf_truth_estimated_hist.root | Bin 29263 -> 29348 bytes .../performance_ivf_truth_smeared_hist.root | Bin 29218 -> 29230 bytes .../reference/performance_seeding_ttbar.root | Bin 0 -> 11376 bytes CI/physmon/vertexing_config.yml | 5 +- .../workflows/physmon_track_finding_ttbar.py | 180 ++++++++++++++++++ 15 files changed, 231 insertions(+), 9 deletions(-) create mode 100644 CI/physmon/reference/performance_ambi_ttbar.root create mode 100644 CI/physmon/reference/performance_amvf_ttbar_hist.root create mode 100644 CI/physmon/reference/performance_ckf_ttbar.root create mode 100644 CI/physmon/reference/performance_seeding_ttbar.root create mode 100755 CI/physmon/workflows/physmon_track_finding_ttbar.py diff --git a/CI/physmon/phys_perf_mon.sh b/CI/physmon/phys_perf_mon.sh index a13de11fcd0..c05bc2738ce 100755 --- a/CI/physmon/phys_perf_mon.sh +++ b/CI/physmon/phys_perf_mon.sh @@ -21,32 +21,34 @@ SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd SPYRAL_BIN="spyral" SPYRAL="${SPYRAL_BIN} run -i 0.1 --summary" -mkdir ${outdir}/memory +mkdir -p "${outdir}/memory" source $SCRIPT_DIR/setup.sh echo "::group::Generate validation dataset" if [[ "$mode" == "all" || "$mode" == "kalman" ]]; then $SPYRAL -l "Truth Tracking KF" -o "$outdir/memory/mem_truth_tracking_kalman.csv" -- CI/physmon/workflows/physmon_truth_tracking_kalman.py $outdir 2>&1 > $outdir/run_truth_tracking_kalman.log + $SPYRAL_BIN plot $outdir/memory/mem_truth_tracking_kalman.csv --output $outdir/memory fi if [[ "$mode" == "all" || "$mode" == "gsf" ]]; then $SPYRAL -l "Truth Tracking GSF" -o "$outdir/memory/mem_truth_tracking_gsf.csv" -- CI/physmon/workflows/physmon_truth_tracking_gsf.py $outdir 2>&1 > $outdir/run_truth_tracking_gsf.log + $SPYRAL_BIN plot $outdir/memory/mem_truth_tracking_gsf.csv --output $outdir/memory fi if [[ "$mode" == "all" || "$mode" == "fullchains" ]]; then $SPYRAL -l "CKF Tracking" -o "$outdir/memory/mem_ckf_tracking.csv" -- CI/physmon/workflows/physmon_ckf_tracking.py $outdir 2>&1 > $outdir/run_ckf_tracking.log + $SPYRAL -l "Track finding ttbar" -o "$outdir/memory/mem_ttbar.csv" -- CI/physmon/workflows/physmon_track_finding_ttbar.py $outdir 2>&1 > $outdir/run_track_finding_ttbar.log + $SPYRAL_BIN plot $outdir/memory/mem_ckf_tracking.csv --output $outdir/memory + $SPYRAL_BIN plot $outdir/memory/mem_ttbar.csv --output $outdir/memory fi if [[ "$mode" == "all" || "$mode" == "vertexing" ]]; then $SPYRAL -l "Vertexing" -o "$outdir/memory/mem_vertexing.csv" -- CI/physmon/workflows/physmon_vertexing.py $outdir 2>&1 > $outdir/run_vertexing.log + $SPYRAL_BIN plot $outdir/memory/mem_vertexing.csv --output $outdir/memory fi if [[ "$mode" == "all" || "$mode" == "simulation" ]]; then $SPYRAL -l "Simulation" -o "$outdir/memory/mem_simulation.csv" -- CI/physmon/workflows/physmon_simulation.py $outdir 2>&1 > $outdir/run_simulation.log + $SPYRAL_BIN plot $outdir/memory/mem_simulation.csv --output $outdir/memory fi echo "::endgroup::" -$SPYRAL_BIN plot $outdir/memory/mem_truth_tracking_kalman.csv --output $outdir/memory -$SPYRAL_BIN plot $outdir/memory/mem_truth_tracking_gsf.csv --output $outdir/memory -$SPYRAL_BIN plot $outdir/memory/mem_ckf_tracking.csv --output $outdir/memory -$SPYRAL_BIN plot $outdir/memory/mem_vertexing.csv --output $outdir/memory -$SPYRAL_BIN plot $outdir/memory/mem_simulation.csv --output $outdir/memory set +e @@ -212,6 +214,47 @@ if [[ "$mode" == "all" || "$mode" == "fullchains" ]]; then --title "Ambisolver orthogonal" \ -o $outdir/ambi_orthogonal.html \ -p $outdir/ambi_orthogonal_plots + + run \ + $outdir/performance_seeding_ttbar.root \ + $refdir/performance_seeding_ttbar.root \ + --title "Seeding ttbar" \ + -c $config \ + -o $outdir/seeding_ttbar.html \ + -p $outdir/seeding_ttbar_plots + + run \ + $outdir/performance_ckf_ttbar.root \ + $refdir/performance_ckf_ttbar.root \ + --title "CKF ttbar" \ + -c $config \ + -o $outdir/ckf_ttbar.html \ + -p $outdir/ckf_ttbar_plots + + run \ + $outdir/performance_ambi_ttbar.root \ + $refdir/performance_ambi_ttbar.root \ + --title "Ambisolver " \ + -o $outdir/ambi_ttbar.html \ + -p $outdir/ambi_ttbar_plots + + Examples/Scripts/generic_plotter.py \ + $outdir/performance_amvf_ttbar.root \ + vertexing \ + $outdir/performance_amvf_ttbar_hist.root \ + --silent \ + --config CI/physmon/vertexing_config.yml + ec=$(($ec | $?)) + + # remove ntuple file because it's large + rm $outdir/performance_amvf_ttbar.root + + run \ + $outdir/performance_amvf_ttbar_hist.root \ + $refdir/performance_amvf_ttbar_hist.root \ + --title "AMVF ttbar" \ + -o $outdir/amvf_ttbar.html \ + -p $outdir/amvf_ttbar_plots fi if [[ "$mode" == "all" || "$mode" == "gsf" ]]; then diff --git a/CI/physmon/reference/performance_ambi_ttbar.root b/CI/physmon/reference/performance_ambi_ttbar.root new file mode 100644 index 0000000000000000000000000000000000000000..f6881fb8505a28663ef5e8dad33470ec98bd1206 GIT binary patch literal 31423 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 K+2Y2L0Qw&yO4a88 literal 0 HcmV?d00001 diff --git a/CI/physmon/reference/performance_amvf_orthogonal_hist.root b/CI/physmon/reference/performance_amvf_orthogonal_hist.root index 288244ac76020e14a2e69ca347834ebcfe59d037..8555d56a7c6915786d3f5e2a83d5ccb8098edfbb 100644 GIT binary patch delta 2839 zcmcgtdsI_(9RJ<1$AyhC2-zJ>*pw8;Yce8&79hl9j%N!*DDHSQHeL=irE&}cldyox z$wy_DWvG9056SHh7DHA@PqtI zjEY{kR7D%{(AXGdRc0B!E9!ejVq>(DLrV>I;;n@x@&I@!LaSEIV`5%tYkH)qq~x=)%uR*6DIqK@m1_JKZW@pt_fo*f;y z+SnUVCV9%eXS4)G|KuG)$2wmE<;K?u1=bIIgxi)Dx%MO9HkO2Or{x`Ng2` zywzUe!*Q8zP11g}OySrXQT# zv~5b3&Qq_EITe@pM7p+m=4CA98ZSCb8@(Fda}gvDLpwoc(qZEd#(@@)tbD=a6TzId z&kHotlqCt?A6_s%&g+fQ$9H^gZyz!x1)b&S4n#G{#A~u+;p63XaPt-I)`1X%yy6*7 zkB8x9czb$pe~vkdo05bw+hiL6uU8k=0|2RSgb@S-2!K3aqL|CLhO|Y=aE-ybqCBr* z6(EA^1UCgY?*9&K?Y^c!WbXPJ^~vsnp%rL}L6jzQ>EPFQx(fLGM1CHc4MCdIr_xkS zmyb;f*2Ry=xk~W#?}=kQISV`59446BMB@?wF8U>&CtS}UU3WvMcD|c*Jzt02n;)3x z4gtiCE+a1^x)Mb{E*Yys(QG5iU93PW&@ss|VaSfU^sw}d@S_n{eP(7@Q2OyDndM;T zc@!;a=j`D1e~D~t`k|OpP?X@4ef&K;#C2kO^P_ET#R($5_^NBR&&mlir7~)|X*5)l#~{*HzD>!EP3O#)7G8 zKVd2rBR8tclr?wWQAC|#DrOO6%qoVl6BRNkMS=7Nog-y5AQ$362DVeKoBblqt zayD0Er-Z>e{mF1~uqx~*9I^uisxc}_!5k_{ZZ{_hs))7?MJV_#Yp1zE!ng|#N2hRa zF>=_c>%6AOl30{6feCqE*g)4lyUR-9E>ne4eY!ZN$mzb)lYedW%{L|v`GlmN$#3k6 z+t|l+K5_=^f4I{=2Qef#M1N?yBhe_$bmF;|O=7;pbM4VsiSxDjVo6tym1)ieY@hZ% z3BcN+EN`MzXAiqPGY|qW>poSg-BhV|&XnrdF~)se#z_y-`;mIJdeQy%Pr;W}B7MS& z2zzL_LKswW)&V#+%&v*HjY}mh_M|nOp4JzwD0+lf7|~Jk#GOYPI|H72TOt=LLMGyy z(oQ>|1Fa86;SyvbLLPpde*kWAP8#poQ2l{d>U|dD@5RER+j<|$c)!V2(s(`&eqwkK zI#g#m-ZY6z6E6Lx<-#wzZb#%jTL(lc_wn`J+mvcw&W-Ii5d?Bi88k#tP^!*ysagyr zYtb|h_ca+H5p|PF-c&YYS<54_zDjqx$OkK#;U|Xzu%CF$to1a{K7koojGdVOZgl!$ z)fSjGx7wnl!4?+y>!jFM7RK&ij1;@h9^9*#M%1w2U=|`K+zcY!blFR_lZ}1$8VioG z;4%xOZ2xLm@cu&x_EwSCgKvhd0PA5`|rjM@#5?x zt@I%tWl>z@MP)mR%=YqpTwCjeyk~oYX>0xS2*U7!fEUv-=P1I+r3fRtj3o@oNrK#A zqFqjfga@lK!qfF5>E%FmjwTH*MH=lllg7l-Xx-nKop1EM+t7R(T!9H@PvYLf>_g5O zW{E0%U+<8YW=KN?i@*Kh=DzPD)J}RmdzGy;x>8?FiaPUvjP@5b4gb#Z| zDAmyvBNs`y4M_^PVAMo?Fzv#Agdz6f)$zh2{wY>y>rNGkgaC&cg6TtPxbQ_EPtS$R zjW^5{9Htwh1Te1=f+u{y(ilPwELQpa2>RM$0tS+1kq~8OhX{zWG!nEW2ZdYMv?K<4 z?9IIiZI1kv5M5iE2xoa4RrJE;O6a@n$pN^*;&lWaw&GRj?^<~PqNd_#hw}kDA5`AY{je0lR^-B+8lKJ!n;hq1A`B$6bMBqDuNnznpAPKxcQI(Whp8&tY&K6doA zGMRg;wA?t2$%{w(r8KokudZoRhS_m}J=4 zof>3Kw9S|ukw6s@#tR*J${I!Ij<}p3GM~|!)O~)JQ}yb9h39AD+Bu%v4^zYgw^NLTWgOB8zQ`ZU`9hUHwuYbmv7)DCJ* zb8!5fWJlz$OLy5K#fEc=4Spct&(iq?;L@BhkrBbP|bN z_)y$G&^Eu%u?0nO2lZ8HniZQHiJ{U@uvl5<_YBh|dv*tDPl?Yt18#3;kKQZl_e`NL z49`>_tSrk*k+r+0J+Ft?El?WHE_!8RS}I!l?cVIixD+6hu>FdBTgGSyirOO41ae8UKu18)tqEye0O!zswt>7Yq9NWGsl~xir z64j4RW5;mY)cDQFUpBtuntKUNY0dlEjs{dSKN(VYENXjCWim*xKqWc!Tj!f{;$wMC z!DjMPH;9@f%cY(VTZWOS@TXrs-S+rIY1?=HvX zw^WK=t8H~hjx{xF9s5jtjmY?c7uly!beB>$Haz*cTMPf$k*lm zT)T@Bmg;TP2*Bk7ULQD~uA;)$!Nn8S!3o7a;95ygFZ+g@iU|b(BJn+WKjd?~>I44k zVT^@7Q0W8hKJafJP#6j7mom8eR2iHZYz}?k4$KR9c%lSTU1mS>Ue(#VX%g!Tl5%A& z&5b23n+lCB)pe{9xxcT5X0xUT8Vc7>?oP+e`sR&m*#O+!hZk*45IZE)xCcvIINjQG l8JWZWM`KF_Nd7$uEE#MPJf$pm0#N=2mnW9~zb)(m{|8vXr_=xd diff --git a/CI/physmon/reference/performance_amvf_seeded_hist.root b/CI/physmon/reference/performance_amvf_seeded_hist.root index f4d36ce451c3fc41851fc0aeb6610e8bb40dd6df..7cd7ed9e6b06840f037c52aefc7ff62f92f76ba2 100644 GIT binary patch delta 2819 zcmcgt2~bm46n#JWfsh0YsPHL^iR@8GP(UguiCBdgASkl9G(Ql6hysa#mC-`9fKar+ zr!7@%UD_&cRbyMH8#q*30kv4CwNs}?k=9zrt;@7EgucHaSY|rY=`=I{&3*UXd-LwS z=iJvgfY=9-Ip&g*G5~(=0st5QFx%l*2jz13@!)4f;gSykzX1SQ0YJLrK5UO%0cH)};+djV;tGoz2*1c?BFGK>WP5oro*-XD}i>Zvo}+z-`8_8fphDrD#7 z#~}fS^nCn7)|0WhpOXI8Nys_`B_-k>&)?d?Swinlpg)bML+SJ38B!wNNkDJZPRX>e zv(Vdm%;_d=Y;EJ`$tb;bc*c50R~hBRp>0j8y{)@qIP}iCFIGugw4mNCvzfS^tEbYo zgRBsqfVlyHNXR?$7Em{hEP9IAG24qa>|Bjm&(98Eg8mX8%}CIXmWaqA%&J$R%ykOq z1u_=R^Bs(iSdhR-Uo8w_P7!q|8%cALJ|*1OVAi3OJ&f(%G*-V7>don5Mqj)5AtJPp z_jR#T_iZTlnXpX5PeXv+1w4nW!){0uci&fn4NXI z^k8q5r9KibS3K3H3Wjk|?`(^ThxU~w1ayG2^6n{ta|ML<%I?tM%>zoMtL8fux>s!M z}FO=W9M524bb7SVMv z0N?wl24GStWKsf*!~>W`CY2RT_+=7WO+IG)?U>ENCC25YOnt>fgZP^(POblz3zzglL|66w<8aQ$2~W9^PT|7mSGkoV-qEwHTonSKmQMD zMr_a+U{))e;hZs`8A*}zd3hxj*~{P-2^+=Tg0)j=-fm+BD=VlB6w}pIr34gnhSvs7 z)KPmrRXI6XIpf>J5u>*kjLiO!Z%6&ZMM%b@Ih_m{P0M98jS1CS%=-RHF+=KJT@XX+ zZBQ@L^BFys$LOspgFhl1dzq<=A&uP;AYeL{n@4u6MNf9DDWC0_?V>PDt+^247A6Zn z$Dh`L1DTC#RTzhAm0)bEzRQo%cFQGB+&ViNDvTTK>S_|cr^C)T)J;Q@G;xtMHc**) zr3y5kvT!SehgKP-imSTV75aE!_HY{NkAC+?&o%k>kFE{X%)GqKP1A^kLty{Ydp__he=e`iF-cFKj6hvQ!5KXU0y$jnkexZF+LlD4c6Q&^ALP4}? z1fsTHCSi5Tg$ZQ7J?j*T$Y(5G;dVC%U+@Lt?T27D+%zz+yU+$ABknK=?J*KKSX&;6RLCR%P(zG zy(Rr9-5iB3NW@2$>er&9!U7`P6hSl; zy3$zQSr|a$GaMkqSzZvWy~i8(6qBGt!a zQr!0_Qn{~T{AaPkN>4RGlchu?YkHFN zf$Ro7B~WTgG>n}R=d)wfe;epd`bo1gHeUq~`EZ~oqE8I;ZpX+#j~)8f{r3;f_utxa ze@Dlo1V3U?j&Fipb>pQO&I;6bzT}m-%kD|7+*@bU4=o&_MQth{fQfc(G^|lMSqCuV aF$K(ZkOFDq>~#R-)RBcT+t)|8g1-RoAl7^U delta 2679 zcmcgueNYo;8hO0 z0442y6H_gm{`kPhyqAv7!1Y){mq8@$1+>{}`{(>!U@?FwOvM5KcU#lCUQa2c<%|vp zuTbLZkRAYQJ{_{p^gJZs(VovQXfqv~AEf<>NXP*RXoRD-1mbtR73|vy!Dru!Q(66Z zrUI|DO=Vx|Y;pKHcn+!&b2AsD&K5)9^B!>)tyytA*5dF2 z9V_z+V!T=|_NXn1jSJUtVD=(R3;-mj*BzG6tk$K?V2ws?7;#aX%mw^1_XQ47J}kmr zT8yaD3%HM4Us7@wU3t$q8J@q0Mfmr__6dVo1=bYvx<=1}wrsMh-=WGA6W#LDXOe6>o)n=&G7?go6Xv)%+ zjid?TZ57AQ22!I-T4up-A1DnUXcm@LjYD~NVDZT8qrH3ENZ`C}kn}|(>ig-jUuVRw zmSo#5_8eI;NIYQBERfgEg3Sm+FJVE@; zSaOE=6@yl=RM^rMBg;Lwr6l!lkxSEcR-{eO1KxV=%1zY9CO%IVNTZ8=<$bDLEdoBabpvpQuqi=NMX*8oxZr?N8^0=Vf&TD1DcvkGT&m1Uo1R4AhQJAZhfaa z3Nz_Q(SJ1FsphAdKKbVbdp&Z2`HI*v6+~QIl0`AiWtfi8WdY{XOiP4#Zi%w^DF7o} zZJZ8}4@vUKS@JeX9t@C3tB$-tl8W8ZV&iRZxp!ZMclBML-(PO<`u6yTfa&}I8H@}w zhFgdXHT3a&f)l4)vkv;U^7)7lKUtFHgp=hkUw9c#4bQnplAYl{-==QYsPvb>*KJg- z+~%DKJmc>OPLmy9Rk}nI?j9Hq97?F2jha80zx&3xkXJC)yC}Ejl0RFW0Us6Td$I>x z`FxL@pRRtx<%)S+ql_X)s@3{CyE27(gzSy~Bob`8U^q!})iPYCU1iA5qsf*o#$UE8 zR(u1%RgUb7=NYmAhV0D~MMJtY-J6UErqjZ;j6EK`JG8a0t=z&;66kGefh zrLPCSXr-7czR$GkJ4~PXrwD#?E^J(O-Sk-I`=7gN(lEQq$9Uo-bU6O+hKLmK(|bik z6Wv`S=?8s5H)!TuqxvV+8;+H|$MpvLDizmJdMZP`Wq5T8tHqCPzz9(l!dYNzIXR)X zBtYRgOge{{jv}v5GE&EZ2Z5nbBG1u@RGy&|DIX3ca-c_Q?DqL9WkyrEcY3jL0GLQN zi2^o>rze!lt#w&W&vew|_SEpZ+x9n@&4Xb#=60-crVKWB{$$6WQP~W6`ol%8!kZ-Y zPTR_P1d2VLD?6r60NGPK&qlW9bhQ-m1zyfptM9h_QJVj!(=#j!2PMIBL@ex#`YKVU ztRHp#HK5v8hKRnBic2tw%ipSZ_8I2Gyn^P#zT^kd4Z@k{T-_ODty;Kuli7?~%wcd8 zsy_2g#}OVjESg!kMg5_|0kz&zf|=ZqwxRMoh)mOj%H2YN#8|2MNJ$H`m1BAQaM9rF7PGJz=H mI0{8lKa5lt0h!MW#V*D7H!MfXwgOPLovtN&$L1Hh!G8gGyq(4X diff --git a/CI/physmon/reference/performance_amvf_truth_estimated_hist.root b/CI/physmon/reference/performance_amvf_truth_estimated_hist.root index ea02111161ac8f656cc7d1e39f4db5fd7c55e36a..324d89410fa443995224883c0bbb17fa0672947c 100644 GIT binary patch delta 2930 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 WhZU3~(v<+9vr_x*m~ zIe8swy$OYt78R`p;E@>skOM%8KLEvT1g;>4H8Iw5i76KV-uD2=bO5S9=cNf+GW9-g z$BAsU$EP}YG^i0l8z9}ZrQByR4W)T8vctEH(XZ<gTu z6TFo&)}L^Z^nLY z6>73xIKbGiERsN0WFVc1DDD_7q3>?bcQ&b`ZJ0Bc#VToUTte(U8rRJ1%}{kP_5#B0 zpRkj$cP4r<=#Kh2B>0F-m+cspuKr{X#18^vwqPSvhFQk&r^)m6dN&k}6L(bZo6{JI zv^wr^+|^Tjq4K;8@kfxl=jPhOtyx zYvO>?Uz`Lj8Ov=Cp|{I50o-TqIW^-pq8+aDu%m6YS|^UxSWQ&exup{G-X99fkBp5E z#@EAPk57E8ufU7aUG<~6qt)G>7gaVc$An>a=3&EtViW2)grJ_yTThoMGlcuqSH6SJ z6!Q${+l8XdBM{FW-jBK=(h;9f7fy>*!AwnC0LZ+cb^!oVOvVj-0R%vb2SK46Zc0qh zLf)E`(sj9MYd3sUiEh2VGB7BsR zSO^(=Y631Ulx7mq+Cc#2)8)6%#@1uld5xtQ!_?UrH&TLWF^lN5!0$kDY(Q+h{8V76 zDj^}jC-%&egj{g&7KUkRIR~r{T}HpGyqCqvT&{FdpZVDUaVPK-sdM#Pvy=!dd}^z9 zTHk9hNS%j~>}k4`s;{7jf_A5>F6&n)QT?2y#H$H-WBl!B-x5EcOn`ioDuyZ=@kgIQb5bD8&m8({Tzmn z*2b2qNqx3RNvT%P;)1`rk_fH7A+O>jf~oEh;IY9|Ol_o?YJ9;|%V{R~xcI&)WbD0V z3PsQ&!$okp08uH|1seS25`mBQx*2fvcvkLQ^$llHwIOOVr>Um76vIwhFAJ>Gd@|!? zxzTg`k0Ljr#BVsfGG@>W{Zx}W$0S0B1Ks3TKLDln@c8ED@ngF+f7cHVOo_a9Z#Y~L z1n;YZ@bEN8b%vJcT{g4bE2LgRSCwS!!iO$OWLrI-7d`^E)aQc>E!~VnS+QCK5HQ?L zg^BnRC_W}ryz(b`rHwKz&_+TCWr-s#x(2*f7fI9koNhV|p6GmOP{i>dDVs!|V|9|E ztO8~pIei}EEe{ssoq2qEM@gudR6;OyGp&S?JTH39C7%IG7HC;emoKG#+F9@$3!bq+ zvWnThm<0taARoActC+vg!CJ4efXhly#)5Yw;pudyncU;M3&bzIn4JY44zGDh@COA> z4zKUB-VV`Judt^oIESoYQNBrCH~~$AUbBL$3q8^Zw(}ui&#?U_CG})V>gpU;>Y_fP zFY^eh_xs6xW%2S@Rc~xAklm!^&ZXpD`?B00roE;q!`ah9QmO{QLmttTqqwg$<*teU z-ISBw)|5TSrd-w@rz76p3NkNX)+Dow+X!X{<6jg>MQOVMNWVfRtHggpa1;0s8%vC` delta 2571 zcmchZc~BE~6vuy?g-s+O36~@iApx`!5f%_AqbR2li%4U*QG*}}P(hA*Ow|wrig*E` z8XY_;w%Qp~>Q+Zb2A2q!qGHu*QM{nqV-#jQs?}QQUO};ro&Hm1e&L(9Z(sKF_Pg)5 zP5qGNHWXe`R8$7QV;lgG13;05Sn-5jNi17pmDvy#vl;N)JL$m2ExGA+0GA?x?r675X#Ke+&4_mDM+Mbsw$G-YH7MD@A^V9`gcpU`Zw z7Tivrk9k6IkPWGfky>|b6U&GBh9WYo7xtyUVO(3;aC{=2z5t1%#ehbPbs&$K%oa|z z+?pW9rUnf%alhd4EGmwrhPp7SIP5%A)ge!WrB4wv@nzwmv@*^k;$739F*Q0Oy_t-G z=--&w^;jtclb3%ue~i&p;ZFZ{cb+Sjs9-bM8F3Lz_U>6n8I>A;oKemR`x$j=jwhq; zCEkX(@l=~ysODTyy7R_BpallYp;EQ_mhriAif%lg$NQ3(8KE5WodmgB-Clie;qknP zJQrj%f^_QCvFTSg94@`~=#2LI%J3fqft+@vOB;ZqA}B!g;qs7(v>qQevNp51!#rVh z${Jtf#I96s+iG@N)({lakMMgYHSOJAi343d$-Pk6{G3)h_HF_73Dgx&{3#zkIzYH`t`H_o&%$wsVB#~cdiSAI~Du; z9!Z2-4L$m2CV@Bt72Dtp2}IX0@9 z0Wq9HRmrD|oRZ6ygY$*TXoFK%ZChG2bT@kNhaL7HxfDf<0wQEWFzZPXX} zrFOvSd~67TI+a50p`NEu5;e)!7_wNFMzFmEpoh8Vt!DfIj_>Qh`*3`Z8Rt|);OB5$ zw$(LR*;kidwQ?v#5tUc+6w7Of7FM|Okfm4pxaSVzpS;Hq|eNpnVOY!Fx9He~+7 zT{}sr{yubT?+}}%_-XI-shaMFFf;&Z;?FRISt?-IAcZHQ2g=LEgBouayeAip?B5c| zj^yBb$NuaLmYhpGM&O1}xb~MQ-pM3yveu|8lFfY%z$FHD+8GMgOu<@)VH*fohx#22 zyHhfiDuUQlZzM?DX;KbF>N1k_anUrdk5+%}#_@ueq)tU!c8sYmGN-ncVagk4AzFmt0nL(VAS{BcZ%AVq|I zyugWxYqOX*R=dz0;s#N_)J07UeTt`&FwhGCx~x6zBzx+8n(V3cthJ|YyIqy_riOVE zr7FG7Az9f5R5;yF8@ivZBb?)x6QVOX2n~9J2ClE%S)x{3Y)tgiW>>ZnlS zD4qCVdil&7ICQcyX)-1h4f;#`ZcYOwj>unqUA6d+2On)-c_TmY9#+DS-w63+mtR!# z1IcT$;C4K@RK503ahxr>LmXf8N9uM|qyAjtvTp?kfMZn&YO-9Ntq`{a*v5J9c>a^}Ru1HqEoTjjfoykn(v{eY_lc%vLgV@p_f)pkx*eYrlQng1u zE90CL)FqC5O4onlg}!+=hHb_zD=8rCH9M;;TT29?kz2LfbRzWc`xi4G-aY-jo#-+7 t_LOwTHY~maiHNs1@_j7uwKJ%9I?5R@mUs$Nb^(xbopel*Wm#}N_#1J&qA36X diff --git a/CI/physmon/reference/performance_amvf_ttbar_hist.root b/CI/physmon/reference/performance_amvf_ttbar_hist.root new file mode 100644 index 0000000000000000000000000000000000000000..be083f9d3b3bf7f6f4d1636db61b43a237ed4f44 GIT binary patch literal 27043 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 delta 5704 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 IdkpCR035xAumAu6 delta 15326 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 E4_Nt~I{*Lx diff --git a/CI/physmon/reference/performance_seeding_ttbar.root b/CI/physmon/reference/performance_seeding_ttbar.root new file mode 100644 index 0000000000000000000000000000000000000000..7ef18d19e5563cef845c1fb98fa081162e6e7ad2 GIT binary patch literal 11376 zcmcJV1yo!?m*=}_+}+)SySsaWTX6Tr-5r7kcMT!IlHl&{bO^3Vut4JwbUXQWcXnrI zW@pa%rq8K9-S54sSMU9Qb?>dJ>*40+1pr)W0RRAN0Duh)D`c?Wy08L-6(4xmmkj_w zq6q+Cl>vZy)HHD{vN$J^#c%-Jnr8StkeJNGlpDuC+WmSJ}T01$K(9Gq=l zaC*6b{}ew53tlrmJ10)Cjfbt9hl_=)m5rIFjg7T~s~v~OpPv2~E(IX`xtseLHZlMJ zVusz!2Uh;x6#!tq`2Rf$T)>|mBO}4;BLS5E^cYC^r#_mFysfQ+m4l6|l|QDJhlQ1s zjjgSjkEa<}2T$kEUszYEVTb)C z*<_}!W)974MXC3N#Ww}MGvZJjaxy|78uD4n7-GTOAC@8)Tpi6>E6kOst`J>I*T;}I zx@~G49Yxh1aUj(QnaP*jR82RzXn`wo7)qDa6=}5#UJ<(MFU>M(&a7^(R-KYSi^ap= zsk?Z9@Eup#V_p8g+i^)7P!`#R2E)^{3tJ5eNH_#^m9kZq1NYz@lc)Tf{Wl^KTTVW> zq0z?=-z+DyXSoZPOZLmv|u=N3gc!!4nur^JUyMn+q8A6zBQiSWadt z(!|r_ciL8`A$#T=56ao?!Cs$x&4qiagDNDcBc)4#-M#HLoL2pHxEnHWRk@(98H9~< zYM`YUt0k#j-9|+xc{GaBh9G$`;8y(uJ7n$XDlh1TPxrUb;J~N4M@RVwzJx6^=6#jl z6s^1SsB?4o>{u7yw+Vo=&Qx}H`{4mWG)X@V9SyBEgTX)FnXamnf1Pse+r*N+1SK{>WA6|+1CY-F?o6eB6oMq zeOk?%G7j&c#FAF8_=w1W6Y;&~dgY9*otF3)Y3vR#3Nf{n)M9Z5_!%)uKfJi8ZGwz zGkdGgM_nT^5&8oN{{1@#%yEhKlAooY#wqWIOaw*^1dRk{P|2IA=BbMyudcn4p17339*cLf9dE0?U?TF@h zhJr!wRtXXWLiF>2WZ}0^9*T0CCxz@|4E#GaJbj|z%7JTCo~cekCzpqb+DELZ)Df+Q9=*|Z^#b!SH``Vh$ z&%zM`HfPFs+$xqNZ~CY=6^ix}jBFb$-Yah{LK>Dlw32DW%|92!>JD6(P$B2ZuWK0a zYANdap7L@^ZFK%(jI<)l`Ev}Ah#~xL5Kt2#79qiq=-gq}*6U9Og*sq!Ef&GW}OKI>klj+kjrj7oL1L0!;)2nwwN3>gH$dg&n}>Os^pOmU#eQtj|QQ!)9{aGT76y0 zUqLLI&sz#^+FcQodE@4%4fXmOz~%kk?bdC>O?w2(8Sp6AK)^P4`GP_f`baQAgB@~+ z%JLwI>}eX&EuC#Pf0X1Yj_=?@1qNOBa$hLRD+EeG@mmspaR^3~#9dhXw%g>dSAzSm zOMt{7!^?AVpePFarEr7sSm%C37z?L7wo*c~7ZBO;18~ zW6bWx6=pVMn%M zjG>)l>(I>2IxH)!4k~fsSPJwtt#Ip}tL!dQrwfi@UuL5Bm_!9Edr8+zXP(^_TMhP& zb~z~)nqT8QYlvc=)8|gHdKfkj2;gE#d<^(VMYjshEQo$o9exI>^#H_o?Si|&8Fp&P z&~w83oWd&CxVe_jIZdjqZ{T?@s@?J;zfg~g*tF2OUt3h?&T946eBbv~`}Be3A)H{o zp9TCPMMyjSl%6@}c9Nx1bAb^-uuOMz(w z$J8{~#wKUbRM?c0I9Zc9k#WG%aV+@2=p1I8AQm<_*ui z07NcQ=84^26#VvXD6Uff(?QZfA~{hgLIN63Lw_M4-O?OvZoY9&g)Ae6ZX#9i1AP(` zU9B6P91|UV+B`5kAq>+tweN82HezsR2ZFEc<;!X>Mb8&L3=r?dN8mvnMf48?u7ji& zn!-GOpud*VB3#y5Oc+d3>Ts|MMUkMV2 zV4%c z>+L9ls1)%KP?s7Bup4Dhh37)Ug=%W(|Ge76nWVR&Y2T% z8u$TGWHS7(d=t+VR? ztMf~CWqhzSGAllCDE#h^&>}FQOO(<3Cpp8}IO5(1gFumwNQhivb5e$sNE#)xSHam* zxYsY;)XcuO-m<=Wof=MXf|!aAiUff!?w+NvcP#edcGxV%T-o2--`jhORUGwgMzT7r7#dkmSYLn&y4vVP8dK;A$Ol+ zj}{*O43{t45Koaq3?ht~BX%TjsARU*?QKO=ME<Fy-8d!597G2iP{4vpaIX?f<&(coc>G^f z#yI-Vs0{PP|CY+xK*0Dv9I?78oF=|_NVL%_noA%;1pB$(N(r_83@njS>2+J8W;*eIDa zBB8*ek(ugHBvBHVTwkmEW<1D*bGuC~8zxn9t-LzTR{3LqAqZ{*JpxYYW4juy)ubrz zbSD1*^LM+BctxVsaG{%Rlul6(&bxE?eFp8hmgc>`0pnIa35xH zzXZ?*GvW1y(_$Y`gE>$dC}fNM_9O70hvMV#7F(xTmW%>Dq85m3W-RM98$oqYmMxak z7Px{zeci1>Cjt*zw-5217R9)p!Hk~`d72(`H^OVczLZ$_W~~=~B@(W)x?4Rj%pV7W zf#HzBJecM*f7C{F`>VF`pD~TK{6}qMomU=iwy=3Oj_dz2(Kgoqu-Ob6-Y%9l9@K8O z)c+x!qUg^3?i+LIqG2G5ij2rFe2i;y>na%jpK*fnBExD@{6_3PKH)~zrj6U zSBcQ#3kel_6Irs-9~hJoRmfC8ZSYy*6zCSNx;n^K+(OawZ0X$AN0@h=YK%#LhfFWv zYeJ@~o-3VWy`c{uf$CtZ;Kcpymd{LL*OFUw{!O6O%naooBPuo_y*XNlY2XK)?e%0= zrq554jZN6eGnlKYYqPP0y;tn35vAow&we92vrBv4n$8SCb?`v+b7Rse5~v$)YWn;- z|GcQo3cLqfKNbm7zM)A(K*Mjzt15@=q4hMZZ}KgU8%16ZcXo z=eebBIEQYky54>qkYBcAjvS{|U`7DOL+!+qjdMpN2>pt4qg7jQ9am zAy^-HTC!D2dC5E-$h-QE9k`4#xfg7=F@1A9LHp*%7QW0aSroR66SjO;8rxJN%dpX>t1TKS zBdIYSq(2hZz85$SOMr)ClX`PLL}MJsH>prc+O6gTA&upd+N$43ZC!jO%@=vw>G628 zXXhAaq|F2byp0sHLB;FdpUPkSv{}T`vSC}7w?BGwDgZyPi0Ld zV@07kkEj>&v2E`0ZDUVMfs=Prfj>XS?5oecoit7S-9Ggz?xkCbcd>*3YLEkOhko?S z+``o|JGlu@ishD#pLdACzW7Vtxm5Z&t9W_ccyI;L$iXQ1rI9^e_5~t6&rW8EN197d zW6tAE{LYJ57(dg0If>L5`{qo$cMV-JeVnva+2=BLdH2otx{~usub@e=rHx_5hGU{z zKDNns+J+ri3Yu@gPmJ^MP6t`H=0eA(i33vc1I*i$*j2SmFdHfNx^mjSP z@CfuCuTfG}USnF!)N3y60zCCLpwf|q+&GklHc@uw_n(} z-y4-gX~eC5qa**V3PL8Tv>7X@*0{ye{iHV3;UNFHPZmwWcktO{)+ewy^lsvK-=C2v z^tM}|S@GfMmT4?DRE-hKc3rFz@1?0c#rb*>nMW18z$QrFJ-`(z)j|sv2KGE**4Ra% z3s8ldoYgbUx`14hBAj#^iVPo{k<^=pgk@y13RLzaAm(%^e_tj5y26B6;KE%8o zoa1=#E?qR_J#`Z2N*6P!UcgouVF*E~Nr}i$J9Ps;0v;58y4}utp9#)LxgAHcgB(NY z#(HRu$aDuqQ+mI;l!DIm^!OsDV6}g{RDYR|+>DpnjZ89Wc;VfCQ|g54_K3W00whDi zCGrWFJ>p(Ej5s!Vb>BkscrbpmFisr;)dlkcvxC63NIn%$`aC^ZaXnaznWD4mRL&Gg zQ0GXpa&s@-nycP-lg3G<(Ha$;j3NpH*V(ScrB5#fbkCPUV=lkjc@GKYvvLy$m}?m9 zOYa4reg?VgHk0_B4!}>-McnD>RZLLxDvY%Jzti&Lbh zsQhMyk#@vnkq`^?kLlgH-9eagN9{7`JsDx$`?HZJ+7JFzA%=~`Qkz`n%##eWE8ohn zC`iE?2A3t70hq@luOs}PpX87bLud%^wmOka1xl_pX9{0#5k&=1NhCtUzeUN%Tw66* z_J&DMm0z4w2Cc`2Ahi)c5g@Aet0Icvo1B%PaqpA8x~B-3&3k$O`d44E4c8m#)*-Sq#14Y@>@w=P(C(``x$sI)KGQ;tT;=Is6>GyfRlzh3aDL zI%YSOPr`=2((ivmzP0bs&tx<}Cq$+sb6ZggGX3-HWPTpBWr~>Hm+UPD?b#F-8DH7O zv-ajOP0Si8Rv+mc1stao={H>~-bhw@zYegRDtGTPJ5D(a3AHZ#SXEo58EscNF_=ml9rGC)&{68i;1?BP3*BoQ28y zsTGp>)H`&)M>$%RM0R>Ip)9f`8QU@4P#+51G%MrzyH5f$QC!I|w~7pq`r}sr?4AIu z5p>iXJiXAhy*z9zTx>j)U2WZP<=nh2o&Bku9bBDYftEAuJS?~}!~!I9!OYOHk&`pI zK7j<~h>vB5MuGm@Oa4~Q5&vb4J0-1H_noMyckXe5wQn2M?R@QiDcjl=b^g?KXpZO6n_Ct-s60O7%n^lH7QwSlVja2srEqpSwdp;a4FWb6O^a7 z6{HBKAV(qG=txjz3h_d{t3Ep`IGMnExS&o}^FN58`lTdk;12fM;i4=_!M6#csoGWN zz8t97ekhUJ%!+aSVmQ4)tnQnTMI_v8g&@b5V1<{Apo+wb3qYK+B5+(zIihzED#j%j zPg-!@D;RBRCwMg|2!Th8vKKa@5sCds^K&JY56e@RUr9ySxqj;XlkV$4r`if&PUd-# zf??VH+S;3(aRv+m=QJyQI61kGzZQx-tDPyxRx9LQOa$wBe=GfoN{*Iw@|j3PyGR#8 z!O0V+LNNc%y-8`{cjwl1mHX zbFf-Ad4$Kt?5TV5oGBg*nZq2RX_&}Cu95c+Mzwi)zi#BdrsI$>6VF;v zqpF;Xu&d{Bj@fjFXZw*K5lznomv_R&E>$q+W9#7$m$htPR`7uo6~o|K;n>~nX8VdK1mXFix$a{s&8ntL%5 zi8@6qgwtiakqR8=jBQ1m=UGck_r2KuN_NC=^o7C#_b}VDGkIiFs4Bza(-oTibKGwG z<&ztd>9#FK*$s>P!<}~RwIK(eQr7$vUB&hZjeDu+mz6yM`TLK*vt4PQ4P#J9;c@XQ z!txqHNBeCmHRV-uiut$}Tcw4+O0jbK_6#v{iS`U3nh-oV z;$(R``TXNM#t~EzQykUV90N;GM+N)*PwNXuosbTa_!B>@6S%$sdRcf9BesP^f=y4 z@DjW-(*>N0dg~6l2=nfV*5wL%)ODsZ$whZ!RJ-c^ zJ7q&stXX%{C*a$y4Qtr*^yir0U**LM_v9j(Cu-+{HC&$ZC8(#FB0&s zH4cO367>v_3v!hhn{a6FFkm*u*cG3XL@pZjitB-v%MSkMr#$|fgyj>z9=F5jFp+P|;V7}PC+R!qC4O1#8(Tm-k?4|k znWDf|n6D4BuW32+*<^Vvq(xqmp6m#v8nRl}Qe>%qIr0p#ZoiXj8#LUl`Jtw+s4f$& z@;(vHCSKvDf`0`^7GpEL50x`B&7wD9B1#^P1NR-?sal10gT8>ZxfKo)V>K-9L9Jot zkznCDQJQGw$vPW9z4D&R8~T`iie-C7*=xX-scH<-?Qw3mQH>dWbEJslDWV>~gj}&Z zlzMgrTh^-n%5fh>m62>-d4P2EyXrtBCBL(C(ZzqMAdV9fzgbTC#VbyhbXwbTspE5k z#P`57GfY<;g_kKoUHehIb;7ZYFQ3Axy_P-vXUTg%4W?_5l7JvTP-{0MKPr+=FTe%) zlD4i7mlRN(BwBE*aJ}|#fKUidQPUx#U}R&;z<6EEq#6m z@AM%Pr37HhYh`kBW+JxZel84R1Er6e!7+Sm>Pm_{7rxoS4bH22z02+`ceNicTMJ25 zNlj;%rI80SFpIGF{p5?cu|@IMvOKFL#L6zLxvKUm_4j+JljWn25<$&c^(8ZyyiLgGCRefD+_4y52r*wY_oSorn(n9f{Q`m&Xh)s`}nV(o3?sbnp zd>7F%-+NxFQ4vBhnPLh)8+^2Q4o;R)uC5E!BQ_7rbfKch@MRimCONEHm*|TRm_& z`>hwn&}~eY{Q_(eDUA?7itS=-xeW6L{0?jMPL7Y#t-`W>DxGJ8rjjUWdAURhDs_A} znvvd7{4vqx^Tv)m@QdjKrnxNj;Q0bM+b4%FDYAw~w}Xp@l;aH1JE^wjC=7bz6|u*# zg|klg{(do2_*Xh#Oy)6i`C!hx$U5|$iez_*lsk7ECDH8t0(T9W=xYWiHVx{S;Vu({ z6`czdYv&T8UP4vIkx3U~Lq=ree18*GFsc~!;!f%nN1gXXLMOBXQBz+1LSJdXggSVP zEvut~Q&H$+!U;Jq-4UiV(oHzhs`t^jL@*dySGsKycylN_aPI?M793qr=S2xKDaF`? z4-om<%3qs;nIn?&y*gGf*g#9)6&TqHH3bEU%nN`sOl?{|vo!Dww z=?CQZKI75ys^XE4@fy}*V)}Jlxrk&G?T4k46>LElFDHi2&8mhnbUT3{f3$?#QMs+7we4 z>VLki5Sjpd5EQlE(eP!{QUlk-bCS%srTX%dnt%LM-Y?^Rw=z&=vlFI+-@=@V2vMZZ zu-5A#S3xC8G2YPO>yqe>v)U>(g+rS$ooh8v4WLof3?juoa((PXU4FKVX@1ekpuR}5 z`w8)z-q%(&Htct0<6;hoq3(UlbJv&1R=P3*XII|wBX+Z-H+KQ#W6gYd>2D}POm?m& zfOL@}m^$?IK5OhWpKKLx8eBvXkjQpCy$-*!hccqS?wpU-$0iUpaw^{$;ZV2TczXUG z-1!x8cet`0aTdKyKVz>)Dd@)k>><99)6(Bj{-^&!VXnYU@c7k*f%bHcIThO}0{15Z zp{*d;QjV~8r+okGSD|yCr#_P%(6y`ivO2`CSxDrD8eScu&40A3)UTTFf+L&ivYbt+ z(bnh9=#x7UQXmRWG;$Bo4iGXtriqQ<{}7w(YYHJa5nWDQOnnSn0hCfJ`%u=v)khgU zg)41@5&<~p-2HYBtQ<~9X_|_}OO1y0zA*C^hnvQoUTDQtdY9KsZFyJ4dYM|G{ zCB$MSFa;YeEz9^ebPtcDVp!+rdt7`0`cYdLMVZU>6zMs3sF9x7R%DkSnASB%TfFr0 zHa=HvBpeGtBvku~Wg(Xci7K+qEDAaZrxR}#T#v-Or&(rd-s~g2a2>$`ko7;Cju*s7N2_czyw0xhXPQIEFk05us zSh<&7R&=-^T>|6Fhz@k8*sD6p_d}Lr)QHBze8(C^Th5c|Tkwke+ScW1o`(Z8FmN90 zqY^uI?bRm1oc7VClb+e^!uZ-)Zwo~b#PiwgIZGCe;{CHWB(~0|e(^AY4&~@jS*JC@ ziN3v^T4BZ+E#aK+HiuACB6z!--c%kd36mSMqjSJz(~|OQq0F} zFFU<3M=OuIli}|qOYQLo7*-E?1c9~=?*th44&Bs>NbGT;U&deVDMiu@I1TVN&wR4z z71algF`I$+9EDvc%CEB1skzeze4&$}gKn>gpg2jZRpWI-7L^Fk&oPkUJ<_vb{GM~I z0DEz=K6b^^CNhbfVFj~3kwL1);%|A5(;GetA{LkIaguVR#uI(~d)BkN7E&rVh n;xM>%|17ox{w%g5|08YpZ*nkfCF{RBPGE~*Y+zsh48Z>YkLf!S literal 0 HcmV?d00001 diff --git a/CI/physmon/vertexing_config.yml b/CI/physmon/vertexing_config.yml index 0bcda614920..42d77f02ff1 100644 --- a/CI/physmon/vertexing_config.yml +++ b/CI/physmon/vertexing_config.yml @@ -14,11 +14,10 @@ histograms: min: -0.0005 max: 0.0005 - # fixed mu=50 "^n.*$": - nbins: 50 + nbins: 250 min: 0 - max: 51 + max: 251 "truthX|truthY|recoX|recoY": nbins: 100 diff --git a/CI/physmon/workflows/physmon_track_finding_ttbar.py b/CI/physmon/workflows/physmon_track_finding_ttbar.py new file mode 100755 index 00000000000..a0a374e7af4 --- /dev/null +++ b/CI/physmon/workflows/physmon_track_finding_ttbar.py @@ -0,0 +1,180 @@ +#!/usr/bin/env python3 +import tempfile +from pathlib import Path +import shutil + +import acts +from acts.examples.simulation import ( + addPythia8, + addFatras, + addDigitization, +) + +from acts.examples.reconstruction import ( + addSeeding, + TruthSeedRanges, + ParticleSmearingSigmas, + SeedFinderConfigArg, + SeedFinderOptionsArg, + SeedingAlgorithm, + TruthEstimatedSeedingAlgorithmConfigArg, + addCKFTracks, + addAmbiguityResolution, + AmbiguityResolutionConfig, + addVertexFitting, + VertexFinder, + TrackSelectorConfig, +) + +from physmon_common import makeSetup + +u = acts.UnitConstants + +setup = makeSetup() + + +with tempfile.TemporaryDirectory() as temp: + fpeMasks = acts.examples.Sequencer.FpeMask.fromFile( + Path(__file__).parent.parent / "fpe_masks.yml" + ) + [ + acts.examples.Sequencer.FpeMask( + "Examples/Algorithms/Fatras/src/FatrasSimulation.cpp:172", + acts.FpeType.FLTINV, + 1, + ), + acts.examples.Sequencer.FpeMask( + "Examples/Algorithms/Fatras/src/FatrasSimulation.cpp:172", + acts.FpeType.FLTOVF, + 1, + ), + acts.examples.Sequencer.FpeMask( + "Examples/Io/Root/src/RootTrajectorySummaryWriter.cpp:371", + acts.FpeType.FLTINV, + 1, + ), + acts.examples.Sequencer.FpeMask( + "Core/src/Utilities/AnnealingUtility.cpp:38", + acts.FpeType.FLTUND, + 1, + ), + acts.examples.Sequencer.FpeMask( + "Fatras/include/ActsFatras/Kernel/detail/SimulationActor.hpp:110", + acts.FpeType.FLTINV, + 1, + ), + acts.examples.Sequencer.FpeMask( + "Fatras/include/ActsFatras/Kernel/Simulation.hpp:98", + acts.FpeType.FLTOVF, + 1, + ), + ] + s = acts.examples.Sequencer( + events=3, + numThreads=-1, + logLevel=acts.logging.INFO, + fpeMasks=fpeMasks, + ) + + tp = Path(temp) + + for d in setup.decorators: + s.addContextDecorator(d) + + rnd = acts.examples.RandomNumbers(seed=42) + + addPythia8( + s, + hardProcess=["Top:qqbar2ttbar=on"], + npileup=200, + vtxGen=acts.examples.GaussianVertexGenerator( + mean=acts.Vector4(0, 0, 0, 0), + stddev=acts.Vector4(0.0125 * u.mm, 0.0125 * u.mm, 55.5 * u.mm, 5.0 * u.ns), + ), + rnd=rnd, + ) + + addFatras( + s, + setup.trackingGeometry, + setup.field, + rnd=rnd, + ) + + addDigitization( + s, + setup.trackingGeometry, + setup.field, + digiConfigFile=setup.digiConfig, + rnd=rnd, + ) + + addSeeding( + s, + setup.trackingGeometry, + setup.field, + TruthSeedRanges(pt=(500.0 * u.MeV, None), nHits=(9, None)), + ParticleSmearingSigmas(pRel=0.01), # only used by SeedingAlgorithm.TruthSmeared + SeedFinderConfigArg( + r=(None, 200 * u.mm), # rMin=default, 33mm + deltaR=(1 * u.mm, 60 * u.mm), + collisionRegion=(-250 * u.mm, 250 * u.mm), + z=(-2000 * u.mm, 2000 * u.mm), + maxSeedsPerSpM=1, + sigmaScattering=5, + radLengthPerSeed=0.1, + minPt=500 * u.MeV, + impactMax=3 * u.mm, + ), + SeedFinderOptionsArg(bFieldInZ=1.99724 * u.T, beamPos=(0.0, 0.0)), + TruthEstimatedSeedingAlgorithmConfigArg(deltaR=(10.0 * u.mm, None)), + seedingAlgorithm=SeedingAlgorithm.Default, + geoSelectionConfigFile=setup.geoSel, + rnd=rnd, # only used by SeedingAlgorithm.TruthSmeared + outputDirRoot=tp, + ) + + addCKFTracks( + s, + setup.trackingGeometry, + setup.field, + TrackSelectorConfig( + pt=(500 * u.MeV, None), + loc0=(-4.0 * u.mm, 4.0 * u.mm), + nMeasurementsMin=6, + ), + outputDirRoot=tp, + ) + + addAmbiguityResolution( + s, + AmbiguityResolutionConfig(maximumSharedHits=3), + outputDirRoot=tp, + ) + + addVertexFitting( + s, + setup.field, + associatedParticles=None, + outputProtoVertices="amvf_protovertices", + outputVertices="amvf_fittedVertices", + vertexFinder=VertexFinder.AMVF, + outputDirRoot=tp / "amvf", + ) + + s.run() + del s + + vertexing = "amvf" + shutil.move( + tp / f"{vertexing}/performance_vertexing.root", + tp / f"performance_{vertexing}.root", + ) + + for stem in [ + "performance_ckf", + "tracksummary_ckf", + "performance_amvf", + ] + (["performance_seeding", "performance_ambi"]): + perf_file = tp / f"{stem}.root" + assert perf_file.exists(), "Performance file not found" + shutil.copy(perf_file, setup.outdir / f"{stem}_ttbar.root") From 3f4cbabb95ee953f37a0e50ed5c2b4ea73ad5d24 Mon Sep 17 00:00:00 2001 From: Paul Gessinger Date: Fri, 4 Aug 2023 23:05:27 +0200 Subject: [PATCH 21/21] ci: Physmon comment report pipes through summary (#2342) This PR changes the physmon report CI job to basically pipe through the `summary.md` that we create inside the physmon job to the comment. This should be more robust than what we had before. Blocked by - #2339 --- .github/workflows/report.yml | 9 +- CI/physmon/comment_template.md | 167 --------------------------------- CI/physmon/generate_comment.py | 100 -------------------- 3 files changed, 7 insertions(+), 269 deletions(-) delete mode 100644 CI/physmon/comment_template.md delete mode 100755 CI/physmon/generate_comment.py diff --git a/.github/workflows/report.yml b/.github/workflows/report.yml index 8ad7f1a893a..374ab1c8921 100644 --- a/.github/workflows/report.yml +++ b/.github/workflows/report.yml @@ -91,9 +91,14 @@ jobs: - name: Render comment if: steps.get-pr-number.outputs.result != 'false' run: | - pip install Jinja2 ls -al $GITHUB_WORKSPACE/physmon - CI/physmon/generate_comment.py $GITHUB_WORKSPACE/physmon comment.md + echo "# 📊: Physics performance monitoring for ${PR_SHA}" >> comment.md + echo "[Full contents](${ARTIFACT_URL})" >> comment.md + if [ -f "$GITHUB_WORKSPACE/physmon/summary.md" ]; then + cat $GITHUB_WORKSPACE/physmon/summary.md >> comment.md + else + echo "🟥 summary not found!" >> comment.md + fi cat comment.md - name: Find Comment diff --git a/CI/physmon/comment_template.md b/CI/physmon/comment_template.md deleted file mode 100644 index 6717aa480ad..00000000000 --- a/CI/physmon/comment_template.md +++ /dev/null @@ -1,167 +0,0 @@ -## :bar_chart: Physics performance monitoring for {{ commit }} -{% if has_errors %} -> :red_square: **ERROR** The result has missing elements! -> This is likely a physmon job failure -{% endif %} - -[Summary]({{ url }}/summary.html) -[Full report]({{ url }}/) -Seeding: {{ make_url("seeded", "seeding_seeded.html") }}, {{ make_url("truth estimated", "seeding_truth_estimated.html") }}, {{ make_url("orthogonal", "seeding_orthogonal.html") }} -CKF: {{ make_url("seeded", "ckf_seeded.html") }}, {{ make_url("truth smeared", "ckf_truth_smeared.html") }}, {{ make_url("truth estimated", "ckf_truth_estimated.html") }}, {{ make_url("orthogonal", "ckf_orthogonal.html") }} -IVF: {{ make_url("seeded", "ivf_seeded.html") }}, {{ make_url("truth smeared", "ivf_truth_smeared.html") }}, {{ make_url("truth estimated", "ivf_truth_estimated.html") }}, {{ make_url("orthogonal", "ivf_orthogonal.html") }} -AMVF: {{ make_url("seeded", "amvf_seeded.html") }}, {{ make_url("truth smeared", "amvf_truth_smeared.html") }}, {{ make_url("truth estimated", "amvf_truth_estimated.html") }}, {{ make_url("orthogonal", "amvf_orthogonal.html") }} -Ambiguity resolution: {{ make_url("seeded", "ambi_seeded.html") }}, {{ make_url("orthogonal", "ambi_orthogonal.html") }} -{{ make_url("Truth tracking", "truth_tracking.html") }} -{{ make_url("Truth tracking (GSF)", "gsf.html")}} - -### Vertexing {{ "" if all_exist( - "vertexing_mu_scan.pdf", - "ivf_seeded_plots", - "ivf_truth_smeared_plots", - "ivf_truth_estimated_plots", - "ivf_orthogonal_plots", - "amvf_seeded_plots", - "amvf_truth_smeared_plots", - "amvf_truth_estimated_plots", - "amvf_orthogonal_plots", -) else ":x: "}} - -{% call detail_block("Vertexing vs. mu", "vertexing_mu_scan.pdf") %} -{{ make_image("vertexing_mu_scan.pdf", 350) }} -{% endcall %} - -{% for mode in ["seeded", "truth_smeared", "truth_estimated", "orthogonal"] %} - -{% call detail_block("IVF "+mode, "ivf_"+mode+"_plots") %} - -{% for url in [ - "covXX.pdf", - "covYY.pdf", - "covZZ.pdf", - "resX.pdf", - "resY.pdf", - "resZ.pdf", - "recoOverTrue.pdf", -] -%} -{{- make_image("ivf_"+mode+"_plots/"+url, "50%") -}} -{%- endfor %} - -{% endcall %} - -{% endfor %} - -{% for mode in ["seeded", "truth_smeared", "truth_estimated", "orthogonal"] %} - -{% call detail_block("AMVF "+mode, "amvf_"+mode+"_plots") %} - -{% for url in [ - "covXX.pdf", - "covYY.pdf", - "covZZ.pdf", - "resX.pdf", - "resY.pdf", - "resZ.pdf", - "recoOverTrue.pdf", -] -%} -{{- make_image("amvf_"+mode+"_plots/"+url, "50%") -}} -{%- endfor %} - -{% endcall %} - -{% endfor %} - -### Seeding {{ "" if all_exist( - "seeding_seeded_plots", - "seeding_truth_estimated_plots", - "seeding_orthogonal_plots", -) else ":x: "}} - -{% for mode in ["seeded", "truth_estimated", "orthogonal"] %} - -{% call detail_block("Seeding "+mode, "seeding_"+mode+"_plots") %} - -{% for url in [ - "trackeff_vs_eta.pdf", - "trackeff_vs_pT.pdf", - "nDuplicated_vs_eta.pdf", - "nDuplicated_vs_pT.pdf", -] -%} -{{- make_image("seeding_"+mode+"_plots/"+url, "50%") -}} -{%- endfor %} - -{% endcall %} - -{% endfor %} - -### CKF {{ "" if all_exist( - "ckf_seeded_plots", - "ckf_truth_smeared_plots", - "ckf_truth_estimated_plots", - "ckf_orthogonal_plots", -) else ":x: "}} - -{% for mode in ["seeded", "truth_smeared", "truth_estimated", "orthogonal"] %} - -{% call detail_block("CKF "+mode, "ckf_"+mode+"_plots") %} - -{% for url in [ - "trackeff_vs_eta.pdf", - "trackeff_vs_pT.pdf", - "nHoles_vs_eta.pdf", - "nMeasurements_vs_eta.pdf", -] -%} -{{- make_image("ckf_"+mode+"_plots/"+url, "50%") -}} -{%- endfor %} - -{% endcall %} - -{% endfor %} - -### Ambiguity resolution {{ "" if exists("ambi_seeded_plots") else ":x: "}} - -{% call detail_block("seeded", "ambi_seeded_plots") %} - -{% for url in [ - "trackeff_vs_eta.pdf", - "trackeff_vs_pT.pdf", - "nHoles_vs_eta.pdf", - "nMeasurements_vs_eta.pdf", -] -%} -{{- make_image("ambi_seeded_plots/"+url, "50%") -}} -{%- endfor %} - -{% endcall %} - -### Truth tracking (Kalman Filter) {{ "" if exists("truth_tracking_plots") else ":x: "}} - -{% call detail_block("Truth tracking", "truth_tracking_plots") %} - -{% for url in [ - "nHoles_vs_eta.pdf", - "nMeasurements_vs_eta.pdf", - "pull_d0.pdf", - "pull_z0.pdf", - "pull_theta.pdf", - "pull_phi.pdf", - "pull_qop.pdf", - "pull_t.pdf", -] -%} -{{- make_image("truth_tracking_plots/"+url, "50%") -}} -{%- endfor %} - -{% endcall %} - -### Truth tracking (GSF) {{ "" if exists("truth_tracking_plots") else ":x: "}} - -{% call detail_block("Truth tracking", "truth_tracking_plots") %} - -{% for url in [ - "pull_d0.pdf", - "res_d0.pdf", - "pull_qop.pdf", - "res_qop.pdf", -] -%} -{{- make_image("gsf_plots/"+url, "50%") -}} -{%- endfor %} - -{% endcall %} diff --git a/CI/physmon/generate_comment.py b/CI/physmon/generate_comment.py deleted file mode 100755 index 00854a3107c..00000000000 --- a/CI/physmon/generate_comment.py +++ /dev/null @@ -1,100 +0,0 @@ -#!/usr/bin/env python3 - - -### CHANGE BELOW WITH CAUTION ### - -import sys -import os -from pathlib import Path, PurePath - -import jinja2 - -template_source = (Path(__file__).parent / "comment_template.md").read_text() - -macro_source = """ -{% macro detail_block(title, check) %} -{% if exists(check) %} -

- {{ title }} - {{ caller() }} -
-{% else %} -:x: {{ title }} -{% endif %} -{% endmacro %} -""" - -template_source = macro_source + template_source - -template = jinja2.Template(template_source) - -_, artifact_dir, outfile = sys.argv -artifact_dir = Path(artifact_dir) -outfile = Path(outfile) - -artifact_url = os.environ["ARTIFACT_URL"] -pr_sha = os.environ["PR_SHA"] - -print("artifact_url:", artifact_url) -print("pr_sha:", pr_sha) - -has_errors = False - - -def exists(arg): - file = artifact_dir / arg - result = file.exists() - print("Check exists:", file, "=>", result) - global has_errors - if not result: - has_errors = True - return result - - -def all_exist(*args): - result = all(exists(a) for a in args) - global has_errors - if not result: - has_errors = True - return result - - -def make_url(title, target): - if exists(target): - url = artifact_url + "/" + target - return f"[{title}]({url})" - else: - global has_errors - has_errors = True - return f":x: {title}" - - -def make_image(target, width): - file = target - if "?" in target: - file = target[: target.index("?")] - if exists(file): - url = artifact_url + "/" + file - return f'' - else: - global has_errors - has_errors = True - return f":framed_picture: {target} :x:" - - -kwargs = dict( - url=artifact_url, - commit=pr_sha, - exists=exists, - all_exist=all_exist, - make_url=make_url, - make_image=make_image, - has_errors=has_errors, -) - -# render once to fill `has_errors` -template.render(**kwargs) - -kwargs["has_errors"] = has_errors - -outfile.write_text(template.render(**kwargs))