-
Notifications
You must be signed in to change notification settings - Fork 173
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(nav): Gen3 Navigation policies and factory (#3760)
Terminology: - "Navigation delegate": the function that is registered with a tracking volume. In principle, this can be anything - "Navigation policy": the object that is registered with the tracking volume. It contains a method that is connected to the "navigation delegate". It has extra methods - "Navigation policy factory": To delay construction of the actual policy object **until after** the volume is fully sized and has all of its internal structure registered, the blueprint tree only contains *navigation policy factories*. This is configurable. During construction, the factory is applied to volumes, and produces a policy that is registered with the volume. This is called "navigation policy factory" from a conceptual point of view. - "MultiNavigationPolicy": chains together multiple policies in a sort of composition. You can have one policy that only deals with portals, one for sensitive and one for passive surfaces, for example. - To make this less annoying to construct, as you would have to manage the factory, I'm adding a concrete class `NavigationPolicyFactory`. Its job is to make defining a factory that produces a "MultiNavigationPolicy" easy, like: ```cpp using namespace Acts; using SurfaceArrayNavigationPolicy::LayerType; SurfaceArrayNavigationPolicy::Config config{ .layerType = LayerType::Cylinder, .bins = {10, 10} }; auto factory = NavigationPolicyFactory::make() .add<TryAllPortalNavigationPolicy>() .add<SurfaceArrayNavigationPolicy>(config); ``` or in python: ```python policy = ( acts.NavigationPolicyFactory.make() .add(acts.TryAllPortalNavigationPolicy) .add(acts.TryAllSurfaceNavigationPolicy) .add( acts.SurfaceArrayNavigationPolicy, acts.SurfaceArrayNavigationPolicy.Config( layerType=acts.SurfaceArrayNavigationPolicy.LayerType.Plane, bins=(10, 10), ), ) ) ``` Part of #3502
- Loading branch information
1 parent
6d982d0
commit c34520f
Showing
20 changed files
with
1,324 additions
and
55 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 <concepts> | ||
#include <memory> | ||
namespace Acts { | ||
|
||
class TrackingVolume; | ||
class GeometryContext; | ||
class Logger; | ||
class INavigationPolicy; | ||
|
||
namespace detail { | ||
template <typename... Factories> | ||
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<NavigationPolicy1>(arg1, arg2) | ||
/// .add<NavigationPolicy2>(/*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<INavigationPolicy> build( | ||
const GeometryContext& gctx, const TrackingVolume& volume, | ||
const Logger& logger) const = 0; | ||
}; | ||
|
||
namespace detail { | ||
|
||
template <typename F, typename... Args> | ||
concept NavigationPolicyIsolatedFactoryConcept = requires(F f) { | ||
{ | ||
f(std::declval<const GeometryContext&>(), | ||
std::declval<const TrackingVolume&>(), std::declval<const Logger&>(), | ||
std::declval<Args>()...) | ||
} -> std::derived_from<INavigationPolicy>; | ||
|
||
requires NavigationPolicyConcept<decltype(f( | ||
std::declval<const GeometryContext&>(), | ||
std::declval<const TrackingVolume&>(), std::declval<const Logger&>(), | ||
std::declval<Args>()...))>; | ||
|
||
requires(std::is_copy_constructible_v<Args> && ...); | ||
}; | ||
|
||
template <> | ||
class NavigationPolicyFactoryImpl<> { | ||
public: | ||
template <typename...> | ||
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 <NavigationPolicyConcept P, typename... Args> | ||
requires(std::is_constructible_v<P, const GeometryContext&, | ||
const TrackingVolume&, const Logger&, | ||
Args...> && | ||
(std::is_copy_constructible_v<Args> && ...)) | ||
constexpr auto add(Args&&... args) && { | ||
auto factory = [=](const GeometryContext& gctx, | ||
const TrackingVolume& volume, const Logger& logger) { | ||
return P{gctx, volume, logger, args...}; | ||
}; | ||
|
||
return NavigationPolicyFactoryImpl<decltype(factory)>{ | ||
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 <typename Fn, typename... Args> | ||
requires(NavigationPolicyIsolatedFactoryConcept<Fn, Args...>) | ||
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<decltype(factory)>{ | ||
std::make_tuple(std::move(factory))}; | ||
} | ||
}; | ||
|
||
template <typename F, typename... Fs> | ||
class NavigationPolicyFactoryImpl<F, Fs...> : 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 <NavigationPolicyConcept P, typename... Args> | ||
requires(std::is_constructible_v<P, const GeometryContext&, | ||
const TrackingVolume&, const Logger&, | ||
Args...> && | ||
(std::is_copy_constructible_v<Args> && ...)) | ||
constexpr auto add(Args&&... args) && { | ||
auto factory = [=](const GeometryContext& gctx, | ||
const TrackingVolume& volume, const Logger& logger) { | ||
return P{gctx, volume, logger, args...}; | ||
}; | ||
|
||
return NavigationPolicyFactoryImpl<F, Fs..., decltype(factory)>{ | ||
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 <typename Fn, typename... Args> | ||
requires(NavigationPolicyIsolatedFactoryConcept<Fn, Args...>) | ||
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<F, Fs..., decltype(factory)>{ | ||
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<NavigationPolicyFactoryImpl<F, Fs...>> | ||
asUniquePtr() && { | ||
return std::make_unique<NavigationPolicyFactoryImpl<F, Fs...>>( | ||
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<const GeometryContext&>(), | ||
std::declval<const TrackingVolume&>(), | ||
std::declval<const Logger&>())...}); | ||
|
||
// ... 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<policy_type>( | ||
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<INavigationPolicy> build( | ||
const GeometryContext& gctx, const TrackingVolume& volume, | ||
const Logger& logger) const override { | ||
return operator()(gctx, volume, logger); | ||
} | ||
|
||
private: | ||
template <typename...> | ||
friend class NavigationPolicyFactoryImpl; | ||
|
||
NavigationPolicyFactoryImpl(std::tuple<F, Fs...>&& factories) | ||
: m_factories(std::move(factories)) {} | ||
|
||
std::tuple<F, Fs...> m_factories; | ||
}; | ||
|
||
} // namespace detail | ||
|
||
inline auto NavigationPolicyFactory::make() { | ||
return detail::NavigationPolicyFactoryImpl<>{}; | ||
} | ||
|
||
} // namespace Acts |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 <type_traits> | ||
|
||
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 <typename T> | ||
concept NavigationPolicyConcept = requires { | ||
requires std::is_base_of_v<INavigationPolicy, T>; | ||
// Has a conforming update method | ||
requires requires(T policy, const NavigationArguments& args) { | ||
policy.initializeCandidates(args, | ||
std::declval<AppendOnlyNavigationStream&>(), | ||
std::declval<const Logger&>()); | ||
}; | ||
}; | ||
|
||
/// 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 <NavigationPolicyConcept T> | ||
void connectDefault(NavigationDelegate& delegate) const { | ||
// This cannot be a concept because we use it in CRTP below | ||
const auto* self = static_cast<const T*>(this); | ||
DelegateChainBuilder{delegate}.add<&T::initializeCandidates>(self).store( | ||
delegate); | ||
} | ||
}; | ||
|
||
} // namespace Acts |
Oops, something went wrong.