Skip to content

Commit

Permalink
feat: Gen3 geometry Portals (#3501)
Browse files Browse the repository at this point in the history
Part of #3502

This PR implements *Portals*. A portal connects two or more neighboring volumes. Each volume has a set of portals that describes which volumes lie behind the portal in that direction. Portals use associated portal links to perform lookups of target volumes. Each portal has two links, and a corresponding surface. One link is associated with the direction along the surface's normal vector, and one with the opposite direction.

Portals can be **fused** and **merged**. 

**Fusing** is the combination of two portal linkson the same logical surfaces. The actual surface instances can be different, as long as they are geometrically equivalent (within numerical precistion). The resulting portal will have one portal along the shared surface's normal vector, and one opposite that vector.

```
   portal1   portal2
     +---+   +---+
     |   |   |   |
     |   |   |   |
<----+   | + |   +---->
     |   |   |   |
     |   |   |   |
     +---+   +---+
```

The input portals need to have compatible link loadout, e.g. one portal needs to have the *along normal* slot filled, and the other one needs to have the *opposite normal* slot filled. If
portals share a filled slot, the function throws an exception.

**Merging** is the complementary operation to the fusing of portals. To be able to merge portals, the surfaces of their associated links need to be *mergeable*, and the portal links need to be compatible. This means that both portals need to have a link along the portal surface normal, opposite the normal, or both. If the equipped links are opposite relative to one another (e.g. one along one opposite), the function will throw an exception.

```
           ^                     ^
           |                     |
    portal1|              portal2|
   +-------+-------+     +-------+-------+
   |               |  +  |               |
   +-------+-------+     +-------+-------+
           |                     |
           |                     |
           v                     v
```

This is a destructive operation on both portals, their links will be moved to produce merged links, which can fail if the portal links are not compatible
  • Loading branch information
paulgessinger authored Aug 29, 2024
1 parent d6b112d commit 4569230
Show file tree
Hide file tree
Showing 7 changed files with 1,173 additions and 1 deletion.
2 changes: 2 additions & 0 deletions Core/include/Acts/Definitions/Algebra.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,4 +100,6 @@ using AngleAxis3 = Eigen::AngleAxis<ActsScalar>;
using Transform2 = Eigen::Transform<ActsScalar, 2, Eigen::AffineCompact>;
using Transform3 = Eigen::Transform<ActsScalar, 3, Eigen::Affine>;

constexpr ActsScalar s_transformEquivalentTolerance = 1e-9;

} // namespace Acts
234 changes: 234 additions & 0 deletions Core/include/Acts/Geometry/Portal.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
// This file is part of the Acts project.
//
// Copyright (C) 2024 CERN for the benefit of the Acts project
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#pragma once

#include "Acts/Definitions/Algebra.hpp"
#include "Acts/Definitions/Direction.hpp"
#include "Acts/Utilities/BinningType.hpp"
#include "Acts/Utilities/Logger.hpp"
#include "Acts/Utilities/Result.hpp"

#include <memory>

