Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(nav): Gen3 Navigation policies and factory #3760

Merged
merged 26 commits into from
Oct 24, 2024
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
6dc1e8f
refactor: Initial Gen3 portal support for `NavigationStream`
paulgessinger Sep 20, 2024
c437c73
feat(geo): Initial draft for Gen3 navigation delegate
paulgessinger Sep 20, 2024
05b16ec
refactor(geo): NavigationDelegate -> NavigationPolicy
paulgessinger Sep 26, 2024
93303f5
refactor(geo): NavigationArguments, policy / multipolicy, interface u…
paulgessinger Sep 26, 2024
6457fa6
feat(nav): Navigation policy factory, split up headers
paulgessinger Sep 30, 2024
6709564
refactor: NavPolicy factory works with isolated factory
paulgessinger Oct 11, 2024
f113ce8
refactor(nav): NavigationPolicies get geo context + logger in constru…
paulgessinger Oct 12, 2024
646f5f8
refactor: NavPolFactory captures all arguments by value
paulgessinger Oct 12, 2024
9e21a66
blueprint: Add SurfaceArrayNavPol stub
paulgessinger Oct 12, 2024
c0f417e
feat: Python bindings for nav policy factory
paulgessinger Oct 15, 2024
1839c8d
test: Add SrfArrNavPol to python factory api test
paulgessinger Oct 15, 2024
ecf1fe1
refactor: Add logger to `NavigationArguments`
paulgessinger Oct 14, 2024
e4b0e7c
refactor: SurfaceArrayNavigationPolicy basic working version
paulgessinger Oct 14, 2024
0bd237b
fix: GCC compilation for policy factory
paulgessinger Oct 18, 2024
fb74c4b
fix after logger change
paulgessinger Oct 16, 2024
2e9d93b
add doc comments
paulgessinger Oct 18, 2024
fe3ece5
make `add` functions only work on rvalues
paulgessinger Oct 18, 2024
3c9b37d
Add doc domments to factory
paulgessinger Oct 18, 2024
bbdc5b0
updates after PR feedback
paulgessinger Oct 22, 2024
e87497f
fix unit test
paulgessinger Oct 23, 2024
c859c95
fix python bindings issues + logger
paulgessinger Oct 23, 2024
fdf63f9
fix python test
paulgessinger Oct 23, 2024
5946ab8
fix clang-tidy + docs
paulgessinger Oct 23, 2024
b8d52c4
another doc comment fix
paulgessinger Oct 23, 2024
74b1f91
Merge branch 'main' into feat/navigation-policy
paulgessinger Oct 24, 2024
16602ad
sonar fix
paulgessinger Oct 24, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
220 changes: 220 additions & 0 deletions Core/include/Acts/Geometry/NavigationPolicyFactory.hpp
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();
andiwand marked this conversation as resolved.
Show resolved Hide resolved

// 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
32 changes: 25 additions & 7 deletions Core/include/Acts/Geometry/TrackingVolume.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -35,22 +35,22 @@
#include <utility>
#include <vector>

#include <boost/container/small_vector.hpp>
#include <boost/container/container_fwd.hpp>

namespace Acts {

class GlueVolumesDescriptor;
class VolumeBounds;
template <typename object_t>
struct NavigationOptions;
class GeometryIdentifier;
class IMaterialDecorator;
class ISurfaceMaterial;
class IVolumeMaterial;
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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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<INavigationPolicy> 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);
Expand Down Expand Up @@ -561,6 +575,10 @@ class TrackingVolume : public Volume {
std::vector<std::unique_ptr<TrackingVolume>> m_volumes;
std::vector<std::shared_ptr<Portal>> m_portals;
std::vector<std::shared_ptr<Surface>> m_surfaces;

std::unique_ptr<INavigationPolicy> m_navigationPolicy;

NavigationDelegate m_navigationDelegate{};
};

} // namespace Acts
73 changes: 73 additions & 0 deletions Core/include/Acts/Navigation/INavigationPolicy.hpp
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
Loading
Loading