Skip to content

Commit

Permalink
feat: Adding cuboid volume stack (#4040)
Browse files Browse the repository at this point in the history
Adding `CuboidVolumeStack`. The overall logic is similar to `CylinderVolumeStack` -- a set of aligned volumes is formed into a stack, the stack volumes are adjusted in size and the connection between the volumes is insured via resizing/construction of the gap volumes. 

The main difference with `CylinderVolumeStack` is in the fact that cuboid volumes don't really have a "special" direction that can fix the stacking conventions, e.g. specific treatment of `AxisZ` and `AxisR` stacking. For this reason a level of generalization is applied to the stacking direction handling, including handling of arbitrary (in global coordinate system) merging directions.      


<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

## Release Notes

- **New Features**
	- Introduced `CuboidVolumeStack` class for managing stacks of cuboid volumes.
	- Added `VolumeAttachmentStrategy` and `VolumeResizeStrategy` for flexible volume management.
	- Enhanced `CuboidVolumeBounds` with a new constructor for flexible initialization.

- **Improvements**
	- Generalized strategy handling across various volume types.
	- Improved error checking and initialization for volume bounds.
	- Expanded geometry module with new strategy classes.

- **Testing**
	- Added comprehensive unit tests for new volume management features.
	- Enhanced test coverage for volume initialization and error handling.

- **Documentation**
	- Updated documentation generation to support new classes and strategies.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
  • Loading branch information
ssdetlab authored Jan 24, 2025
1 parent 429e34d commit 9f58ce1
Show file tree
Hide file tree
Showing 22 changed files with 2,540 additions and 240 deletions.
9 changes: 9 additions & 0 deletions Core/include/Acts/Geometry/CuboidVolumeBounds.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

#include <array>
#include <cmath>
#include <cstddef>
#include <iomanip>
#include <iosfwd>
#include <memory>
Expand Down Expand Up @@ -71,6 +72,9 @@ class CuboidVolumeBounds : public VolumeBounds {
/// @param values iw the bound values
CuboidVolumeBounds(const std::array<double, eSize>& values);

CuboidVolumeBounds(
std::initializer_list<std::pair<BoundValues, double>> keyValues);

/// Copy Constructor
///
/// @param bobo is the source volume bounds to be copied
Expand Down Expand Up @@ -149,6 +153,11 @@ class CuboidVolumeBounds : public VolumeBounds {
/// @param keyValues the initializer list of key value pairs
void set(std::initializer_list<std::pair<BoundValues, double>> keyValues);

/// Convert axis direction to a corresponding bound value
/// in local coordinate convention
/// @param direction the axis direction to convert
static BoundValues fromAxisDirection(AxisDirection direction);

/// Output Method for std::ostream
///
/// @param os is ostream operator to be dumped into
Expand Down
187 changes: 187 additions & 0 deletions Core/include/Acts/Geometry/CuboidVolumeStack.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
// 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/Geometry/Volume.hpp"
#include "Acts/Geometry/VolumeAttachmentStrategy.hpp"
#include "Acts/Geometry/VolumeResizeStrategy.hpp"
#include "Acts/Utilities/AxisDefinitions.hpp"
#include "Acts/Utilities/Logger.hpp"

#include <cstddef>
#include <vector>

namespace Acts {

/// @class CuboidVolumeStack
/// This class implements a x-. y-. z-aligned stack
/// of cuboid volumes with synchronized bounds.
/// Externally, it presents as a single volume.
/// On construction, the input volumes are modified so that
/// they are connected in x, y, z and have synchronized bounds.
/// The way this is done can be configured using an *attachment*
/// and a *resize* strategy. Depending on the configuration,
/// the input volumes are either extended or gap volumes are created.
///
/// @note The size adjustment convention is that volumes are never shrunk
class CuboidVolumeStack : public Volume {
public:
/// Constructor from a vector of volumes and direction
/// @param volumes is the vector of volumes
/// @param direction is the axis direction
/// @param strategy is the attachment strategy
/// @param resizeStrategy is the resize strategy
/// @note @p resizeStrategy only affects resizing along
/// @p direction. Resizing in the other direction
/// is always delegated to the child volumes,
/// which might in turn be @c CuboidVolumeStack
/// @param logger is the logger
/// @pre The volumes need to have a common coordinate
/// system relative to @p direction. I.e. they need
/// to be aligned in @c z and cannot have a rotation
/// in @c x or @c y.
/// @pre The volumes all need to have @c CuboidVolumeBounds
/// @note Preconditions are checked on construction
CuboidVolumeStack(
std::vector<Volume*>& volumes, AxisDirection direction,
VolumeAttachmentStrategy strategy = VolumeAttachmentStrategy::Midpoint,
VolumeResizeStrategy resizeStrategy = VolumeResizeStrategy::Expand,
const Logger& logger = Acts::getDummyLogger());

/// Constructor from a vector of volumes and direction
/// @param volumes is the vector of volumes
/// @param direction is the vector specifying the global direction
/// @param strategy is the attachment strategy
/// @param resizeStrategy is the resize strategy
/// @note @p resizeStrategy only affects resizing along
/// @p direction. Resizing in the other direction
/// is always delegated to the child volumes,
/// which might in turn be @c CuboidVolumeStack
/// @param logger is the logger
/// @pre The volumes need to have a common coordinate
/// system relative to @p direction. I.e. they need
/// to be aligned in @c z and cannot have a rotation
/// in @c x or @c y.
/// @pre The volumes all need to have @c CuboidVolumeBounds
/// @note Preconditions are checked on construction
CuboidVolumeStack(
std::vector<Volume*>& volumes, const Vector3& direction,
VolumeAttachmentStrategy strategy = VolumeAttachmentStrategy::Midpoint,
VolumeResizeStrategy resizeStrategy = VolumeResizeStrategy::Expand,
const Logger& logger = Acts::getDummyLogger());

/// Update the volume bounds and transform. This
/// will update the bounds of all volumes in the stack
/// to accommodate the new bounds and optionally create
/// gap volumes according to the resize strategy set during
/// construction.
/// @param volbounds is the new bounds
/// @param transform is the new transform
/// @param logger is the logger
/// @pre The volume bounds need to be of type
/// @c CuboidVolumeBounds.
void update(std::shared_ptr<VolumeBounds> volbounds,
std::optional<Transform3> transform = std::nullopt,
const Logger& logger = getDummyLogger()) override;

/// Access the gap volume that were created during attachment or resizing.
/// @return the vector of gap volumes
const std::vector<std::shared_ptr<Volume>>& gaps() const;

/// Convert axis direction to an array index according to
/// stack convention. For example, AxisX --> 0
/// @param direction is the axis direction to convert
static std::size_t axisToIndex(AxisDirection direction);

/// Get axis directions orthogonal to the given one according
/// to stack convention. For example AxisX --> <AxisY, AxisZ>
/// @param direction is the axis direction to find the orthogonal for
static std::pair<AxisDirection, AxisDirection> getOrthogonalAxes(
AxisDirection direction);

private:
/// Helper to get the first volume in the input, and throw an exception if
/// there is not one.
/// @param volumes is the vector of volumes
/// @return the first volume
static Volume& initialVolume(const std::vector<Volume*>& volumes);

/// Helper function called during construction that performs the
/// internal attachment and produces the overall outer volume bounds.
/// @param strategy is the attachment strategy
/// @param logger is the logger
void initializeOuterVolume(VolumeAttachmentStrategy strategy,
const Logger& logger);

struct VolumeTuple;

/// Helper function to pretty print the internal volume representation
/// @param volumes is the vector of volumes
/// @param logger is the logger
/// @param lvl is the logging level
static void printVolumeSequence(const std::vector<VolumeTuple>& volumes,
const Logger& logger,
Acts::Logging::Level lvl);

/// Helper function that prints output helping in debugging overlaps
/// @param a is the first volume
/// @param b is the second volume
/// @param logger is the logger
void overlapPrint(const VolumeTuple& a, const VolumeTuple& b,
const Logger& logger);

/// Helper function that checks if volumes are properly aligned
/// for attachment.
/// @param volumes is the vector of volumes
/// @param logger is the logger
void checkVolumeAlignment(const std::vector<VolumeTuple>& volumes,
const Logger& logger) const;

/// Helper function that checks overlaps and attaches along the stacking
/// direction
/// @param volumes is the vector of volumes
/// @param strategy is the attachment strategy
/// @param logger is the logger
/// @return vector of gap volumes. Can be empty if none were created.
std::vector<VolumeTuple> checkOverlapAndAttach(
std::vector<VolumeTuple>& volumes, VolumeAttachmentStrategy strategy,
const Logger& logger);

/// Helper function to synchronize the bounds of the volumes
/// @param volumes is the vector of volumes
/// @param logger is the logger
/// @return tuple of the minimum and maximum radii
std::pair<double, double> synchronizeBounds(std::vector<VolumeTuple>& volumes,
const Logger& logger);

/// Helper function to create a gap volume with given bounds and register it.
/// @param transform is the transform of the gap volume
/// @param bounds is the bounds of the gap volume
/// @return the shared pointer to the gap volume
std::shared_ptr<Volume> addGapVolume(
const Transform3& transform, const std::shared_ptr<VolumeBounds>& bounds);

/// Merging direction of the stack
/// in local group coordinates
AxisDirection m_dir{};

/// Directions orthogonal to the
/// merging direction of the stack
/// in local group coordinates
AxisDirection m_dirOrth1{};
AxisDirection m_dirOrth2{};

VolumeResizeStrategy m_resizeStrategy{};
Transform3 m_groupTransform{};
std::vector<std::shared_ptr<Volume>> m_gaps{};
std::vector<Volume*>& m_volumes;
};

} // namespace Acts
24 changes: 12 additions & 12 deletions Core/include/Acts/Geometry/CylinderContainerBlueprintNode.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
#include "Acts/Geometry/BlueprintNode.hpp"
#include "Acts/Geometry/CylinderVolumeStack.hpp"
#include "Acts/Geometry/PortalShell.hpp"
#include "Acts/Geometry/VolumeAttachmentStrategy.hpp"
#include "Acts/Geometry/VolumeResizeStrategy.hpp"
#include "Acts/Utilities/AxisDefinitions.hpp"
#include "Acts/Utilities/Logger.hpp"

Expand All @@ -37,10 +39,9 @@ class CylinderContainerBlueprintNode final : public BlueprintNode {
/// see documentation of that class for more information
CylinderContainerBlueprintNode(
const std::string& name, AxisDirection direction,
CylinderVolumeStack::AttachmentStrategy attachmentStrategy =
CylinderVolumeStack::AttachmentStrategy::Midpoint,
CylinderVolumeStack::ResizeStrategy resizeStrategy =
CylinderVolumeStack::ResizeStrategy::Expand);
VolumeAttachmentStrategy attachmentStrategy =
VolumeAttachmentStrategy::Midpoint,
VolumeResizeStrategy resizeStrategy = VolumeResizeStrategy::Expand);

/// @copydoc BlueprintNode::name
const std::string& name() const override;
Expand Down Expand Up @@ -101,25 +102,25 @@ class CylinderContainerBlueprintNode final : public BlueprintNode {
/// @param attachmentStrategy The attachment strategy
/// @return This node for chaining
CylinderContainerBlueprintNode& setAttachmentStrategy(
CylinderVolumeStack::AttachmentStrategy attachmentStrategy);
VolumeAttachmentStrategy attachmentStrategy);

/// Setter for the resize strategy
/// @param resizeStrategy The resize strategy
/// @return This node for chaining
CylinderContainerBlueprintNode& setResizeStrategy(
CylinderVolumeStack::ResizeStrategy resizeStrategy);
VolumeResizeStrategy resizeStrategy);

/// Accessor to the stacking direction
/// @return The stacking direction
AxisDirection direction() const;

/// Accessor to the attachment strategy
/// @return The attachment strategy
CylinderVolumeStack::AttachmentStrategy attachmentStrategy() const;
VolumeAttachmentStrategy attachmentStrategy() const;

/// Accessor to the resize strategy
/// @return The resize strategy
CylinderVolumeStack::ResizeStrategy resizeStrategy() const;
VolumeResizeStrategy resizeStrategy() const;

private:
/// @copydoc BlueprintNode::addToGraphviz
Expand All @@ -138,11 +139,10 @@ class CylinderContainerBlueprintNode final : public BlueprintNode {

AxisDirection m_direction = AxisDirection::AxisZ;

CylinderVolumeStack::AttachmentStrategy m_attachmentStrategy{
CylinderVolumeStack::AttachmentStrategy::Midpoint};
VolumeAttachmentStrategy m_attachmentStrategy{
VolumeAttachmentStrategy::Midpoint};

CylinderVolumeStack::ResizeStrategy m_resizeStrategy{
CylinderVolumeStack::ResizeStrategy::Expand};
VolumeResizeStrategy m_resizeStrategy{VolumeResizeStrategy::Expand};

// Is only initialized during `build`
std::vector<Volume*> m_childVolumes;
Expand Down
52 changes: 9 additions & 43 deletions Core/include/Acts/Geometry/CylinderVolumeStack.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
#include "Acts/Definitions/Algebra.hpp"
#include "Acts/Geometry/CylinderVolumeBounds.hpp"
#include "Acts/Geometry/Volume.hpp"
#include "Acts/Geometry/VolumeAttachmentStrategy.hpp"
#include "Acts/Geometry/VolumeResizeStrategy.hpp"
#include "Acts/Utilities/AxisDefinitions.hpp"
#include "Acts/Utilities/Logger.hpp"

Expand All @@ -32,29 +34,6 @@ namespace Acts {
/// result in overlaps of the resulting volumes bounds.
class CylinderVolumeStack : public Volume {
public:
/// The attachment strategy defines how the volumes are attached
/// Attachment always happens pair-wise
enum class AttachmentStrategy {
/// Given two volumes, the *left* one, i.e. the one with the lower **local**
/// z or r value is extended
First,
/// Given two volumes, the *right* one, i.e. the one with the higher
/// **local** z or r value is extended
Second,
/// Given two volumes, the *midpoint* between the two volumes is found
Midpoint,
/// A gap volume is created to fit between the two volumes
Gap,
};

/// The resize strategy defines how the volumes are resized
enum class ResizeStrategy {
/// Extend the volume connected to the respective edge to fit the new bounds
Expand,
/// Create a gap volume at the respective edge to fit the new bounds
Gap,
};

/// Constructor from a vector of volumes and direction
/// @param volumes is the vector of volumes
/// @param direction is the binning direction
Expand All @@ -74,8 +53,8 @@ class CylinderVolumeStack : public Volume {
/// @note Preconditions are checked on construction
CylinderVolumeStack(
std::vector<Volume*>& volumes, AxisDirection direction,
AttachmentStrategy strategy = AttachmentStrategy::Midpoint,
ResizeStrategy resizeStrategy = ResizeStrategy::Expand,
VolumeAttachmentStrategy strategy = VolumeAttachmentStrategy::Midpoint,
VolumeResizeStrategy resizeStrategy = VolumeResizeStrategy::Expand,
const Logger& logger = Acts::getDummyLogger());

/// Update the volume bounds and transform. This
Expand Down Expand Up @@ -109,7 +88,8 @@ class CylinderVolumeStack : public Volume {
/// @param strategy is the attachment strategy
/// @param logger is the logger
void initializeOuterVolume(AxisDirection direction,
AttachmentStrategy strategy, const Logger& logger);
VolumeAttachmentStrategy strategy,
const Logger& logger);

struct VolumeTuple;

Expand Down Expand Up @@ -142,7 +122,7 @@ class CylinderVolumeStack : public Volume {
/// @param logger is the logger
/// @return vector of gap volumes. Can be empty if none were created.
std::vector<VolumeTuple> checkOverlapAndAttachInZ(
std::vector<VolumeTuple>& volumes, AttachmentStrategy strategy,
std::vector<VolumeTuple>& volumes, VolumeAttachmentStrategy strategy,
const Logger& logger);

/// Helper function to synchronize the r bounds of the volumes
Expand All @@ -158,7 +138,7 @@ class CylinderVolumeStack : public Volume {
/// @param logger is the logger
/// @return vector of gap volumes. Can be empty if none were created.
std::vector<VolumeTuple> checkOverlapAndAttachInR(
std::vector<VolumeTuple>& volumes, AttachmentStrategy strategy,
std::vector<VolumeTuple>& volumes, VolumeAttachmentStrategy strategy,
const Logger& logger);

/// Helper function to synchronize the z bounds of the volumes
Expand All @@ -183,24 +163,10 @@ class CylinderVolumeStack : public Volume {
const Transform3& transform, const std::shared_ptr<VolumeBounds>& bounds);

AxisDirection m_direction{};
ResizeStrategy m_resizeStrategy{};
VolumeResizeStrategy m_resizeStrategy{};
Transform3 m_groupTransform{};
std::vector<std::shared_ptr<Volume>> m_gaps{};
std::vector<Volume*>& m_volumes;
};

/// Output operator for the attachment strategy
/// @param os is the output stream
/// @param strategy is the attachment strategy
/// @return the output stream
std::ostream& operator<<(std::ostream& os,
CylinderVolumeStack::AttachmentStrategy strategy);

/// Output operator for the resize strategy
/// @param os is the output stream
/// @param strategy is the resize strategy
/// @return the output stream
std::ostream& operator<<(std::ostream& os,
CylinderVolumeStack::ResizeStrategy strategy);

} // namespace Acts
Loading

0 comments on commit 9f58ce1

Please sign in to comment.