diff --git a/Core/include/Acts/EventData/TrackParameterHelpers.hpp b/Core/include/Acts/EventData/TrackParameterHelpers.hpp index 0d14d39fd7f..02a7e66afce 100644 --- a/Core/include/Acts/EventData/TrackParameterHelpers.hpp +++ b/Core/include/Acts/EventData/TrackParameterHelpers.hpp @@ -8,7 +8,6 @@ #pragma once -#include "Acts/Definitions/Algebra.hpp" #include "Acts/Definitions/TrackParametrization.hpp" #include "Acts/Utilities/detail/periodic.hpp" @@ -28,8 +27,20 @@ inline BoundVector normalizeBoundParameters(const BoundVector& boundParams) { return result; } +/// Add bound parameters and take care of angle periodicity for phi and theta. +/// This is intended for small differences only i.e. KF updates. +/// +/// @param lhs The left hand side bound parameters +/// @param rhs The right hand side bound parameters +/// +/// @return The sum of the bound parameters +inline BoundVector addBoundParameters(const BoundVector& lhs, + const BoundVector& rhs) { + return normalizeBoundParameters(lhs + rhs); +} + /// Subtract bound parameters and take care of angle periodicity for phi and -/// theta. +/// theta. This is intended for small differences only i.e. KF updates. /// /// @param lhs The left hand side bound parameters /// @param rhs The right hand side bound parameters @@ -38,8 +49,8 @@ inline BoundVector normalizeBoundParameters(const BoundVector& boundParams) { inline BoundVector subtractBoundParameters(const BoundVector& lhs, const BoundVector& rhs) { BoundVector result = lhs - rhs; - result[eBoundPhi] = detail::difference_periodic( - lhs[eBoundPhi], rhs[eBoundPhi], 2 * std::numbers::pi); + result[eBoundPhi] = detail::radian_sym(result[eBoundPhi]); + result[eBoundTheta] = detail::radian_sym(result[eBoundTheta]); return result; } diff --git a/Core/include/Acts/EventData/detail/GenerateParameters.hpp b/Core/include/Acts/EventData/detail/GenerateParameters.hpp index 0d38bb8d966..023314e2527 100644 --- a/Core/include/Acts/EventData/detail/GenerateParameters.hpp +++ b/Core/include/Acts/EventData/detail/GenerateParameters.hpp @@ -8,33 +8,48 @@ #pragma once +#include "Acts/Definitions/Algebra.hpp" #include "Acts/Definitions/TrackParametrization.hpp" -#include "Acts/Utilities/detail/periodic.hpp" +#include "Acts/Definitions/Units.hpp" +#include "Acts/Utilities/AngleHelpers.hpp" #include +#include #include #include namespace Acts::detail::Test { -/// Generate a random parameters vector and covariance matrix. -/// -/// @return std:::pair template -inline auto generateParametersCovariance(generator_t& rng) - -> std::pair, - Eigen::Matrix> { +inline auto generateParameters(generator_t& rng) + -> Eigen::Matrix { + using Scalar = scalar_t; + using ParametersVector = Eigen::Matrix; + + std::normal_distribution standardNormal(0, 1); + + ParametersVector params; + for (auto i = 0u; i < kSize; ++i) { + params[i] = standardNormal(rng); + } + + return params; +} + +template +inline auto generateCovariance(generator_t& rng) + -> Eigen::Matrix { using Scalar = scalar_t; using ParametersVector = Eigen::Matrix; using CovarianceMatrix = Eigen::Matrix; - std::normal_distribution distNormal(0, 1); + std::normal_distribution standardNormal(0, 1); std::uniform_real_distribution distCorr(-1, 1); // generate standard deviations ParametersVector stddev; for (auto i = 0u; i < kSize; ++i) { - stddev[i] = std::abs(distNormal(rng)); + stddev[i] = std::abs(standardNormal(rng)); } // generate correlation matrix CovarianceMatrix corr; @@ -48,32 +63,216 @@ inline auto generateParametersCovariance(generator_t& rng) // construct the covariance matrix CovarianceMatrix cov = stddev.asDiagonal() * corr * stddev.asDiagonal(); - // generate random parameters - // this is ignoring the correlations; since this does not need to generate - // credible data, this should be fine. - ParametersVector params; - for (auto i = 0u; i < kSize; ++i) { - params[i] = stddev[i] * distNormal(rng); + return cov; +} + +/// Generate a random parameters vector and covariance matrix. +/// +/// @return std:::pair +template +inline auto generateParametersCovariance(generator_t& rng) + -> std::pair, + Eigen::Matrix> { + auto params = generateParameters(rng); + auto cov = generateCovariance(rng); + return {params, cov}; +} + +struct GenerateBoundDirectionOptions { + /// Low, high (exclusive) for the transverse direction angle. + double phiMin = -std::numbers::pi; + double phiMax = std::numbers::pi; + + /// Low, high (inclusive) for the longitudinal direction angle. + /// + /// This intentionally uses theta instead of eta so it can represent the + /// full direction space with finite values. + /// + /// @note This is the standard generation, for detector performance + /// classification, where a flat distribution in eta can be useful, + /// this can be set by the etaUniform flag; + /// + double thetaMin = AngleHelpers::thetaFromEta(6.0); + double thetaMax = AngleHelpers::thetaFromEta(-6.0); + + bool etaUniform = true; +}; + +template +inline std::pair generateBoundDirection( + generator_t& rng, const GenerateBoundDirectionOptions& options) { + using UniformReal = std::uniform_real_distribution; + + // since we want to draw the direction uniform on the unit sphere, we must + // draw from cos(theta) instead of theta. see e.g. + // https://mathworld.wolfram.com/SpherePointPicking.html + double cosThetaMin = std::cos(options.thetaMin); + // ensure upper bound is included. see e.g. + // https://en.cppreference.com/w/cpp/numeric/random/uniform_real_distribution + double cosThetaMax = std::nextafter(std::cos(options.thetaMax), + std::numeric_limits::max()); + + // in case we force uniform eta generation + double etaMin = Acts::AngleHelpers::etaFromTheta(options.thetaMin); + double etaMax = Acts::AngleHelpers::etaFromTheta(options.thetaMax); + + UniformReal phiDist(options.phiMin, options.phiMax); + UniformReal cosThetaDist(cosThetaMin, cosThetaMax); + UniformReal etaDist(etaMin, etaMax); + + // draw parameters + double phi = phiDist(rng); + + double theta = 0; + if (!options.etaUniform) { + const double cosTheta = cosThetaDist(rng); + theta = std::acos(cosTheta); + } else { + const double eta = etaDist(rng); + theta = AngleHelpers::thetaFromEta(eta); } + return {phi, theta}; +} + +struct GenerateQoverPOptions { + /// Low, high (exclusive) for absolute/transverse momentum. + double pMin = 1 * UnitConstants::GeV; + double pMax = 100 * UnitConstants::GeV; + + /// Indicate if the momentum referse to transverse momentum + bool pTransverse = true; + + /// Charge of the parameters. + double charge = 1; + + /// Randomize the charge and flip the PDG particle number sign accordingly. + bool randomizeCharge = true; +}; + +template +inline double generateQoverP(generator_t& rng, + const GenerateQoverPOptions& options, + double theta) { + using UniformIndex = std::uniform_int_distribution; + using UniformReal = std::uniform_real_distribution; + + // choose between particle/anti-particle if requested + // the upper limit of the distribution is inclusive + UniformIndex particleTypeChoice(0u, options.randomizeCharge ? 1u : 0u); + // (anti-)particle choice is one random draw but defines two properties + const double qChoices[] = { + options.charge, + -options.charge, + }; + UniformReal pDist(options.pMin, options.pMax); + + // draw parameters + const std::uint8_t type = particleTypeChoice(rng); + const double q = qChoices[type]; + + const double p = + pDist(rng) * (options.pTransverse ? 1. / std::sin(theta) : 1.); + const double qOverP = (q != 0) ? q / p : 1 / p; + + return qOverP; +} + +struct GenerateBoundParametersOptions { + struct { + double loc0Mean = 0 * UnitConstants::mm; + double loc0Std = 1 * UnitConstants::mm; + + double loc1Mean = 0 * UnitConstants::mm; + double loc1Std = 1 * UnitConstants::mm; + + double timeMean = 0 * UnitConstants::ns; + double timeStd = 1 * UnitConstants::ns; + } position; + + GenerateBoundDirectionOptions direction; + + GenerateQoverPOptions qOverP; +}; + +template +inline BoundVector generateBoundParameters( + generator_t& rng, const GenerateBoundParametersOptions& options) { + std::normal_distribution standardNormal(0, 1); + + const double loc0 = options.position.loc0Mean + + options.position.loc0Std * standardNormal(rng); + const double loc1 = options.position.loc1Mean + + options.position.loc1Std * standardNormal(rng); + + auto [phi, theta] = generateBoundDirection(rng, options.direction); + auto qOverP = generateQoverP(rng, options.qOverP, theta); + + const double time = options.position.timeMean + + options.position.timeStd * standardNormal(rng); + + return {loc0, loc1, phi, theta, qOverP, time}; +} + +template +inline std::pair generateBoundParametersCovariance( + generator_t& rng, const GenerateBoundParametersOptions& options) { + auto params = generateBoundParameters(rng, options); + auto cov = generateCovariance(rng); return {params, cov}; } -/// Generate a random bound parameters vector and covariance matrix. +struct GenerateFreeParametersOptions { + struct { + double xMean = 0 * UnitConstants::mm; + double xStd = 1 * UnitConstants::mm; + + double yMean = 0 * UnitConstants::mm; + double yStd = 1 * UnitConstants::mm; + + double zMean = 0 * UnitConstants::mm; + double zStd = 1 * UnitConstants::mm; + + double timeMean = 0 * UnitConstants::ns; + double timeStd = 1 * UnitConstants::ns; + } position; + + GenerateBoundDirectionOptions direction; + + GenerateQoverPOptions qOverP; +}; + template -inline auto generateBoundParametersCovariance(generator_t& rng) { - auto parCov = generateParametersCovariance(rng); - auto [phi, theta] = detail::normalizePhiTheta(parCov.first[eBoundPhi], - parCov.first[eBoundTheta]); - parCov.first[eBoundPhi] = phi; - parCov.first[eBoundTheta] = theta; - return parCov; +inline FreeVector generateFreeParameters( + generator_t& rng, const GenerateFreeParametersOptions& options) { + std::normal_distribution standardNormal(0, 1); + + const double x = + options.position.xMean + options.position.xStd * standardNormal(rng); + const double y = + options.position.yMean + options.position.yStd * standardNormal(rng); + const double z = + options.position.zMean + options.position.zStd * standardNormal(rng); + const double time = options.position.timeMean + + options.position.timeStd * standardNormal(rng); + + auto [phi, theta] = generateBoundDirection(rng, options.direction); + + Vector3 direction = makeDirectionFromPhiTheta(phi, theta); + + auto qOverP = generateQoverP(rng, options.qOverP, theta); + + FreeVector freeParams; + freeParams << x, y, z, time, direction, qOverP; + return freeParams; } -/// Generate a random free parameters vector and covariance matrix. template -inline auto generateFreeParametersCovariance(generator_t& rng) { - return generateParametersCovariance(rng); +inline std::pair generateFreeParametersCovariance( + generator_t& rng, const GenerateFreeParametersOptions& options) { + auto params = generateFreeParameters(rng, options); + auto cov = generateCovariance(rng); + return {params, cov}; } } // namespace Acts::detail::Test diff --git a/Core/include/Acts/EventData/detail/MultiTrajectoryTestsCommon.hpp b/Core/include/Acts/EventData/detail/MultiTrajectoryTestsCommon.hpp index ec64dbe5241..93ef7a3eb7c 100644 --- a/Core/include/Acts/EventData/detail/MultiTrajectoryTestsCommon.hpp +++ b/Core/include/Acts/EventData/detail/MultiTrajectoryTestsCommon.hpp @@ -331,7 +331,7 @@ class MultiTrajectoryTestsCommon { auto tsb = traj.getTrackState(index); // then modify one and check that the other was modified as well { - auto [par, cov] = generateBoundParametersCovariance(rng); + auto [par, cov] = generateBoundParametersCovariance(rng, {}); tsb.predicted() = par; tsb.predictedCovariance() = cov; BOOST_CHECK_EQUAL(tsa.predicted(), par); @@ -340,7 +340,7 @@ class MultiTrajectoryTestsCommon { BOOST_CHECK_EQUAL(tsb.predictedCovariance(), cov); } { - auto [par, cov] = generateBoundParametersCovariance(rng); + auto [par, cov] = generateBoundParametersCovariance(rng, {}); tsb.filtered() = par; tsb.filteredCovariance() = cov; BOOST_CHECK_EQUAL(tsa.filtered(), par); @@ -349,7 +349,7 @@ class MultiTrajectoryTestsCommon { BOOST_CHECK_EQUAL(tsb.filteredCovariance(), cov); } { - auto [par, cov] = generateBoundParametersCovariance(rng); + auto [par, cov] = generateBoundParametersCovariance(rng, {}); tsb.smoothed() = par; tsb.smoothedCovariance() = cov; BOOST_CHECK_EQUAL(tsa.smoothed(), par); @@ -377,7 +377,7 @@ class MultiTrajectoryTestsCommon { } { // reset measurements w/ full parameters - auto [measPar, measCov] = generateBoundParametersCovariance(rng); + auto [measPar, measCov] = generateBoundParametersCovariance(rng, {}); tsb.allocateCalibrated(eBoundSize); tsb.template calibrated() = measPar; tsb.template calibratedCovariance() = measCov; @@ -390,7 +390,7 @@ class MultiTrajectoryTestsCommon { } { // reset only the effective measurements - auto [measPar, measCov] = generateBoundParametersCovariance(rng); + auto [measPar, measCov] = generateBoundParametersCovariance(rng, {}); std::size_t nMeasurements = tsb.effectiveCalibrated().rows(); auto effPar = measPar.head(nMeasurements); auto effCov = measCov.topLeftCorner(nMeasurements, nMeasurements); diff --git a/Core/include/Acts/EventData/detail/TestTrackState.hpp b/Core/include/Acts/EventData/detail/TestTrackState.hpp index 3a83f401cd3..c1471566c5b 100644 --- a/Core/include/Acts/EventData/detail/TestTrackState.hpp +++ b/Core/include/Acts/EventData/detail/TestTrackState.hpp @@ -65,7 +65,7 @@ struct TestTrackState { } // create track parameters - auto [trkPar, trkCov] = generateBoundParametersCovariance(rng); + auto [trkPar, trkCov] = generateBoundParametersCovariance(rng, {}); // trkPar[eBoundPhi] = 45_degree; // trkPar[eBoundTheta] = 90_degree; // trkPar[eBoundQOverP] = 5.; diff --git a/Core/include/Acts/Geometry/NavigationPolicyFactory.hpp b/Core/include/Acts/Geometry/NavigationPolicyFactory.hpp new file mode 100644 index 00000000000..4d9542af826 --- /dev/null +++ b/Core/include/Acts/Geometry/NavigationPolicyFactory.hpp @@ -0,0 +1,220 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 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 https://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/Navigation/INavigationPolicy.hpp" +#include "Acts/Navigation/MultiNavigationPolicy.hpp" + +#include +#include +namespace Acts { + +class TrackingVolume; +class GeometryContext; +class Logger; +class INavigationPolicy; + +namespace detail { +template +class NavigationPolicyFactoryImpl; +} + +/// Base class for navigation policy factories. The factory can be assembled +/// iteratively by using `make` followed by a number of calls to the `add` +/// function of the helper type. Example: +/// +/// ```cpp +/// auto factory = NavigationPolicyFactory::make() +/// .add(arg1, arg2) +/// .add(/*no args*/) +/// .asUniquePtr(); +/// ``` +class NavigationPolicyFactory { + public: + virtual ~NavigationPolicyFactory() = default; + + // This needs to be listed here, but the return type cannot be spelled out + // yet. + static auto make(); + + // This will potentially get serialization interface and deserialization + // functionality + + virtual std::unique_ptr build( + const GeometryContext& gctx, const TrackingVolume& volume, + const Logger& logger) const = 0; +}; + +namespace detail { + +template +concept NavigationPolicyIsolatedFactoryConcept = requires(F f) { + { + f(std::declval(), + std::declval(), std::declval(), + std::declval()...) + } -> std::derived_from; + + requires NavigationPolicyConcept(), + std::declval(), std::declval(), + std::declval()...))>; + + requires(std::is_copy_constructible_v && ...); +}; + +template <> +class NavigationPolicyFactoryImpl<> { + public: + template + friend class NavigationPolicyFactoryImpl; + NavigationPolicyFactoryImpl() = default; + + /// Create a factory with the specified policy added + /// @tparam P The policy type to add + /// @param args The arguments to pass to the policy constructor + /// @note Arguments need to be copy constructible because the factory must be + /// able to execute multiple times. + /// @return A new policy factory including the @c P policy. + template + requires(std::is_constructible_v && + (std::is_copy_constructible_v && ...)) + constexpr auto add(Args&&... args) && { + auto factory = [=](const GeometryContext& gctx, + const TrackingVolume& volume, const Logger& logger) { + return P{gctx, volume, logger, args...}; + }; + + return NavigationPolicyFactoryImpl{ + std::make_tuple(std::move(factory))}; + } + + /// Create a factory with a policy returned by a factory function + /// @tparam Fn The type of the function to construct the policy + /// @param args The arguments to pass to the policy factory + /// @note Arguments need to be copy constructible because the factory must be + /// able to execute multiple times. + /// @return A new policy factory including the function + template + requires(NavigationPolicyIsolatedFactoryConcept) + constexpr auto add(Fn&& fn, Args&&... args) { + auto factory = [=](const GeometryContext& gctx, + const TrackingVolume& volume, const Logger& logger) { + return fn(gctx, volume, logger, args...); + }; + + return NavigationPolicyFactoryImpl{ + std::make_tuple(std::move(factory))}; + } +}; + +template +class NavigationPolicyFactoryImpl : public NavigationPolicyFactory { + public: + /// Create a factory with the specified policy added + /// @tparam P The policy type to add + /// @param args The arguments to pass to the policy constructor + /// @note Arguments need to be copy constructible because the factory must be + /// able to execute multiple times. + /// @return A new policy factory including the @c P policy. + template + requires(std::is_constructible_v && + (std::is_copy_constructible_v && ...)) + constexpr auto add(Args&&... args) && { + auto factory = [=](const GeometryContext& gctx, + const TrackingVolume& volume, const Logger& logger) { + return P{gctx, volume, logger, args...}; + }; + + return NavigationPolicyFactoryImpl{ + std::tuple_cat(std::move(m_factories), + std::make_tuple(std::move(factory)))}; + } + + /// Create a factory with a policy returned by a factory function + /// @tparam Fn The type of the function to construct the policy + /// @param args The arguments to pass to the policy factory + /// @note Arguments need to be copy constructible because the factory must be + /// able to execute multiple times. + /// @return A new policy factory including the function + template + requires(NavigationPolicyIsolatedFactoryConcept) + constexpr auto add(Fn&& fn, Args&&... args) && { + auto factory = [=](const GeometryContext& gctx, + const TrackingVolume& volume, const Logger& logger) { + return fn(gctx, volume, logger, args...); + }; + + return NavigationPolicyFactoryImpl{ + std::tuple_cat(std::move(m_factories), + std::make_tuple(std::move(factory)))}; + } + + /// Move the factory into a unique pointer + /// @note Only callable on rvalue references + /// @return A unique pointer to the factory + constexpr std::unique_ptr> + asUniquePtr() && { + return std::make_unique>( + std::move(*this)); + } + + /// Construct a navigation policy using the factories + /// @param gctx The geometry context + /// @param volume The tracking volume + /// @param logger The logger + auto operator()(const GeometryContext& gctx, const TrackingVolume& volume, + const Logger& logger) const { + return std::apply( + [&](auto&&... factories) { + // Deduce policy type explicitly here... + using policy_type = decltype(MultiNavigationPolicy{ + std::invoke(factories, std::declval(), + std::declval(), + std::declval())...}); + + // ... so we can create a unique_ptr of the concrete type here rather + // than the base. (`make_unique` can't do type deduction) + return std::make_unique( + std::invoke(factories, gctx, volume, logger)...); + }, + m_factories); + } + + /// Construct a navigation policy using the factories + /// @param gctx The geometry context + /// @param volume The tracking volume + /// @param logger The logger + std::unique_ptr build( + const GeometryContext& gctx, const TrackingVolume& volume, + const Logger& logger) const override { + return operator()(gctx, volume, logger); + } + + private: + template + friend class NavigationPolicyFactoryImpl; + + NavigationPolicyFactoryImpl(std::tuple&& factories) + : m_factories(std::move(factories)) {} + + std::tuple m_factories; +}; + +} // namespace detail + +inline auto NavigationPolicyFactory::make() { + return detail::NavigationPolicyFactoryImpl<>{}; +} + +} // namespace Acts diff --git a/Core/include/Acts/Geometry/TrackingVolume.hpp b/Core/include/Acts/Geometry/TrackingVolume.hpp index c980314fa6d..f47f8b440fc 100644 --- a/Core/include/Acts/Geometry/TrackingVolume.hpp +++ b/Core/include/Acts/Geometry/TrackingVolume.hpp @@ -13,13 +13,13 @@ #include "Acts/Geometry/BoundarySurfaceT.hpp" #include "Acts/Geometry/GeometryContext.hpp" #include "Acts/Geometry/GeometryIdentifier.hpp" -#include "Acts/Geometry/GlueVolumesDescriptor.hpp" #include "Acts/Geometry/Layer.hpp" #include "Acts/Geometry/Portal.hpp" #include "Acts/Geometry/TrackingVolumeVisitorConcept.hpp" #include "Acts/Geometry/Volume.hpp" #include "Acts/Material/IVolumeMaterial.hpp" -#include "Acts/Surfaces/BoundaryTolerance.hpp" +#include "Acts/Navigation/NavigationDelegate.hpp" +#include "Acts/Navigation/NavigationStream.hpp" #include "Acts/Surfaces/Surface.hpp" #include "Acts/Surfaces/SurfaceArray.hpp" #include "Acts/Surfaces/SurfaceVisitorConcept.hpp" @@ -35,7 +35,7 @@ #include #include -#include +#include namespace Acts { @@ -43,7 +43,6 @@ class GlueVolumesDescriptor; class VolumeBounds; template struct NavigationOptions; -class GeometryIdentifier; class IMaterialDecorator; class ISurfaceMaterial; class IVolumeMaterial; @@ -51,6 +50,7 @@ class Surface; class TrackingVolume; struct GeometryIdentifierHook; class Portal; +class INavigationPolicy; /// Interface types of the Gen1 geometry model /// @note This interface is being replaced, and is subject to removal @@ -117,8 +117,8 @@ class TrackingVolume : public Volume { ~TrackingVolume() override; TrackingVolume(const TrackingVolume&) = delete; TrackingVolume& operator=(const TrackingVolume&) = delete; - TrackingVolume(TrackingVolume&&) = default; - TrackingVolume& operator=(TrackingVolume&&) = default; + TrackingVolume(TrackingVolume&&); + TrackingVolume& operator=(TrackingVolume&&); /// Constructor for a container Volume /// - vacuum filled volume either as a for other tracking volumes @@ -155,7 +155,6 @@ class TrackingVolume : public Volume { /// @param volumeName is a string identifier TrackingVolume(Volume& volume, const std::string& volumeName = "undefined"); - // @TODO: This needs to be refactored to include Gen3 volumes /// Return the associated sub Volume, returns THIS if no subVolume exists /// @param gctx The current geometry context object, e.g. alignment /// @param position is the global position associated with that search @@ -498,6 +497,21 @@ class TrackingVolume : public Volume { const ViewConfig& portalViewConfig, const ViewConfig& sensitiveViewConfig) const; + /// Register a navigation policy with this volume. The argument can not be + /// nullptr. + /// @param policy is the navigation policy to be registered + void setNavigationPolicy(std::unique_ptr policy); + + /// Populate the navigation stream with navigation candidates from this + /// volume. Internally, this consults the registered navigation policy, where + /// the default is a noop. + /// @param args are the navigation arguments + /// @param stream is the navigation stream to be updated + /// @param logger is the logger + void initializeNavigationCandidates(const NavigationArguments& args, + AppendOnlyNavigationStream& stream, + const Logger& logger) const; + private: void connectDenseBoundarySurfaces( MutableTrackingVolumeVector& confinedDenseVolumes); @@ -561,6 +575,10 @@ class TrackingVolume : public Volume { std::vector> m_volumes; std::vector> m_portals; std::vector> m_surfaces; + + std::unique_ptr m_navigationPolicy; + + NavigationDelegate m_navigationDelegate{}; }; } // namespace Acts diff --git a/Core/include/Acts/Navigation/INavigationPolicy.hpp b/Core/include/Acts/Navigation/INavigationPolicy.hpp new file mode 100644 index 00000000000..6794dbdea09 --- /dev/null +++ b/Core/include/Acts/Navigation/INavigationPolicy.hpp @@ -0,0 +1,73 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 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 https://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/Navigation/NavigationDelegate.hpp" +#include "Acts/Navigation/NavigationStream.hpp" +#include "Acts/Utilities/DelegateChainBuilder.hpp" + +#include + +namespace Acts { + +class TrackingVolume; +class INavigationPolicy; + +/// Concept for a navigation policy +/// This exists so `updateState` can be a non-virtual method and we still have a +/// way to enforce it exists. +template +concept NavigationPolicyConcept = requires { + requires std::is_base_of_v; + // Has a conforming update method + requires requires(T policy, const NavigationArguments& args) { + policy.initializeCandidates(args, + std::declval(), + std::declval()); + }; +}; + +/// Base class for all navigation policies. The policy needs to be *connected* +/// to a delegate via a virtual method for it to become active. The update +/// method is not part of the class interface. The conventional `updateState` +/// method is only required for use with the navigation policy factory, +/// otherwise `connect` is free to connect any function. +class INavigationPolicy { + public: + /// Noop update function that is suitable as a default for default navigation + /// delegates. + static void noopInitializeCandidates(const NavigationArguments& /*unused*/, + AppendOnlyNavigationStream& /*unused*/, + const Logger& /*unused*/) {} + + /// Virtual destructor so policies can be held through this base class. + virtual ~INavigationPolicy() = default; + + /// Connect a policy with a delegate (usually a member of a volume). + /// This method exists to allow a policy to ensure a non-virtual function is + /// registered with the delegate. + /// @param delegate The delegate to connect to + virtual void connect(NavigationDelegate& delegate) const = 0; + + protected: + /// Internal helper function for derived classes that conform to the concept + /// and have a conventional `updateState` method. Mainly used to save some + /// boilerplate. + /// @tparam T The type of the navigation policy + /// @param delegate The delegate to connect to + template + void connectDefault(NavigationDelegate& delegate) const { + // This cannot be a concept because we use it in CRTP below + const auto* self = static_cast(this); + DelegateChainBuilder{delegate}.add<&T::initializeCandidates>(self).store( + delegate); + } +}; + +} // namespace Acts diff --git a/Core/include/Acts/Navigation/MultiNavigationPolicy.hpp b/Core/include/Acts/Navigation/MultiNavigationPolicy.hpp new file mode 100644 index 00000000000..c5c8a397aea --- /dev/null +++ b/Core/include/Acts/Navigation/MultiNavigationPolicy.hpp @@ -0,0 +1,78 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 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 https://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/Navigation/INavigationPolicy.hpp" + +namespace Acts { + +/// Base class for multi navigation policies +class MultiNavigationPolicyBase : public INavigationPolicy {}; + +/// Combined navigation policy that calls all contained other navigation +/// policies. This class only works with policies complying with +/// `NavigationPolicyConcept`, which means that they have a conventional +/// `updateState` method. +/// +/// Internally, this uses a delegate chain factory to produce an unrolled +/// delegate chain. +/// +/// @tparam Policies The navigation policies to be combined +template + requires(sizeof...(Policies) > 0) +class MultiNavigationPolicy final : public MultiNavigationPolicyBase { + public: + /// Constructor from a set of child policies. + /// @param policies The child policies + MultiNavigationPolicy(Policies&&... policies) + : m_policies{std::move(policies)...} {} + + /// Implementation of the connection to a navigation delegate. + /// It uses the delegate chain factory to produce a delegate chain and stores + /// that chain in the owning navigation delegate. + /// @param delegate The navigation delegate to connect to + void connect(NavigationDelegate& delegate) const override { + auto factory = add(DelegateChainBuilder{delegate}, + std::index_sequence_for{}); + + factory.store(delegate); + } + + /// Access the contained policies + /// @return The contained policies + const std::tuple& policies() const { return m_policies; } + + private: + /// Internal helper to build the delegate chain + template + constexpr auto add( + auto factory, + std::integer_sequence /*unused*/) const { + return add(factory); + } + + /// Internal helper to build the delegate chain + template + constexpr auto add(auto factory) const { + using policy_type = std::tuple_element_t; + auto* policy = &std::get(m_policies); + + auto added = + factory.template add<&policy_type::initializeCandidates>(policy); + + if constexpr (sizeof...(Is) > 0) { + return add(added); + } else { + return added; + } + } + + std::tuple m_policies; +}; +} // namespace Acts diff --git a/Core/include/Acts/Navigation/NavigationDelegate.hpp b/Core/include/Acts/Navigation/NavigationDelegate.hpp new file mode 100644 index 00000000000..8e243818d30 --- /dev/null +++ b/Core/include/Acts/Navigation/NavigationDelegate.hpp @@ -0,0 +1,35 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 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 https://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/Definitions/Algebra.hpp" +#include "Acts/Navigation/NavigationStream.hpp" +#include "Acts/Surfaces/BoundaryTolerance.hpp" +#include "Acts/Utilities/Delegate.hpp" + +namespace Acts { + +class NavigationStream; +class Logger; + +/// Struct that serves as the argument to the navigation delegate. +/// It is not supposed to be used as an lvalue. +struct NavigationArguments { + Vector3 position; + Vector3 direction; + + BoundaryTolerance tolerance = BoundaryTolerance::None(); +}; + +/// Central alias for the navigation delegate. This type is owning to support +/// (type-erased) navigation delegate chains (i.e. multiple policies). +using NavigationDelegate = OwningDelegate; + +} // namespace Acts diff --git a/Core/include/Acts/Navigation/NavigationStream.hpp b/Core/include/Acts/Navigation/NavigationStream.hpp index 71443e48915..97e154214d7 100644 --- a/Core/include/Acts/Navigation/NavigationStream.hpp +++ b/Core/include/Acts/Navigation/NavigationStream.hpp @@ -10,9 +10,11 @@ #include "Acts/Definitions/Algebra.hpp" #include "Acts/Geometry/GeometryContext.hpp" +#include "Acts/Geometry/Portal.hpp" #include "Acts/Surfaces/BoundaryTolerance.hpp" #include "Acts/Utilities/Intersection.hpp" +#include #include #include @@ -22,7 +24,6 @@ namespace Acts { namespace Experimental { class Portal; } -using namespace Experimental; class Surface; @@ -55,6 +56,7 @@ class NavigationStream { ObjectIntersection intersection = ObjectIntersection::invalid(); /// The portal + const Acts::Experimental::Portal* gen2Portal = nullptr; const Portal* portal = nullptr; /// The boundary tolerance BoundaryTolerance bTolerance = BoundaryTolerance::None(); @@ -116,25 +118,27 @@ class NavigationStream { /// /// @param surface the surface to be filled /// @param bTolerance the boundary tolerance used for the intersection - void addSurfaceCandidate(const Surface* surface, + void addSurfaceCandidate(const Surface& surface, const BoundaryTolerance& bTolerance); /// Fill n surfaces into the candidate vector /// /// @param surfaces the surfaces that are filled in /// @param bTolerance the boundary tolerance used for the intersection - void addSurfaceCandidates(const std::vector& surfaces, + void addSurfaceCandidates(std::span surfaces, const BoundaryTolerance& bTolerance); /// Fill one portal into the candidate vector /// + void addPortalCandidate(const Experimental::Portal& portal); /// @param portal the portals that are filled in - void addPortalCandidate(const Portal* portal); + + void addPortalCandidate(const Portal& portal); /// Fill n portals into the candidate vector /// /// @param portals the portals that are filled in - void addPortalCandidates(const std::vector& portals); + void addPortalCandidates(std::span portals); /// Initialize the stream from a query point /// @@ -175,4 +179,14 @@ class NavigationStream { std::size_t m_currentIndex = 0u; }; +struct AppendOnlyNavigationStream { + explicit AppendOnlyNavigationStream(NavigationStream& stream); + void addSurfaceCandidate(const Surface& surface, + const BoundaryTolerance& bTolerance); + void addPortalCandidate(const Portal& portal); + + private: + NavigationStream* m_stream; +}; + } // namespace Acts diff --git a/Core/include/Acts/Navigation/SurfaceArrayNavigationPolicy.hpp b/Core/include/Acts/Navigation/SurfaceArrayNavigationPolicy.hpp new file mode 100644 index 00000000000..9143cf0087d --- /dev/null +++ b/Core/include/Acts/Navigation/SurfaceArrayNavigationPolicy.hpp @@ -0,0 +1,83 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 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 https://mozilla.org/MPL/2.0/. + +#include "Acts/Navigation/INavigationPolicy.hpp" + +#pragma once + +namespace Acts { + +class SurfaceArray; + +/// A navigation policy that internally uses the Gen1 @c SurfaceArray class +class SurfaceArrayNavigationPolicy : public INavigationPolicy { + public: + /// Enum for configuring which type of surface array to produce. This affects + /// the projection that is used for creating the binning structure. + enum class LayerType { Cylinder, Disc, Plane }; + + /// Config struct to configure the surface array navigation + struct Config { + /// The type of the layer + LayerType layerType = LayerType::Cylinder; + /// The number of bins in the local directions. The interpretation depends + /// on the layer type. + std::pair bins; + }; + + /// Main constructor, which internally creates the surface array acceleration + /// structure. + /// @note Expects that all relevant surfaces are registered with @p volume. + /// Only selects sensitive surfaces for the surface array. + /// @param gctx The geometry context + /// @param volume The *layer volume* to construct the surface array from + /// @param logger A logging instance + /// @param config The configuration for the surface array + explicit SurfaceArrayNavigationPolicy(const GeometryContext& gctx, + const TrackingVolume& volume, + const Logger& logger, Config config); + + /// Update the navigation state from the surface array + /// @param args The navigation arguments + /// @param stream The navigation stream to update + /// @param logger The logger + void initializeCandidates(const NavigationArguments& args, + AppendOnlyNavigationStream& stream, + const Logger& logger) const; + + /// Connect this policy with a navigation delegate + /// @param delegate The navigation delegate to connect to + void connect(NavigationDelegate& delegate) const override; + + /// Output stream operator for the contained layer type enum + /// @param os The output stream + /// @param layerType The layer type to print + friend std::ostream& operator<<(std::ostream& os, + const LayerType& layerType) { + switch (layerType) { + case LayerType::Cylinder: + os << "Cylinder"; + break; + case LayerType::Disc: + os << "Disc"; + break; + case LayerType::Plane: + os << "Plane"; + break; + } + return os; + } + + private: + std::unique_ptr m_surfaceArray{}; + const TrackingVolume& m_volume; +}; + +static_assert(NavigationPolicyConcept); + +} // namespace Acts diff --git a/Core/include/Acts/Navigation/TryAllNavigationPolicy.hpp b/Core/include/Acts/Navigation/TryAllNavigationPolicy.hpp new file mode 100644 index 00000000000..e7dc4bb3ddc --- /dev/null +++ b/Core/include/Acts/Navigation/TryAllNavigationPolicy.hpp @@ -0,0 +1,63 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 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 https://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/Navigation/INavigationPolicy.hpp" +#include "Acts/Navigation/NavigationStream.hpp" + +namespace Acts { + +class TrackingVolume; +class GeometryContext; +class Logger; + +/// Policy which adds **all** candidates of the configured type to the +/// stream +class TryAllNavigationPolicy final : public INavigationPolicy { + public: + struct Config { + bool portals = true; + bool sensitives = true; + }; + + /// Constructor from a volume + /// @param config The configuration for the policy + /// @param gctx is the geometry context + /// @param volume is the volume to navigate + /// @param logger is the logger + TryAllNavigationPolicy(const Config& config, const GeometryContext& gctx, + const TrackingVolume& volume, const Logger& logger); + + /// Constructor from a volume + /// @param gctx is the geometry context + /// @param volume is the volume to navigate + /// @param logger is the logger + TryAllNavigationPolicy(const GeometryContext& gctx, + const TrackingVolume& volume, const Logger& logger); + + /// Add all candidates to the stream + /// @param args are the navigation arguments + /// @param stream is the navigation stream to update + /// @param logger is the logger + void initializeCandidates(const NavigationArguments& args, + AppendOnlyNavigationStream& stream, + const Logger& logger) const; + + /// Connect the policy to a navigation delegate + /// @param delegate is the navigation delegate + void connect(NavigationDelegate& delegate) const override; + + private: + Config m_cfg; + const TrackingVolume* m_volume; +}; + +static_assert(NavigationPolicyConcept); + +} // namespace Acts diff --git a/Core/src/Geometry/TrackingVolume.cpp b/Core/src/Geometry/TrackingVolume.cpp index e7d069b80c5..611904537f0 100644 --- a/Core/src/Geometry/TrackingVolume.cpp +++ b/Core/src/Geometry/TrackingVolume.cpp @@ -16,6 +16,8 @@ #include "Acts/Material/IMaterialDecorator.hpp" #include "Acts/Material/IVolumeMaterial.hpp" #include "Acts/Material/ProtoVolumeMaterial.hpp" +#include "Acts/Navigation/INavigationPolicy.hpp" +#include "Acts/Navigation/NavigationStream.hpp" #include "Acts/Propagator/Navigator.hpp" #include "Acts/Surfaces/RegularSurface.hpp" #include "Acts/Surfaces/Surface.hpp" @@ -28,6 +30,8 @@ #include #include +#include + namespace Acts { // constructor for arguments @@ -47,6 +51,10 @@ TrackingVolume::TrackingVolume( createBoundarySurfaces(); interlinkLayers(); connectDenseBoundarySurfaces(denseVolumeVector); + + DelegateChainBuilder{m_navigationDelegate} + .add<&INavigationPolicy::noopInitializeCandidates>() + .store(m_navigationDelegate); } TrackingVolume::TrackingVolume(Volume& volume, const std::string& volumeName) @@ -61,6 +69,8 @@ TrackingVolume::TrackingVolume(const Transform3& transform, {}, volumeName) {} TrackingVolume::~TrackingVolume() = default; +TrackingVolume::TrackingVolume(TrackingVolume&&) = default; +TrackingVolume& TrackingVolume::operator=(TrackingVolume&&) = default; const TrackingVolume* TrackingVolume::lowestTrackingVolume( const GeometryContext& gctx, const Vector3& position, @@ -735,4 +745,20 @@ void TrackingVolume::visualize(IVisualization3D& helper, } } +void TrackingVolume::setNavigationPolicy( + std::unique_ptr policy) { + if (policy == nullptr) { + throw std::invalid_argument("Navigation policy is nullptr"); + } + + m_navigationPolicy = std::move(policy); + m_navigationPolicy->connect(m_navigationDelegate); +} + +void TrackingVolume::initializeNavigationCandidates( + const NavigationArguments& args, AppendOnlyNavigationStream& stream, + const Logger& logger) const { + m_navigationDelegate(args, stream, logger); +} + } // namespace Acts diff --git a/Core/src/Navigation/CMakeLists.txt b/Core/src/Navigation/CMakeLists.txt index 81cbf616d44..72bbf65b853 100644 --- a/Core/src/Navigation/CMakeLists.txt +++ b/Core/src/Navigation/CMakeLists.txt @@ -1 +1,7 @@ -target_sources(ActsCore PRIVATE NavigationStream.cpp) +target_sources( + ActsCore + PRIVATE + NavigationStream.cpp + TryAllNavigationPolicy.cpp + SurfaceArrayNavigationPolicy.cpp +) diff --git a/Core/src/Navigation/NavigationStream.cpp b/Core/src/Navigation/NavigationStream.cpp index bf15367cd3b..c36eed12bbe 100644 --- a/Core/src/Navigation/NavigationStream.cpp +++ b/Core/src/Navigation/NavigationStream.cpp @@ -13,10 +13,12 @@ #include -bool Acts::NavigationStream::initialize(const GeometryContext& gctx, - const QueryPoint& queryPoint, - const BoundaryTolerance& cTolerance, - ActsScalar onSurfaceTolerance) { +namespace Acts { + +bool NavigationStream::initialize(const GeometryContext& gctx, + const QueryPoint& queryPoint, + const BoundaryTolerance& cTolerance, + ActsScalar onSurfaceTolerance) { // Position and direction from the query point const Vector3& position = queryPoint.position; const Vector3& direction = queryPoint.direction; @@ -36,7 +38,7 @@ bool Acts::NavigationStream::initialize(const GeometryContext& gctx, // A container collecting additional candidates from multiple // valid interseciton std::vector additionalCandidates = {}; - for (auto& [sIntersection, portal, bTolerance] : m_candidates) { + for (auto& [sIntersection, gen2Portal, portal, bTolerance] : m_candidates) { // Get the surface from the object intersection const Surface* surface = sIntersection.object(); // Intersect the surface @@ -58,7 +60,10 @@ bool Acts::NavigationStream::initialize(const GeometryContext& gctx, originalCandidateUpdated = true; } else { additionalCandidates.emplace_back( - Candidate{rsIntersection, portal, bTolerance}); + Candidate{.intersection = rsIntersection, + .gen2Portal = gen2Portal, + .portal = portal, + .bTolerance = bTolerance}); } } } @@ -74,7 +79,7 @@ bool Acts::NavigationStream::initialize(const GeometryContext& gctx, // The we find the first invalid candidate auto firstInvalid = std::ranges::find_if(m_candidates, [](const Candidate& a) { - const auto& [aIntersection, aPortal, aTolerance] = a; + const auto& [aIntersection, aGen2Portal, aPortal, aTolerance] = a; return !aIntersection.isValid(); }); @@ -88,32 +93,28 @@ bool Acts::NavigationStream::initialize(const GeometryContext& gctx, return true; } -bool Acts::NavigationStream::update(const GeometryContext& gctx, - const QueryPoint& queryPoint, - ActsScalar onSurfaceTolerance) { - // Position and direction from the query point - const Vector3& position = queryPoint.position; - const Vector3& direction = queryPoint.direction; - +bool NavigationStream::update(const GeometryContext& gctx, + const QueryPoint& queryPoint, + ActsScalar onSurfaceTolerance) { // Loop over the (currently valid) candidates and update for (; m_currentIndex < m_candidates.size(); ++m_currentIndex) { // Get the candidate, and resolve the tuple Candidate& candidate = currentCandidate(); - auto& [sIntersection, portal, bTolerance] = candidate; // Get the surface from the object intersection - const Surface* surface = sIntersection.object(); + const Surface* surface = candidate.intersection.object(); // (re-)Intersect the surface - auto multiIntersection = surface->intersect(gctx, position, direction, - bTolerance, onSurfaceTolerance); + auto multiIntersection = + surface->intersect(gctx, queryPoint.position, queryPoint.direction, + candidate.bTolerance, onSurfaceTolerance); // Split them into valid intersections for (const auto& rsIntersection : multiIntersection.split()) { // Skip wrong index solution - if (rsIntersection.index() != sIntersection.index()) { + if (rsIntersection.index() != candidate.intersection.index()) { continue; } // Valid solution is either on surface or updates the distance if (rsIntersection.isValid()) { - sIntersection = rsIntersection; + candidate.intersection = rsIntersection; return true; } } @@ -122,32 +123,57 @@ bool Acts::NavigationStream::update(const GeometryContext& gctx, return false; } -void Acts::NavigationStream::addSurfaceCandidate( - const Surface* surface, const BoundaryTolerance& bTolerance) { - m_candidates.emplace_back(Candidate{ - ObjectIntersection::invalid(surface), nullptr, bTolerance}); +void NavigationStream::addSurfaceCandidate( + const Surface& surface, const BoundaryTolerance& bTolerance) { + m_candidates.emplace_back( + Candidate{.intersection = ObjectIntersection::invalid(&surface), + .bTolerance = bTolerance}); } -void Acts::NavigationStream::addSurfaceCandidates( - const std::vector& surfaces, - const BoundaryTolerance& bTolerance) { +void NavigationStream::addSurfaceCandidates( + std::span surfaces, const BoundaryTolerance& bTolerance) { std::ranges::for_each(surfaces, [&](const auto* surface) { - m_candidates.emplace_back(Candidate{ - ObjectIntersection::invalid(surface), nullptr, bTolerance}); + m_candidates.emplace_back( + Candidate{.intersection = ObjectIntersection::invalid(surface), + .bTolerance = bTolerance}); }); } -void Acts::NavigationStream::addPortalCandidate(const Portal* portal) { - m_candidates.emplace_back( - Candidate{ObjectIntersection::invalid(&(portal->surface())), - portal, BoundaryTolerance::None()}); +void NavigationStream::addPortalCandidate(const Experimental::Portal& portal) { + m_candidates.emplace_back(Candidate{ + .intersection = ObjectIntersection::invalid(&portal.surface()), + .gen2Portal = &portal, + .bTolerance = BoundaryTolerance::None()}); +} + +void NavigationStream::addPortalCandidate(const Portal& portal) { + m_candidates.emplace_back(Candidate{ + .intersection = ObjectIntersection::invalid(&portal.surface()), + .portal = &portal, + .bTolerance = BoundaryTolerance::None()}); } -void Acts::NavigationStream::addPortalCandidates( - const std::vector& portals) { +void NavigationStream::addPortalCandidates( + std::span portals) { std::ranges::for_each(portals, [&](const auto& portal) { - m_candidates.emplace_back( - Candidate{ObjectIntersection::invalid(&(portal->surface())), - portal, BoundaryTolerance::None()}); + m_candidates.emplace_back(Candidate{ + .intersection = + ObjectIntersection::invalid(&(portal->surface())), + .gen2Portal = portal, + .bTolerance = BoundaryTolerance::None()}); }); } + +AppendOnlyNavigationStream::AppendOnlyNavigationStream(NavigationStream& stream) + : m_stream{&stream} {} + +void AppendOnlyNavigationStream::addPortalCandidate(const Portal& portal) { + m_stream->addPortalCandidate(portal); +} + +void AppendOnlyNavigationStream::addSurfaceCandidate( + const Surface& surface, const BoundaryTolerance& bTolerance) { + m_stream->addSurfaceCandidate(surface, bTolerance); +} + +} // namespace Acts diff --git a/Core/src/Navigation/SurfaceArrayNavigationPolicy.cpp b/Core/src/Navigation/SurfaceArrayNavigationPolicy.cpp new file mode 100644 index 00000000000..f40621fdd06 --- /dev/null +++ b/Core/src/Navigation/SurfaceArrayNavigationPolicy.cpp @@ -0,0 +1,83 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 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 https://mozilla.org/MPL/2.0/. + +#include "Acts/Navigation/SurfaceArrayNavigationPolicy.hpp" + +#include "Acts/Geometry/SurfaceArrayCreator.hpp" +#include "Acts/Geometry/TrackingVolume.hpp" +#include "Acts/Navigation/NavigationStream.hpp" + +#include + +namespace Acts { + +SurfaceArrayNavigationPolicy::SurfaceArrayNavigationPolicy( + const GeometryContext& gctx, const TrackingVolume& volume, + const Logger& logger, Config config) + : m_volume(volume) { + ACTS_VERBOSE("Constructing SurfaceArrayNavigationPolicy for volume " + << volume.volumeName()); + ACTS_VERBOSE("~> Layer type is " << config.layerType); + ACTS_VERBOSE("~> bins: " << config.bins.first << " x " << config.bins.second); + + SurfaceArrayCreator::Config sacConfig; + SurfaceArrayCreator sac{sacConfig, logger.clone("SrfArrCrtr")}; + + std::vector> surfaces; + surfaces.reserve(volume.surfaces().size()); + for (const auto& surface : volume.surfaces()) { + if (surface.associatedDetectorElement() == nullptr) { + continue; + } + surfaces.push_back(surface.getSharedPtr()); + } + + if (config.layerType == LayerType::Disc) { + auto [binsR, binsPhi] = config.bins; + m_surfaceArray = + sac.surfaceArrayOnDisc(gctx, std::move(surfaces), binsPhi, binsR); + } else if (config.layerType == LayerType::Cylinder) { + auto [binsPhi, binsZ] = config.bins; + m_surfaceArray = + sac.surfaceArrayOnCylinder(gctx, std::move(surfaces), binsPhi, binsZ); + // m_surfaces = sac.createCylinderSurfaces(config.bins.first, + // config.bins.second); + } else if (config.layerType == LayerType::Plane) { + ACTS_ERROR("Plane layers are not yet supported"); + throw std::invalid_argument("Plane layers are not yet supported"); + } else { + throw std::invalid_argument("Unknown layer type"); + } + + if (!m_surfaceArray) { + ACTS_ERROR("Failed to create surface array"); + throw std::runtime_error("Failed to create surface array"); + } +} + +void SurfaceArrayNavigationPolicy::initializeCandidates( + const NavigationArguments& args, AppendOnlyNavigationStream& stream, + const Logger& logger) const { + ACTS_VERBOSE("SrfArrNavPol (volume=" << m_volume.volumeName() << ")"); + + ACTS_VERBOSE("Querying sensitive surfaces at " << args.position.transpose()); + const std::vector& sensitiveSurfaces = + m_surfaceArray->neighbors(args.position); + ACTS_VERBOSE("~> Surface array reports " << sensitiveSurfaces.size() + << " sensitive surfaces"); + + for (const auto* surface : sensitiveSurfaces) { + stream.addSurfaceCandidate(*surface, args.tolerance); + }; +} + +void SurfaceArrayNavigationPolicy::connect(NavigationDelegate& delegate) const { + connectDefault(delegate); +} + +} // namespace Acts diff --git a/Core/src/Navigation/TryAllNavigationPolicy.cpp b/Core/src/Navigation/TryAllNavigationPolicy.cpp new file mode 100644 index 00000000000..40ca70c4079 --- /dev/null +++ b/Core/src/Navigation/TryAllNavigationPolicy.cpp @@ -0,0 +1,54 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 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 https://mozilla.org/MPL/2.0/. + +#include "Acts/Navigation/TryAllNavigationPolicy.hpp" + +#include "Acts/Geometry/TrackingVolume.hpp" +#include "Acts/Navigation/NavigationStream.hpp" + +namespace Acts { + +TryAllNavigationPolicy::TryAllNavigationPolicy(const Config& config, + const GeometryContext& /*gctx*/, + const TrackingVolume& volume, + const Logger& logger) + : m_cfg{config}, m_volume(&volume) { + assert(m_volume != nullptr); + ACTS_VERBOSE("TryAllNavigationPolicy created for volume " + << m_volume->volumeName()); +} + +TryAllNavigationPolicy::TryAllNavigationPolicy(const GeometryContext& gctx, + const TrackingVolume& volume, + const Logger& logger) + : TryAllNavigationPolicy({}, gctx, volume, logger) {} + +void TryAllNavigationPolicy::initializeCandidates( + const NavigationArguments& args, AppendOnlyNavigationStream& stream, + const Logger& logger) const { + ACTS_VERBOSE("TryAllNavigationPolicy"); + assert(m_volume != nullptr); + + if (m_cfg.portals) { + for (const auto& portal : m_volume->portals()) { + stream.addPortalCandidate(portal); + } + } + + if (m_cfg.sensitives) { + for (const auto& surface : m_volume->surfaces()) { + stream.addSurfaceCandidate(surface, args.tolerance); + }; + } +} + +void TryAllNavigationPolicy::connect(NavigationDelegate& delegate) const { + connectDefault(delegate); +} + +} // namespace Acts diff --git a/Examples/Algorithms/Generators/ActsExamples/Generators/ParametricParticleGenerator.cpp b/Examples/Algorithms/Generators/ActsExamples/Generators/ParametricParticleGenerator.cpp index d6b6bb9eb30..dcc7ac573f3 100644 --- a/Examples/Algorithms/Generators/ActsExamples/Generators/ParametricParticleGenerator.cpp +++ b/Examples/Algorithms/Generators/ActsExamples/Generators/ParametricParticleGenerator.cpp @@ -89,7 +89,7 @@ ParametricParticleGenerator::operator()(RandomEngine& rng) { sinTheta = std::sqrt(1 - cosTheta * cosTheta); } else { const double eta = etaDist(rng); - const double theta = 2 * std::atan(std::exp(-eta)); + const double theta = Acts::AngleHelpers::thetaFromEta(eta); sinTheta = std::sin(theta); cosTheta = std::cos(theta); } diff --git a/Examples/Detectors/DD4hepDetector/include/ActsExamples/DD4hepDetector/DD4hepGeometryService.hpp b/Examples/Detectors/DD4hepDetector/include/ActsExamples/DD4hepDetector/DD4hepGeometryService.hpp index 9ce1ca5f2e9..edbb720825c 100644 --- a/Examples/Detectors/DD4hepDetector/include/ActsExamples/DD4hepDetector/DD4hepGeometryService.hpp +++ b/Examples/Detectors/DD4hepDetector/include/ActsExamples/DD4hepDetector/DD4hepGeometryService.hpp @@ -52,7 +52,7 @@ class DD4hepGeometryService { /// Log level for the geometry service. Acts::Logging::Level logLevel = Acts::Logging::Level::INFO; /// Log level for DD4hep itself - Acts::Logging::Level dd4hepLogLevel = Acts::Logging::Level::INFO; + Acts::Logging::Level dd4hepLogLevel = Acts::Logging::Level::WARNING; /// XML-file with the detector description std::vector xmlFileNames; /// The name of the service diff --git a/Examples/Python/CMakeLists.txt b/Examples/Python/CMakeLists.txt index 0a834ae546f..0eb4b1ff4ec 100644 --- a/Examples/Python/CMakeLists.txt +++ b/Examples/Python/CMakeLists.txt @@ -21,6 +21,7 @@ pybind11_add_module(ActsPythonBindings src/Output.cpp src/Input.cpp src/Propagation.cpp + src/Navigation.cpp src/Generators.cpp src/Obj.cpp src/TruthTracking.cpp diff --git a/Examples/Python/python/acts/examples/odd.py b/Examples/Python/python/acts/examples/odd.py index a28058af78b..5e187948ee2 100644 --- a/Examples/Python/python/acts/examples/odd.py +++ b/Examples/Python/python/acts/examples/odd.py @@ -90,7 +90,7 @@ def geoid_hook(geoid, surface): dd4hepConfig = acts.examples.dd4hep.DD4hepGeometryService.Config( xmlFileNames=[str(odd_xml)], logLevel=customLogLevel(), - dd4hepLogLevel=customLogLevel(), + dd4hepLogLevel=customLogLevel(minLevel=acts.logging.WARNING), geometryIdentifierHook=acts.GeometryIdentifierHook(geoid_hook), ) detector = acts.examples.dd4hep.DD4hepDetector() diff --git a/Examples/Python/src/ModuleEntry.cpp b/Examples/Python/src/ModuleEntry.cpp index 1a0e27db907..a74d277f7a5 100644 --- a/Examples/Python/src/ModuleEntry.cpp +++ b/Examples/Python/src/ModuleEntry.cpp @@ -50,6 +50,7 @@ void addBinning(Context& ctx); void addEventData(Context& ctx); void addPropagation(Context& ctx); +void addNavigation(Context& ctx); void addGeometry(Context& ctx); void addGeometryBuildingGen1(Context& ctx); @@ -123,6 +124,7 @@ PYBIND11_MODULE(ActsPythonBindings, m) { addOutput(ctx); addPropagation(ctx); + addNavigation(ctx); addGeometryBuildingGen1(ctx); addGeometry(ctx); addExperimentalGeometry(ctx); diff --git a/Examples/Python/src/Navigation.cpp b/Examples/Python/src/Navigation.cpp new file mode 100644 index 00000000000..ac2f1a77ef6 --- /dev/null +++ b/Examples/Python/src/Navigation.cpp @@ -0,0 +1,203 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 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 https://mozilla.org/MPL/2.0/. + +#include "Acts/Geometry/CylinderVolumeBounds.hpp" +#include "Acts/Geometry/NavigationPolicyFactory.hpp" +#include "Acts/Geometry/TrackingVolume.hpp" +#include "Acts/Navigation/SurfaceArrayNavigationPolicy.hpp" +#include "Acts/Navigation/TryAllNavigationPolicy.hpp" +#include "Acts/Plugins/Python/Utilities.hpp" +#include "Acts/Surfaces/CylinderBounds.hpp" +#include "Acts/Surfaces/CylinderSurface.hpp" +#include "Acts/Utilities/Logger.hpp" +#include "Acts/Utilities/TypeTag.hpp" + +#include +#include +#include + +#include +#include +#include + +namespace py = pybind11; +using namespace pybind11::literals; + +namespace Acts::Python { + +struct AnyNavigationPolicyFactory : public Acts::NavigationPolicyFactory { + virtual std::unique_ptr add( + TypeTag /*type*/) = 0; + + virtual std::unique_ptr add( + TypeTag /*type*/, + SurfaceArrayNavigationPolicy::Config config) = 0; +}; + +template , + typename... Policies> +struct NavigationPolicyFactoryT : public AnyNavigationPolicyFactory { + NavigationPolicyFactoryT(Factory impl) + requires(sizeof...(Policies) > 0) + : m_impl(std::move(impl)) {} + + NavigationPolicyFactoryT() + requires(sizeof...(Policies) == 0) + : m_impl{} {} + + std::unique_ptr add( + TypeTag /*type*/) override { + return add(); + } + + std::unique_ptr add( + TypeTag /*type*/, + SurfaceArrayNavigationPolicy::Config config) override { + return add(std::move(config)); + } + + std::unique_ptr build( + const GeometryContext& gctx, const TrackingVolume& volume, + const Logger& logger) const override { + if constexpr (sizeof...(Policies) > 0) { + return m_impl.build(gctx, volume, logger); + } else { + throw std::runtime_error("No policies added to the factory"); + } + } + + private: + template + std::unique_ptr add(Args&&... args) { + if constexpr (!((std::is_same_v || ...))) { + auto impl = + std::move(m_impl).template add(std::forward(args)...); + return std::make_unique< + NavigationPolicyFactoryT>( + std::move(impl)); + } else { + throw std::invalid_argument("Policy already added to the factory"); + } + } + + Factory m_impl; +}; + +class NavigationPolicyFactory : public Acts::NavigationPolicyFactory { + public: + // This overload is for all the navigation policies that don't have extra + // arguments + NavigationPolicyFactory& addNoArguments(const py::object& cls) { + auto m = py::module_::import("acts"); + if (py::object o = m.attr("TryAllNavigationPolicy"); cls.is(o)) { + m_impl = m_impl->add(Type); + } + // Add other policies here + return *this; + } + + NavigationPolicyFactory& addSurfaceArray( + const py::object& /*cls*/, + const SurfaceArrayNavigationPolicy::Config& config) { + m_impl = m_impl->add(Type, config); + return *this; + } + + std::unique_ptr build( + const GeometryContext& gctx, const TrackingVolume& volume, + const Logger& logger) const override { + return m_impl->build(gctx, volume, logger); + } + + private: + std::unique_ptr m_impl = + std::make_unique>(); +}; + +namespace Test { +class DetectorElementStub : public DetectorElementBase { + public: + DetectorElementStub() : DetectorElementBase() {} + + const Transform3& transform(const GeometryContext&) const override { + return m_transform; + } + + /// Return surface representation - const return pattern + const Surface& surface() const override { + throw std::runtime_error("Not implemented"); + } + + /// Non-const return pattern + Surface& surface() override { throw std::runtime_error("Not implemented"); } + + /// Returns the thickness of the module + /// @return double that indicates the thickness of the module + double thickness() const override { return 0; } + + private: + Transform3 m_transform; +}; + +} // namespace Test + +void addNavigation(Context& ctx) { + auto m = ctx.get("main"); + + py::class_>( + m, "_NavigationPolicyFactory"); + + py::class_(m, "TryAllNavigationPolicy"); + + py::class_>( + m, "NavigationPolicyFactory") + // only to mirror the C++ API + .def_static("make", []() { return NavigationPolicyFactory{}; }) + .def("add", &NavigationPolicyFactory::addNoArguments) + .def("add", &NavigationPolicyFactory::addSurfaceArray) + .def("_buildTest", [](NavigationPolicyFactory& self) { + auto vol1 = std::make_shared( + Transform3::Identity(), + std::make_shared(30, 40, 100)); + vol1->setVolumeName("TestVolume"); + + auto detElem = std::make_unique(); + + auto surface = Surface::makeShared( + Transform3::Identity(), std::make_shared(30, 40)); + surface->assignDetectorElement(*detElem); + + vol1->addSurface(std::move(surface)); + + std::unique_ptr result = + self.build(GeometryContext{}, *vol1, + *getDefaultLogger("Test", Logging::VERBOSE)); + }); + + { + auto saPolicy = py::class_( + m, "SurfaceArrayNavigationPolicy"); + + using LayerType = SurfaceArrayNavigationPolicy::LayerType; + py::enum_(saPolicy, "LayerType") + .value("Cylinder", LayerType::Cylinder) + .value("Disc", LayerType::Disc) + .value("Plane", LayerType::Plane); + + using Config = SurfaceArrayNavigationPolicy::Config; + auto c = py::class_(saPolicy, "Config").def(py::init<>()); + ACTS_PYTHON_STRUCT_BEGIN(c, Config); + ACTS_PYTHON_MEMBER(layerType); + ACTS_PYTHON_MEMBER(bins); + ACTS_PYTHON_STRUCT_END(); + } +} + +} // namespace Acts::Python diff --git a/Examples/Python/tests/test_navigation.py b/Examples/Python/tests/test_navigation.py new file mode 100644 index 00000000000..55ec8ac9540 --- /dev/null +++ b/Examples/Python/tests/test_navigation.py @@ -0,0 +1,42 @@ +import pytest + +import acts + +import acts.examples + + +def test_navigation_policy_factory(): + + policy = ( + acts.NavigationPolicyFactory.make() + .add(acts.TryAllNavigationPolicy) + .add( + acts.SurfaceArrayNavigationPolicy, + acts.SurfaceArrayNavigationPolicy.Config( + layerType=acts.SurfaceArrayNavigationPolicy.LayerType.Disc, + bins=(10, 10), + ), + ) + ) + + policy._buildTest() + + policy = acts.NavigationPolicyFactory.make().add(acts.TryAllNavigationPolicy) + + policy._buildTest() + + +def test_navigation_policy_factory_build_empty(): + policy = acts.NavigationPolicyFactory.make() + + with pytest.raises(RuntimeError): + policy._buildTest() + + +def test_navigation_policy_factory_add_multiple(): + with pytest.raises(ValueError): + ( + acts.NavigationPolicyFactory.make() + .add(acts.TryAllNavigationPolicy) + .add(acts.TryAllNavigationPolicy) + ) diff --git a/Tests/UnitTests/Core/EventData/CMakeLists.txt b/Tests/UnitTests/Core/EventData/CMakeLists.txt index 61b5079da8e..1efda4cff29 100644 --- a/Tests/UnitTests/Core/EventData/CMakeLists.txt +++ b/Tests/UnitTests/Core/EventData/CMakeLists.txt @@ -16,5 +16,6 @@ add_unittest(MultiTrajectoryHelpers MultiTrajectoryHelpersTests.cpp) add_unittest(SubspaceHelpers SubspaceHelpersTests.cpp) add_unittest(SeedEdm SeedEdmTests.cpp) add_unittest(SpacePointContainerEdm SpacePointContainerEdmTests.cpp) +add_unittest(TrackParameterHelpers TrackParameterHelpersTests.cpp) add_non_compile_test(MultiTrajectory TrackContainerComplianceTests.cpp) diff --git a/Tests/UnitTests/Core/EventData/TrackParameterHelpersTests.cpp b/Tests/UnitTests/Core/EventData/TrackParameterHelpersTests.cpp new file mode 100644 index 00000000000..0a6f3064594 --- /dev/null +++ b/Tests/UnitTests/Core/EventData/TrackParameterHelpersTests.cpp @@ -0,0 +1,43 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 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 https://mozilla.org/MPL/2.0/. + +#include + +#include "Acts/Definitions/TrackParametrization.hpp" +#include "Acts/EventData/TrackParameterHelpers.hpp" +#include "Acts/Tests/CommonHelpers/FloatComparisons.hpp" + +BOOST_AUTO_TEST_SUITE(TrackParameterHelpers) + +BOOST_AUTO_TEST_CASE(normalizeBoundParameters) { + CHECK_CLOSE_OR_SMALL(Acts::normalizeBoundParameters({1, 2, 3, 4, 5, 6}), + Acts::BoundVector(1, 2, -0.141593, 2.28319, 5, 6), 1e-3, + 1e-3); +} + +BOOST_AUTO_TEST_CASE(addBoundParameters) { + CHECK_CLOSE_OR_SMALL( + Acts::addBoundParameters({1, 2, 3, 4, 5, 6}, {0, 0, 0, 0, 0, 0}), + Acts::normalizeBoundParameters({1, 2, 3, 4, 5, 6}), 1e-3, 1e-3); + CHECK_CLOSE_OR_SMALL( + Acts::addBoundParameters({1, 2, 3, 4, 5, 6}, {0, 0, 1, 1, 0, 0}), + Acts::normalizeBoundParameters({1, 2, 4, 5, 5, 6}), 1e-3, 1e-3); +} + +BOOST_AUTO_TEST_CASE(subtractBoundParameters) { + CHECK_CLOSE_OR_SMALL( + Acts::subtractBoundParameters({1, 2, 3, 4, 5, 6}, {1, 2, 3, 4, 5, 6}), + Acts::BoundVector(0, 0, 0, 0, 0, 0), 1e-3, 1e-3); + CHECK_CLOSE_OR_SMALL( + Acts::addBoundParameters( + Acts::subtractBoundParameters({1, 2, 3, 4, 5, 6}, {0, 0, 1, 1, 0, 0}), + {0, 0, 1, 1, 0, 0}), + Acts::normalizeBoundParameters({1, 2, 3, 4, 5, 6}), 1e-3, 1e-3); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/Tests/UnitTests/Core/Navigation/CMakeLists.txt b/Tests/UnitTests/Core/Navigation/CMakeLists.txt index edca953321d..a18c2d34c9c 100644 --- a/Tests/UnitTests/Core/Navigation/CMakeLists.txt +++ b/Tests/UnitTests/Core/Navigation/CMakeLists.txt @@ -5,3 +5,4 @@ add_unittest(NavigationStream NavigationStreamTests.cpp) add_unittest(NavigationStateUpdaters NavigationStateUpdatersTests.cpp) add_unittest(DetectorNavigator DetectorNavigatorTests.cpp) add_unittest(MultiWireNavigation MultiWireNavigationTests.cpp) +add_unittest(NavigationPolicy NavigationPolicyTests.cpp) diff --git a/Tests/UnitTests/Core/Navigation/NavigationPolicyTests.cpp b/Tests/UnitTests/Core/Navigation/NavigationPolicyTests.cpp new file mode 100644 index 00000000000..181e1ad2a1f --- /dev/null +++ b/Tests/UnitTests/Core/Navigation/NavigationPolicyTests.cpp @@ -0,0 +1,240 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 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 https://mozilla.org/MPL/2.0/. + +#include + +#include "Acts/Definitions/Units.hpp" +#include "Acts/Geometry/CylinderVolumeBounds.hpp" +#include "Acts/Geometry/NavigationPolicyFactory.hpp" +#include "Acts/Geometry/TrackingVolume.hpp" +#include "Acts/Navigation/INavigationPolicy.hpp" +#include "Acts/Navigation/MultiNavigationPolicy.hpp" +#include "Acts/Navigation/NavigationDelegate.hpp" +#include "Acts/Navigation/NavigationStream.hpp" +#include "Acts/Navigation/TryAllNavigationPolicy.hpp" + +using namespace Acts; +using namespace Acts::UnitLiterals; + +BOOST_AUTO_TEST_SUITE(NavigationPolicyTests) + +GeometryContext gctx; +auto logger = getDefaultLogger("NavigationPolicyTests", Logging::VERBOSE); + +struct APolicy : public INavigationPolicy { + APolicy(const GeometryContext& /*gctx*/, const TrackingVolume& /*volume*/, + const Logger& /*logger*/) {} + + void initializeCandidates(const NavigationArguments& /*unused*/, + AppendOnlyNavigationStream& /*unused*/, + const Logger& /*unused*/) const { + const_cast(this)->executed = true; + } + + void connect(NavigationDelegate& delegate) const override { + connectDefault(delegate); + } + + bool executed = false; +}; + +struct BPolicy : public INavigationPolicy { + struct Config { + int value; + }; + + BPolicy(const GeometryContext& /*gctx*/, const TrackingVolume& /*volume*/, + const Logger& /*logger*/, Config config) + : m_config(config) {} + + void connect(NavigationDelegate& delegate) const override { + connectDefault(delegate); + } + + void initializeCandidates(const NavigationArguments& /*unused*/, + AppendOnlyNavigationStream& /*unused*/, + const Logger& /*unused*/) const { + const_cast(this)->executed = true; + const_cast(this)->value = m_config.value; + } + + bool executed = false; + int value = 0; + + Config m_config; +}; + +BOOST_AUTO_TEST_CASE(DirectTest) { + TrackingVolume volume{ + Transform3::Identity(), + std::make_shared(250_mm, 400_mm, 310_mm), + "PixelLayer3"}; + + MultiNavigationPolicy policy{APolicy{gctx, volume, *logger}, + BPolicy{gctx, volume, *logger, {.value = 4242}}}; + + NavigationDelegate delegate; + policy.connect(delegate); + + NavigationStream main; + AppendOnlyNavigationStream stream{main}; + delegate(NavigationArguments{.position = Vector3::Zero(), + .direction = Vector3::Zero()}, + stream, *logger); + + BOOST_CHECK(std::get(policy.policies()).executed); + BOOST_CHECK(std::get(policy.policies()).executed); + BOOST_CHECK_EQUAL(std::get(policy.policies()).value, 4242); +} + +BOOST_AUTO_TEST_CASE(FactoryTest) { + TrackingVolume volume{ + Transform3::Identity(), + std::make_shared(250_mm, 400_mm, 310_mm), + "PixelLayer3"}; + + BPolicy::Config config{.value = 42}; + + std::function( + const GeometryContext&, const TrackingVolume&, const Logger&)> + factory = NavigationPolicyFactory::make() + .add() // no arguments + .add(config); // config struct as argument + + auto policyBase = factory(gctx, volume, *logger); + auto policyBase2 = factory(gctx, volume, *logger); + + auto& policy = + dynamic_cast&>(*policyBase); + + NavigationDelegate delegate; + policy.connect(delegate); + + NavigationStream main; + AppendOnlyNavigationStream stream{main}; + delegate(NavigationArguments{.position = Vector3::Zero(), + .direction = Vector3::Zero()}, + stream, *logger); + + BOOST_CHECK(std::get(policy.policies()).executed); + BOOST_CHECK(std::get(policy.policies()).executed); + BOOST_CHECK_EQUAL(std::get(policy.policies()).value, 42); + + auto& policy2 = + dynamic_cast&>(*policyBase2); + + NavigationDelegate delegate2; + policyBase2->connect(delegate2); + + delegate2(NavigationArguments{.position = Vector3::Zero(), + .direction = Vector3::Zero()}, + stream, *logger); + + BOOST_CHECK(std::get(policy2.policies()).executed); + BOOST_CHECK(std::get(policy2.policies()).executed); + BOOST_CHECK_EQUAL(std::get(policy2.policies()).value, 42); +} + +BOOST_AUTO_TEST_CASE(AsUniquePtrTest) { + TrackingVolume volume{ + Transform3::Identity(), + std::make_shared(250_mm, 400_mm, 310_mm), + "PixelLayer3"}; + + std::unique_ptr factory = + NavigationPolicyFactory::make().add().asUniquePtr(); + + auto policyBase = factory->build(gctx, volume, *logger); + auto& policy = dynamic_cast&>(*policyBase); + + NavigationDelegate delegate; + policyBase->connect(delegate); + + NavigationStream main; + AppendOnlyNavigationStream stream{main}; + delegate(NavigationArguments{.position = Vector3::Zero(), + .direction = Vector3::Zero()}, + stream, *logger); + + BOOST_CHECK(std::get(policy.policies()).executed); +} + +struct CPolicy : public INavigationPolicy {}; + +template +struct CPolicySpecialized : public CPolicy { + struct Config { + T value; + }; + + CPolicySpecialized(const TrackingVolume& /*volume*/, Config config) + : m_config(config) {} + + void connect(NavigationDelegate& delegate) const override { + connectDefault>(delegate); + } + + void initializeCandidates(const NavigationArguments& /*unused*/, + AppendOnlyNavigationStream& /*stream*/, + const Logger& /*logger*/) const { + auto* self = const_cast*>(this); + self->executed = true; + self->value = m_config.value; + } + + bool executed = false; + int value = 0; + + Config m_config; +}; + +struct IsolatedConfig { + int value; +}; + +auto makeCPolicy(const GeometryContext& /*gctx*/, const TrackingVolume& volume, + const Logger& /*logger*/, IsolatedConfig config) { + // I can do arbitrary stuff here + CPolicySpecialized::Config config2{.value = config.value}; + return CPolicySpecialized(volume, config2); +} + +BOOST_AUTO_TEST_CASE(IsolatedFactory) { + TrackingVolume volume{ + Transform3::Identity(), + std::make_shared(250_mm, 400_mm, 310_mm), + "PixelLayer3"}; + + IsolatedConfig config{.value = 44}; + auto factory = + NavigationPolicyFactory::make().add().add(makeCPolicy, config); + + auto factory2 = + NavigationPolicyFactory::make().add(makeCPolicy, config).add(); + + auto policyBase = factory(gctx, volume, *logger); + auto& policy = + dynamic_cast>&>( + *policyBase); + + NavigationDelegate delegate; + policyBase->connect(delegate); + + NavigationStream main; + AppendOnlyNavigationStream stream{main}; + delegate(NavigationArguments{.position = Vector3::Zero(), + .direction = Vector3::Zero()}, + stream, *logger); + + BOOST_CHECK(std::get(policy.policies()).executed); + BOOST_CHECK(std::get>(policy.policies()).executed); + BOOST_CHECK_EQUAL(std::get>(policy.policies()).value, + 44); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/Tests/UnitTests/Core/Navigation/NavigationStreamTests.cpp b/Tests/UnitTests/Core/Navigation/NavigationStreamTests.cpp index 097dde2ed64..9ca578b0aba 100644 --- a/Tests/UnitTests/Core/Navigation/NavigationStreamTests.cpp +++ b/Tests/UnitTests/Core/Navigation/NavigationStreamTests.cpp @@ -94,7 +94,7 @@ BOOST_AUTO_TEST_CASE(NavigationStream_InitializePlanes) { NavigationStream nStreamTemplate; for (const auto& surface : surfaces) { - nStreamTemplate.addSurfaceCandidate(surface.get(), + nStreamTemplate.addSurfaceCandidate(*surface, Acts::BoundaryTolerance::None()); } BOOST_CHECK_EQUAL(nStreamTemplate.remainingCandidates(), 4u); @@ -140,7 +140,7 @@ BOOST_AUTO_TEST_CASE(NavigationStream_InitializePlanes) { // (5) Test de-duplication nStream = nStreamTemplate; - nStreamTemplate.addSurfaceCandidate(surfaces[0].get(), + nStreamTemplate.addSurfaceCandidate(*surfaces.at(0), Acts::BoundaryTolerance::None()); // One surface is duplicated in the stream BOOST_CHECK_EQUAL(nStreamTemplate.remainingCandidates(), 5u); @@ -159,7 +159,7 @@ BOOST_AUTO_TEST_CASE(NavigationStream_UpdatePlanes) { // reachable and intersections inside bounds NavigationStream nStreamTemplate; for (const auto& surface : surfaces) { - nStreamTemplate.addSurfaceCandidate(surface.get(), + nStreamTemplate.addSurfaceCandidate(*surface, Acts::BoundaryTolerance::None()); } BOOST_CHECK_EQUAL(nStreamTemplate.remainingCandidates(), 4u); @@ -231,7 +231,8 @@ BOOST_AUTO_TEST_CASE(NavigationStream_InitializeCylinders) { // Let us fill the surfaces into the navigation stream NavigationStream nStreamTemplate; for (const auto& surface : surfaces) { - nStreamTemplate.addSurfaceCandidates({surface.get()}, + const Surface* pointer = surface.get(); + nStreamTemplate.addSurfaceCandidates({&pointer, 1}, Acts::BoundaryTolerance::None()); } BOOST_CHECK_EQUAL(nStreamTemplate.remainingCandidates(), 4u); diff --git a/Tests/UnitTests/Examples/EventData/MeasurementTests.cpp b/Tests/UnitTests/Examples/EventData/MeasurementTests.cpp index d59ff4a135e..2428aa2429f 100644 --- a/Tests/UnitTests/Examples/EventData/MeasurementTests.cpp +++ b/Tests/UnitTests/Examples/EventData/MeasurementTests.cpp @@ -85,7 +85,8 @@ BOOST_DATA_TEST_CASE(VariableBoundOneEmplace, bd::make(boundIndices), index) { BOOST_AUTO_TEST_CASE(VariableBoundAll) { MeasurementContainer container; - auto [params, cov] = generateBoundParametersCovariance(rng); + auto [params, cov] = + generateParametersCovariance(rng); FixedBoundMeasurementProxy meas = container.makeMeasurement(geoId); @@ -106,7 +107,8 @@ BOOST_AUTO_TEST_CASE(VariableBoundAll) { BOOST_AUTO_TEST_CASE(VariableBoundAllEmplace) { MeasurementContainer container; - auto [params, cov] = generateBoundParametersCovariance(rng); + auto [params, cov] = + generateParametersCovariance(rng); FixedBoundMeasurementProxy meas = container.emplaceMeasurement( @@ -144,7 +146,8 @@ BOOST_AUTO_TEST_CASE(VariableBoundReassign) { BOOST_CHECK(!meas.contains(eBoundQOverP)); // reassign w/ all parameters - auto [paramsN, covN] = generateBoundParametersCovariance(rng); + auto [paramsN, covN] = + generateParametersCovariance(rng); meas = container.makeMeasurement(eBoundSize, geoId); meas.setSubspaceIndices(std::array{eBoundLoc0, eBoundLoc1, eBoundTime, diff --git a/Tests/UnitTests/Examples/Io/Json/JsonDigitizationConfigTests.cpp b/Tests/UnitTests/Examples/Io/Json/JsonDigitizationConfigTests.cpp index ab30e2a98f0..f7c1db0f496 100644 --- a/Tests/UnitTests/Examples/Io/Json/JsonDigitizationConfigTests.cpp +++ b/Tests/UnitTests/Examples/Io/Json/JsonDigitizationConfigTests.cpp @@ -57,7 +57,7 @@ struct Fixture { // generate random track parameters auto [par, cov] = - Acts::detail::Test::generateBoundParametersCovariance(rng); + Acts::detail::Test::generateBoundParametersCovariance(rng, {}); boundParams = par; freeParams = diff --git a/Tests/UnitTests/Fatras/Digitization/UncorrelatedHitSmearerTests.cpp b/Tests/UnitTests/Fatras/Digitization/UncorrelatedHitSmearerTests.cpp index e4fde7f3b2d..2a4ad0b2a6e 100644 --- a/Tests/UnitTests/Fatras/Digitization/UncorrelatedHitSmearerTests.cpp +++ b/Tests/UnitTests/Fatras/Digitization/UncorrelatedHitSmearerTests.cpp @@ -96,7 +96,7 @@ struct Fixture { // generate random track parameters auto [par, cov] = - Acts::detail::Test::generateBoundParametersCovariance(rng); + Acts::detail::Test::generateBoundParametersCovariance(rng, {}); boundParams = par; freeParams = diff --git a/cmake/ActsConfig.cmake.in b/cmake/ActsConfig.cmake.in index 7ed4b3884a5..97d510f1319 100644 --- a/cmake/ActsConfig.cmake.in +++ b/cmake/ActsConfig.cmake.in @@ -52,9 +52,7 @@ list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/Modules) # `find_dependency` is a wrapper around `find_package` that automatically # handles QUIET and REQUIRED parameters. include(CMakeFindDependencyMacro) -if(@ACTS_USE_SYSTEM_BOOST@) - find_dependency(Boost @Boost_VERSION_STRING@ CONFIG EXACT) -endif() +find_dependency(Boost @Boost_VERSION_STRING@ CONFIG EXACT) if(@ACTS_USE_SYSTEM_EIGEN3@) find_dependency(Eigen3 @Eigen3_VERSION@ CONFIG EXACT) endif() @@ -86,10 +84,6 @@ endif() # dependencies that we have built ourselves but cannot be # straightforwardly handed to cmake -if(NOT @ACTS_USE_SYSTEM_BOOST@) - add_library(Boost::boost INTERFACE IMPORTED GLOBAL) - target_include_directories(Boost::boost INTERFACE "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}") -endif() if(NOT @ACTS_USE_SYSTEM_EIGEN3@) add_library(Eigen3::Eigen INTERFACE IMPORTED GLOBAL) target_include_directories(Eigen3::Eigen INTERFACE "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}") diff --git a/thirdparty/README.md b/thirdparty/README.md index bd271826e1c..243479db0aa 100644 --- a/thirdparty/README.md +++ b/thirdparty/README.md @@ -17,9 +17,7 @@ CMake instructions to build [nlohmann::json](https://github.com/nlohmann/json). ## boost For convenience, it's possible to use the ACTS build system to build the minimum -required version of [boost](https://www.boost.org/) (currently 1.71.0). No source is -bundled here, and if requested via "-DACTS_USE_SYSTEM_BOOST=OFF", only the filesystem, -program_options, and test libraries will be built. +required version of [boost](https://www.boost.org/) (currently 1.71.0). Warning: during installation, the built boost libraries will be installed alongside the ACTS libraries, with a version suffix. This location may be known to the system linker.