Skip to content

Commit

Permalink
feat: CylinderVolumeStack (acts-project#3065)
Browse files Browse the repository at this point in the history
This PR adds a `CylinderVolumeStack` that allows managing an array of
volumes. On construction, it synchronizes their extents, but it only
expands volumes, it never shrinks them. The attachment can happen in z
or in r direction.
  • Loading branch information
paulgessinger authored and Tim Adye committed Jun 27, 2024
1 parent bac235a commit 13dee1d
Show file tree
Hide file tree
Showing 10 changed files with 2,936 additions and 14 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ env:
CCACHE_DIR: ${{ github.workspace }}/ccache
CCACHE_MAXSIZE: 500M
CCACHE_KEY_SUFFIX: r1
ACTS_LOG_FAILURE_THRESHOLD: WARNING

# NOTE this only builds core unittests to reduce the output size. if we
# found a way to have Github actions not fail regularly with this job
Expand Down Expand Up @@ -49,7 +50,7 @@ jobs:
-DCMAKE_BUILD_TYPE=Debug
-DCMAKE_CXX_FLAGS="-Werror -gz -g1"
-DACTS_BUILD_UNITTESTS=on
-DACTS_LOG_FAILURE_THRESHOLD=WARNING
-DACTS_ENABLE_LOG_FAILURE_THRESHOLD=ON
- name: Build
run: cmake --build build
- name: ccache stats
Expand Down
19 changes: 10 additions & 9 deletions .github/workflows/builds.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ on:
paths-ignore:
- "docs/**"

concurrency:
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true

Expand Down Expand Up @@ -49,7 +49,7 @@ jobs:
- name: Configure
# setting CMAKE_CXX_STANDARD=17 is a workaround for a bug in the
# dd4hep CMake configuration that gets triggered on recent CMake
# versions
# versions
run: >
ccache -z &&
cmake -B build -S .
Expand All @@ -59,7 +59,7 @@ jobs:
-DCMAKE_CXX_FLAGS=-Werror
-DCMAKE_CXX_STANDARD=17
-DCMAKE_INSTALL_PREFIX="${INSTALL_DIR}"
-DACTS_ENABLE_LOG_FAILURE_THRESHOLD=ON
-DACTS_ENABLE_LOG_FAILURE_THRESHOLD=ON
-DACTS_BUILD_EVERYTHING=ON
-DACTS_BUILD_ODD=ON
-DACTS_BUILD_EXAMPLES_PYTHON_BINDINGS=ON
Expand Down Expand Up @@ -266,7 +266,7 @@ jobs:
- name: Configure
# setting CMAKE_CXX_STANDARD=17 is a workaround for a bug in the
# dd4hep CMake configuration that gets triggered on recent CMake
# versions
# versions
run: >
ccache -z &&
cmake -B build -S .
Expand All @@ -276,7 +276,7 @@ jobs:
-DCMAKE_CXX_FLAGS=-Werror
-DCMAKE_CXX_STANDARD=${{ matrix.std }}
-DCMAKE_INSTALL_PREFIX="${INSTALL_DIR}"
-DACTS_ENABLE_LOG_FAILURE_THRESHOLD=ON
-DACTS_ENABLE_LOG_FAILURE_THRESHOLD=ON
-DACTS_BUILD_EVERYTHING=ON
-DACTS_BUILD_ODD=ON
-DACTS_BUILD_EXAMPLES_PYTHON_BINDINGS=ON
Expand Down Expand Up @@ -341,6 +341,7 @@ jobs:
SETUP: source /opt/lcg/ROOT/v6.24.00-e7098/x86_64-centos8-gcc10-opt/bin/thisroot.sh
PRELOAD: export LD_PRELOAD=/opt/lcg/gcc/10/x86_64-centos8/lib64/libstdc++.so.6
INSTALL_DIR: ${{ github.workspace }}/install
ACTS_LOG_FAILURE_THRESHOLD: WARNING
steps:
- uses: actions/checkout@v4
- name: Install dependencies
Expand Down Expand Up @@ -369,7 +370,7 @@ jobs:
-DCMAKE_INSTALL_PREFIX="${INSTALL_DIR}"
-DACTS_BUILD_UNITTESTS=ON
-DACTS_BUILD_INTEGRATIONTESTS=ON
-DACTS_LOG_FAILURE_THRESHOLD=WARNING
-DACTS_ENABLE_LOG_FAILURE_THRESHOLD=ON
-DACTS_FORCE_ASSERTIONS=ON
-DACTS_USE_SYSTEM_BOOST=OFF
-DACTS_USE_SYSTEM_EIGEN3=OFF
Expand Down Expand Up @@ -403,7 +404,7 @@ jobs:
-DCMAKE_BUILD_TYPE=Release
-DCMAKE_CXX_FLAGS=-Werror
-DCMAKE_INSTALL_PREFIX="${INSTALL_DIR}"
-DACTS_LOG_FAILURE_THRESHOLD=WARNING
-DACTS_ENABLE_LOG_FAILURE_THRESHOLD=ON
-DACTS_FORCE_ASSERTIONS=ON
-DACTS_USE_SYSTEM_BOOST=OFF
-DACTS_USE_SYSTEM_EIGEN3=OFF
Expand Down Expand Up @@ -434,6 +435,7 @@ jobs:
runs-on: macos-13
env:
INSTALL_DIR: ${{ github.workspace }}/install
ACTS_LOG_FAILURE_THRESHOLD: WARNING
steps:
- uses: actions/checkout@v4
with:
Expand Down Expand Up @@ -479,7 +481,7 @@ jobs:
-DCMAKE_PREFIX_PATH=/usr/local/acts
-DACTS_BUILD_EVERYTHING=ON
-DACTS_BUILD_ODD=ON
-DACTS_LOG_FAILURE_THRESHOLD=WARNING
-DACTS_ENABLE_LOG_FAILURE_THRESHOLD=ON
-DACTS_FORCE_ASSERTIONS=ON
-DACTS_BUILD_EXAMPLES_EDM4HEP=ON
-DACTS_BUILD_PLUGIN_ACTSVG=ON
Expand Down Expand Up @@ -515,4 +517,3 @@ jobs:
run: cmake --build build-downstream
- name: Downstream run
run: ./build-downstream/bin/ShowActsVersion

3 changes: 2 additions & 1 deletion .gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,7 @@ linux_ubuntu_2204_clang:
- cvmfs

variables:
ACTS_LOG_FAILURE_THRESHOLD: WARNING
INSTALL_DIR: ${{ github.workspace }}/install

SETUP:
Expand Down Expand Up @@ -382,7 +383,7 @@ linux_ubuntu_2204_clang:
-DCMAKE_CXX_FLAGS=-Werror
-DCMAKE_CXX_STANDARD=17
-DCMAKE_INSTALL_PREFIX="${INSTALL_DIR}"
-DACTS_LOG_FAILURE_THRESHOLD=WARNING
-DACTS_ENABLE_LOG_FAILURE_THRESHOLD=ON
-DACTS_BUILD_EXAMPLES_PYTHON_BINDINGS=ON
-DACTS_FORCE_ASSERTIONS=OFF
-DACTS_BUILD_UNITTESTS=ON
Expand Down
216 changes: 216 additions & 0 deletions Core/include/Acts/Geometry/CylinderVolumeStack.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
// 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/Geometry/CylinderVolumeBounds.hpp"
#include "Acts/Geometry/Volume.hpp"
#include "Acts/Utilities/BinningType.hpp"
#include "Acts/Utilities/Logger.hpp"

#include <vector>

namespace Acts {

/// @class CylinderVolumeStack
/// This class implements a z-aligned or r-aligned stack
/// of cylinder volumes with synchronized bounds.
/// Externally, it presents as a single volume.
/// On construction, the input volumes are modified so that
/// they are connected in z and r 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 volumes are never shrunk, because this would potentially
/// 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
/// @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 CylinderVolumeStack
/// @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 CylinerVolumeBounds
/// and cannot have a @f$\phi@f$ sector or bevels.
/// @note Preconditions are checked on construction
CylinderVolumeStack(
std::vector<Volume*>& volumes, BinningValue direction,
AttachmentStrategy strategy = AttachmentStrategy::Midpoint,
ResizeStrategy resizeStrategy = ResizeStrategy::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
/// @pre The volume bounds need to be of type
/// @c CylinderVolumeBounds.
void update(std::shared_ptr<const VolumeBounds> volbounds,
std::optional<Transform3> transform = std::nullopt) override;

/// 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 newBounds 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 CylinderVolumeBounds.
void update(std::shared_ptr<const CylinderVolumeBounds> newBounds,
std::optional<Transform3> transform, const Logger& logger);

/// 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;

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 direction is the binning direction
/// @param strategy is the attachment strategy
/// @param logger is the logger
void initializeOuterVolume(BinningValue direction,
AttachmentStrategy 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
static 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
static void checkVolumeAlignment(const std::vector<VolumeTuple>& volumes,
const Logger& logger);

/// Helper function that checks overlaps and attaches in z 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> checkOverlapAndAttachInZ(
std::vector<VolumeTuple>& volumes, AttachmentStrategy strategy,
const Logger& logger);

/// Helper function to synchronize the r 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<ActsScalar, ActsScalar> synchronizeRBounds(
std::vector<VolumeTuple>& volumes, const Logger& logger);

/// Helper function that checks overlaps and attaches in r 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> checkOverlapAndAttachInR(
std::vector<VolumeTuple>& volumes, AttachmentStrategy strategy,
const Logger& logger);

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

/// Helper functions that checks if the cylinder volume bounds
/// given do not contain any phi sectors or bevels.
/// @param bounds is the cylinder volume bounds
/// @param logger is the logger
static void checkNoPhiOrBevel(const CylinderVolumeBounds& bounds,
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);

BinningValue m_direction{};
ResizeStrategy 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
1 change: 1 addition & 0 deletions Core/src/Geometry/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,5 @@ target_sources(
TrapezoidVolumeBounds.cpp
Volume.cpp
VolumeBounds.cpp
CylinderVolumeStack.cpp
)
16 changes: 13 additions & 3 deletions Core/src/Geometry/CylinderVolumeBounds.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -256,12 +256,22 @@ std::vector<ActsScalar> CylinderVolumeBounds::values() const {
}

void CylinderVolumeBounds::checkConsistency() {
if (get(eMinR) < 0. || get(eMaxR) <= 0. || get(eMinR) >= get(eMaxR)) {
throw std::invalid_argument("CylinderVolumeBounds: invalid radial input.");
if (get(eMinR) < 0. || get(eMaxR) <= 0.) {
throw std::invalid_argument(
"CylinderVolumeBounds: invalid radial input: minR (" +
std::to_string(get(eMinR)) + ") < 0 or maxR (" +
std::to_string(get(eMaxR)) + ") <= 0");
}
if (get(eMinR) >= get(eMaxR)) {
throw std::invalid_argument(
"CylinderVolumeBounds: invalid radial input: minR (" +
std::to_string(get(eMinR)) + ") >= (" + std::to_string(get(eMaxR)) +
")");
}
if (get(eHalfLengthZ) <= 0) {
throw std::invalid_argument(
"CylinderVolumeBounds: invalid longitudinal input.");
"CylinderVolumeBounds: invalid longitudinal input: hlZ (" +
std::to_string(get(eHalfLengthZ)) + ") <= 0");
}
if (get(eHalfPhiSector) < 0. || get(eHalfPhiSector) > M_PI) {
throw std::invalid_argument(
Expand Down
Loading

0 comments on commit 13dee1d

Please sign in to comment.