namespace Acts {

class RegularSurface;
class GeometryContext;
class TrackingVolume;
class CylinderSurface;
class PlaneSurface;
class DiscSurface;
class Surface;

class PortalLinkBase;

/// Exception thrown when portals cannot be merged
class PortalMergingException : public std::exception {
const char* what() const noexcept override;
};

/// Exception thrown when portals cannot be fused
class PortalFusingException : public std::exception {
const char* what() const noexcept override;
};

/// A portal connects two or more neighboring volumes. Each volume has a set of
/// portals that describes which volumes lie behind the portal in that
/// direction. Portals use associated portal links to perform lookups of target
/// volumes.
/// Each portal has two links, and a corresponding surface. One link is
/// associated with the direction along the surface's normal vector, and one
/// with the opposite direction.
class Portal {
public:
/// Constructor for a portal from a single link
/// @param direction The direction of the link
/// @param link The portal link
Portal(Direction direction, std::unique_ptr<PortalLinkBase> link);

/// Constructor for a portal from a surface and volume, where a trivial portal
/// link is automatically constructed.
/// @param direction The direction of the link
/// @param surface The surface from which to create the portal link
/// @param volume The volume this portal connects to in the @p direction
/// relative to the normal of @p surface.
Portal(Direction direction, std::shared_ptr<RegularSurface> surface,
TrackingVolume& volume);

/// Constructor for a portal from two links. One of the links can be
/// `nullptr`, but at least one of them needs to be set. If both are set, they
/// need to be valid compatible links that can be fused.
/// @param gctx The geometry context
/// @param alongNormal The link along the normal of the surface
/// @param oppositeNormal The link opposite to the normal of the
Portal(const GeometryContext& gctx,
std::unique_ptr<PortalLinkBase> alongNormal,
std::unique_ptr<PortalLinkBase> oppositeNormal);

/// Helper struct for the arguments to the portal constructor below using
/// designated initializers.
struct Arguments {
/// Aggregate over a surface and a volume with optional semantics
struct Link {
Link() = default;
/// Constructor from a surface and a volume
Link(std::shared_ptr<RegularSurface> surfaceIn, TrackingVolume& volumeIn)
: surface(std::move(surfaceIn)), volume(&volumeIn) {}

/// The associated surface
std::shared_ptr<RegularSurface> surface = nullptr;
/// The associated volume
TrackingVolume* volume = nullptr;
};

/// Entry for the link along normal
/// Entry for the link opposite normal
Link alongNormal{};
Link oppositeNormal{};
};

/// Constructor that takes a geometry context and an rvalue reference to a
/// helper struct from above. This pattern allows you to use designated
/// initializers to construct this object like:
/// ```cpp
/// Portal{gctx, {.oppositeNormal = {cyl1, *vol1}}};
/// Portal{gctx, {.alongNormal = {cyl2, *vol2}}};
/// ```
/// @param gctx The geometry context
/// @param args The struct containing the arguments
Portal(const GeometryContext& gctx, Arguments&& args);

/// Fuse two portals together. Fusing is the combination of two portal links
/// on the same logical surfaces. The actual surface instances can be
/// different, as long as they are geometrically equivalent (within numerical
/// precision). The resulting portal will have one portal along the shared
/// surface's normal vector, and one opposite that vector.
///
/// portal1 portal2
/// +---+ +---+
/// | | | |
/// | | | |
/// <----+ | + | +---->
/// | | | |
/// | | | |
/// +---+ +---+
///
/// @note The input portals need to have compatible link loadaout, e.g. one
/// portal needs to have the *along normal* slot filled, and the
/// otherone one needs to have the *opposite normal* slot filled. If
/// portals share a filled slot, the function throws an exception.
/// @note This is a destructive operation on the portals involved
/// @param gctx The geometry context
/// @param aPortal The first portal
/// @param bPortal The second portal
/// @param logger The logger to push output to
static Portal fuse(const GeometryContext& gctx, Portal& aPortal,
Portal& bPortal, const Logger& logger = getDummyLogger());

/// Merge two adjacent portals with each other to produce a new portal that
/// encompasses both inputs. It is the complementary operation to the fusing
/// of portals. To be able to merge portals, the surfaces of their associated
/// links need to be *mergeable*, and the portal links need to be compatible.
/// This means that both portals need to have a link along the portal surface
/// normal, opposite the normal, or both. If the equipped links are opposite
/// relative to one another (e.g. one along one opposite), the function will
/// throw an exception.
///
/// ^ ^
/// | |
/// portal1| portal2|
/// +-------+-------+ +-------+-------+
/// | | + | |
/// +-------+-------+ +-------+-------+
/// | |
/// | |
/// v v
///
/// @note This is a destructive operation on both portals, their
/// links will be moved to produce merged links, which can fail
/// if the portal links are not compatible
/// @param gctx The geometry context
/// @param aPortal The first portal
/// @param bPortal The second portal
/// @param direction The direction of the merge (e.g. along z)
/// @param logger The logger to push output to
static Portal merge(const GeometryContext& gctx, Portal& aPortal,
Portal& bPortal, BinningValue direction,
const Logger& logger = getDummyLogger());

/// Resolve the volume for a 3D position and a direction
/// The @p direction is used to select the right portal link, if it is set.
/// In case no link is found in the specified direction, a `nullptr` is
/// returned.
/// @param gctx The geometry context
/// @param position The 3D position
/// @param direction The direction
/// @return The target volume (can be `nullptr`)
Result<const TrackingVolume*> resolveVolume(const GeometryContext& gctx,
const Vector3& position,
const Vector3& direction) const;

/// Set a link on the portal into the slot specified by the direction.
/// @note The surface associated with @p link must be logically equivalent
/// to the one of the link that's already set on the portal.
/// @param gctx The geometry context
/// @param direction The direction
/// @param link The link to set
void setLink(const GeometryContext& gctx, Direction direction,
std::unique_ptr<PortalLinkBase> link);

/// Helper function create a trivial portal link based on a surface.
/// @param gctx The geometry context
/// @param direction The direction of the link to create
/// @param surface The surface
/// @note The @p surface must be logically equivalent
/// to the one of the link that's already set on the portal.
/// @param volume The target volume
void setLink(const GeometryContext& gctx, Direction direction,
std::shared_ptr<RegularSurface> surface, TrackingVolume& volume);

/// Get the link associated with the @p direction. Can be null if the associated link is unset.
/// @param direction The direction
/// @return The link (can be null)
const PortalLinkBase* getLink(Direction direction) const;

/// Returns true if the portal is valid, that means it has at least one
/// non-null link associated.Portals can be in an invalid state after they get
/// merged or fused with other portals.
/// @return True if the portal is valid
bool isValid() const;

/// Create and attach a trivial portal link to the empty slot of this portal
/// @param volume The target volume to connect to
void fill(TrackingVolume& volume);

/// Access the portal surface that is shared between the two links
/// @return The portal surface
const RegularSurface& surface() const;

private:
/// Helper to check surface equivalence without checking material status. This
/// is needed because we allow fusing portals with surfaces that are
/// equivalent but one of them has material while the other does not. The
/// normal surface comparison would determine these surfaces as not
/// equivalent.
/// @param gctx The geometry context
/// @param a The first surface
/// @param b The second surface
/// @return True if the surfaces are equivalent
static bool isSameSurface(const GeometryContext& gctx, const Surface& a,
const Surface& b);

std::shared_ptr<RegularSurface> m_surface;

std::unique_ptr<PortalLinkBase> m_alongNormal;
std::unique_ptr<PortalLinkBase> m_oppositeNormal;
};

} // namespace Acts
1 change: 1 addition & 0 deletions Core/src/Geometry/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ target_sources(
Volume.cpp
VolumeBounds.cpp
CylinderVolumeStack.cpp
Portal.cpp
GridPortalLink.cpp
GridPortalLinkMerging.cpp
TrivialPortalLink.cpp
Expand Down
Loading

0 comments on commit 4569230

Please sign in to comment.