Skip to content

Commit

Permalink
feat: introduce external volume structure builder (#2148)
Browse files Browse the repository at this point in the history
After the inclusion of the 
- `LayerStructureBuilder` which builds intrinsic layer structures of `DetectorVolume` objects
- `CylindricalContainerBuilder` which connects cylindrical `DetectorVolume` objects

this adds the last missing piece for describing cylindrical detector geometries:

- `VolumeStructurBuilder` which helps to define the external volume structure of a `DetectorVolume`.

To harmonise this accordingly, the `GenericCuboidBounds` are slightly adapted in order to have the same look and feel as all the other `VolumeBounds` derivates.
  • Loading branch information
asalzburger committed May 25, 2023
1 parent 1074d34 commit f8d0446
Show file tree
Hide file tree
Showing 11 changed files with 656 additions and 8 deletions.
69 changes: 69 additions & 0 deletions Core/include/Acts/Detector/VolumeStructureBuilder.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// This file is part of the Acts project.
//
// Copyright (C) 2023 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/Detector/DetectorComponents.hpp"
#include "Acts/Detector/interface/IExternalStructureBuilder.hpp"
#include "Acts/Geometry/Extent.hpp"
#include "Acts/Geometry/GeometryContext.hpp"
#include "Acts/Geometry/VolumeBounds.hpp"
#include "Acts/Utilities/Logger.hpp"

#include <optional>

namespace Acts {
namespace Experimental {

/// This class provides the external detector volume structure, configured
/// either from:
/// - a volume extent
/// - from an array with volume bounds identification
///
class VolumeStructureBuilder : public IExternalStructureBuilder {
public:
/// Nexted configuration struct
struct Config {
/// A defined volume bound type
VolumeBounds::BoundsType boundsType = VolumeBounds::BoundsType::eOther;
/// The values (if already defined)
std::vector<ActsScalar> boundValues = {};
/// The optional extent to feed into the values
std::optional<Extent> extent = std::nullopt;
};

/// Constructor
///
/// @param cfg is the configuration struct
/// @param logger logging instance for screen output
VolumeStructureBuilder(const Config& cfg,
std::unique_ptr<const Logger> logger =
getDefaultLogger("VolumeStructureBuilder",
Logging::INFO));

/// The interface definition for internal structure creation
///
/// @param gctx the geometry context at the creation of the internal structure
///
/// @return a consistent set of detector volume internals
ExternalStructure construct(const GeometryContext& gctx) const final;

private:
/// configuration object
Config m_cfg;

/// Private access method to the logger
const Logger& logger() const { return *m_logger; }

/// logging instance
std::unique_ptr<const Logger> m_logger;
};

} // namespace Experimental
} // namespace Acts
2 changes: 1 addition & 1 deletion Core/include/Acts/Geometry/Extent.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ class Extent {
return 0.5 * (m_range[bValue].min() + m_range[bValue].max());
}

/// Access the parameter internval
/// Access the parameter interval (i.e. the range span)
///
/// @param bValue the binning identification
ActsScalar interval(BinningValue bValue) const {
Expand Down
10 changes: 7 additions & 3 deletions Core/include/Acts/Geometry/GenericCuboidVolumeBounds.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@ class IVisualization3D;

class GenericCuboidVolumeBounds : public VolumeBounds {
public:
static constexpr size_t eSize = 24;
/// @brief This struct helps to symmetrize with the
/// the other volume bounds classes
struct BoundValues {
static constexpr size_t eSize = 24;
};

GenericCuboidVolumeBounds() = delete;

Expand All @@ -40,8 +44,8 @@ class GenericCuboidVolumeBounds : public VolumeBounds {
/// Constructor from a fixed size array
///
/// @param values The input values
GenericCuboidVolumeBounds(const std::array<double, eSize>& values) noexcept(
false);
GenericCuboidVolumeBounds(
const std::array<double, BoundValues::eSize>& values) noexcept(false);

~GenericCuboidVolumeBounds() override = default;

Expand Down
19 changes: 19 additions & 0 deletions Core/include/Acts/Utilities/Helpers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "Acts/Definitions/Common.hpp"
#include "Acts/Definitions/TrackParametrization.hpp"
#include "Acts/Utilities/BinningType.hpp"
#include "Acts/Utilities/Enumerate.hpp"
#include "Acts/Utilities/TypeTraits.hpp"

#include <array>
Expand Down Expand Up @@ -374,6 +375,24 @@ std::vector<const T*> unpack_shared_const_vector(
return rawPtrs;
}

/// This can be abandoned with C++20 to use the std::to_array method
///
/// @note only the first kDIM elments will obviously be filled, if the
/// vector tends to be longer, it is truncated
///
/// @param vecvals the vector of bound values to be converted
/// @return an array with the filled values
template <std::size_t kDIM, typename value_type>
std::array<value_type, kDIM> to_array(const std::vector<value_type>& vecvals) {
std::array<value_type, kDIM> rarray = {};
for (const auto [iv, v] : enumerate(vecvals)) {
if (iv < kDIM) {
rarray[iv] = v;
}
}
return rarray;
}

/// @brief Dispatch a call based on a runtime value on a function taking the
/// value at compile time.
///
Expand Down
1 change: 1 addition & 0 deletions Core/src/Detector/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ target_sources(
Portal.cpp
PortalGenerators.cpp
ProtoDetector.cpp
VolumeStructureBuilder.cpp
)
176 changes: 176 additions & 0 deletions Core/src/Detector/VolumeStructureBuilder.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
// This file is part of the Acts project.
//
// Copyright (C) 2023 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/.

#include "Acts/Detector/VolumeStructureBuilder.hpp"

#include "Acts/Detector/PortalGenerators.hpp"
#include "Acts/Geometry/ConeVolumeBounds.hpp"
#include "Acts/Geometry/CuboidVolumeBounds.hpp"
#include "Acts/Geometry/CutoutCylinderVolumeBounds.hpp"
#include "Acts/Geometry/CylinderVolumeBounds.hpp"
#include "Acts/Geometry/GenericCuboidVolumeBounds.hpp"
#include "Acts/Geometry/TrapezoidVolumeBounds.hpp"
#include "Acts/Utilities/Enumerate.hpp"
#include "Acts/Utilities/Helpers.hpp"

Acts::Experimental::VolumeStructureBuilder::VolumeStructureBuilder(
const Acts::Experimental::VolumeStructureBuilder::Config& cfg,
std::unique_ptr<const Acts::Logger> logger)
: IExternalStructureBuilder(), m_cfg(cfg), m_logger(std::move(logger)) {
// Sanity cross-checks
if (m_cfg.boundValues.empty() and not m_cfg.extent.has_value()) {
throw std::invalid_argument(
"VolumeStructureBuilder: no extent nor boundary values given");
}
// Check for the bounds type
if (m_cfg.boundsType == VolumeBounds::BoundsType::eOther) {
throw std::invalid_argument(
"VolumeStructureBuilder: no known volume bounds type provided.");
}
}

Acts::Experimental::ExternalStructure
Acts::Experimental::VolumeStructureBuilder::construct(
[[maybe_unused]] const Acts::GeometryContext& gctx) const {
// The volume bounds to be constructed
std::unique_ptr<VolumeBounds> volumeBounds = nullptr;

// The transform of the volume, default: identity
auto transform = Transform3::Identity();
std::vector<ActsScalar> boundValues = m_cfg.boundValues;

// This code dispatches into the dedicated volume types
switch (m_cfg.boundsType) {
case VolumeBounds::BoundsType::eCone: {
ACTS_VERBOSE("Building conical volume bounds.");
// Cone translation - only pre-defined values
if (boundValues.size() < 5u) {
throw std::runtime_error(
"VolumeStructureBuilder: parameters for cone volume bounds need to "
"be fully provided, they can not be estimated from an Extent "
"object. It needs at least 5 parameters, while " +
std::to_string(boundValues.size()) + " where given");
}
auto bArray = to_array<ConeVolumeBounds::BoundValues::eSize, ActsScalar>(
boundValues);
volumeBounds = std::make_unique<ConeVolumeBounds>(bArray);
} break;
case VolumeBounds::BoundsType::eCuboid: {
ACTS_VERBOSE("Building cuboid volume bounds.");
// Cuboid translation - either parameters / or extent
if (boundValues.empty() and m_cfg.extent.has_value()) {
ACTS_VERBOSE("Cuboid: estimate parameters from Extent.");
const auto& vExtent = m_cfg.extent.value();
if (vExtent.constrains(binX) and vExtent.constrains(binY) and
vExtent.constrains(binZ)) {
transform.pretranslate(Vector3(vExtent.medium(binX),
vExtent.medium(binY),
vExtent.medium(binZ)));
boundValues = {0.5 * vExtent.interval(binX),
0.5 * vExtent.interval(binY),
0.5 * vExtent.interval(binZ)};

} else {
throw std::runtime_error(
"VolumeStructureBuilder: translation to cuboid does not work as "
"the extent does not constrain all necessary value.");
}
} else if (boundValues.size() < 3u) {
throw std::runtime_error(
"VolumeStructureBuilder: parameters for cuboid volume bounds need "
"to be fully provided, it needs exaclty 3 parameters, while " +
std::to_string(boundValues.size()) + " where given");
}
auto bArray =
to_array<CuboidVolumeBounds::BoundValues::eSize>(boundValues);
volumeBounds = std::make_unique<CuboidVolumeBounds>(bArray);
} break;
case VolumeBounds::BoundsType::eCutoutCylinder: {
ACTS_VERBOSE("Building cutout cylindrical volume bounds.");
// Cutout cylinder translation - only parameters
if (boundValues.size() < 5u) {
throw std::runtime_error(
"VolumeStructureBuilder: parameters for cutout cylinder volume "
"bounds need to be fully provided, they can not be estimated from "
"an Extent object. It needs exaclty 3 parameters, while " +
std::to_string(boundValues.size()) + " where given");
}
auto bArray =
to_array<CutoutCylinderVolumeBounds::BoundValues::eSize>(boundValues);
volumeBounds = std::make_unique<CutoutCylinderVolumeBounds>(bArray);
} break;
case VolumeBounds::BoundsType::eCylinder: {
ACTS_VERBOSE("Building cylindrical volume bounds.");
// Cylinder translation - either parameters / or extent
if (boundValues.empty() and m_cfg.extent.has_value()) {
ACTS_VERBOSE("Cylinder: estimate parameters from Extent.");
const auto& vExtent = m_cfg.extent.value();
if (vExtent.constrains(binR) and vExtent.constrains(binZ)) {
transform.pretranslate(Vector3(0., 0., vExtent.medium(binZ)));
boundValues = {vExtent.min(binR), vExtent.max(binR),
0.5 * vExtent.interval(binZ)};
if (vExtent.constrains(binPhi)) {
boundValues.push_back(0.5 * vExtent.interval(binPhi));
boundValues.push_back(vExtent.medium(binPhi));
}
} else {
throw std::runtime_error(
"VolumeStructureBuilder: translation to cuboid does not work as "
"the extent does not constrain all necessary values.");
}
} else if (boundValues.size() < 3u) {
throw std::runtime_error(
"VolumeStructureBuilder: parameters for cylinder volume "
"bounds need to be fully provided, it needs at least 3 parameters, "
"while " +
std::to_string(boundValues.size()) + " where given");
}
// Check if phi has been constraint, otherwise fill it with full coverage
if (boundValues.size() == 3u) {
boundValues.push_back(M_PI);
boundValues.push_back(0.);
}
auto bArray =
to_array<CylinderVolumeBounds::BoundValues::eSize>(boundValues);
volumeBounds = std::make_unique<CylinderVolumeBounds>(bArray);
} break;
case VolumeBounds::BoundsType::eGenericCuboid: {
ACTS_VERBOSE("Building generic cuboid volume bounds.");
// Generic cuboid translation - parameters only
if (boundValues.size() < GenericCuboidVolumeBounds::BoundValues::eSize) {
throw std::runtime_error(
"VolumeStructureBuilder: parameters for generic cuboid volume "
"bounds need to be provided, they can not be estimated from an "
"Extent object. It needs exactly 24 parameters, while " +
std::to_string(boundValues.size()) + " where given");
}
auto bArray =
to_array<GenericCuboidVolumeBounds::BoundValues::eSize>(boundValues);
volumeBounds = std::make_unique<GenericCuboidVolumeBounds>(bArray);
} break;
case VolumeBounds::BoundsType::eTrapezoid: {
ACTS_VERBOSE("Building trapezoid volume bounds.");
// Trapezoid translation - parameters only
if (boundValues.size() < 4u) {
throw std::runtime_error(
"VolumeStructureBuilder: parameters for trapezoid volume bounds "
"need to be provided, they can not be estimated from an Extent "
"object. It needs at least 4 parameters, while " +
std::to_string(boundValues.size()) + " where given");
}
auto bArray =
to_array<TrapezoidVolumeBounds::BoundValues::eSize>(boundValues);
volumeBounds = std::make_unique<TrapezoidVolumeBounds>(bArray);
} break;
default:
break;
}
// Return the transform, the volume bounds, and some default portal
// generators
return {transform, std::move(volumeBounds), defaultPortalGenerator()};
}
4 changes: 2 additions & 2 deletions Core/src/Geometry/GenericCuboidVolumeBounds.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Acts::GenericCuboidVolumeBounds::GenericCuboidVolumeBounds(
}

Acts::GenericCuboidVolumeBounds::GenericCuboidVolumeBounds(
const std::array<double, GenericCuboidVolumeBounds::eSize>&
const std::array<double, GenericCuboidVolumeBounds::BoundValues::eSize>&
values) noexcept(false)
: m_vertices() {
for (size_t iv = 0; iv < 8; ++iv) {
Expand Down Expand Up @@ -181,7 +181,7 @@ void Acts::GenericCuboidVolumeBounds::construct() noexcept(false) {

std::vector<double> Acts::GenericCuboidVolumeBounds::values() const {
std::vector<double> rvalues;
rvalues.reserve(eSize);
rvalues.reserve(BoundValues::eSize);
for (size_t iv = 0; iv < 8; ++iv) {
for (size_t ic = 0; ic < 3; ++ic) {
rvalues.push_back(m_vertices[iv][ic]);
Expand Down
2 changes: 2 additions & 0 deletions Tests/UnitTests/Core/Detector/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@ add_unittest(SupportHelper SupportHelperTests.cpp)
add_unittest(ProtoDetector ProtoDetectorTests.cpp)
add_unittest(Portal PortalTests.cpp)
add_unittest(PortalGenerators PortalGeneratorsTests.cpp)
add_unittest(VolumeStructureBuilder VolumeStructureBuilderTests.cpp)


4 changes: 2 additions & 2 deletions Tests/UnitTests/Core/Detector/LayerStructureBuilderTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ struct SurfaceProvider {

BOOST_AUTO_TEST_SUITE(Detector)

// Test the creation of the Ring
// Test the creation of a ring like structure
BOOST_AUTO_TEST_CASE(LayerStructureBuilder_creationRing) {
// Detector store
CylindricalTrackingGeometry::DetectorStore dStore;
Expand Down Expand Up @@ -114,7 +114,7 @@ BOOST_AUTO_TEST_CASE(LayerStructureBuilder_creationRing) {
BOOST_CHECK(volumeUpdator2.connected());
}

// Test the creation of the Cylinder
// Test the creation of a cylindrical structure
BOOST_AUTO_TEST_CASE(LayerStructureKDT_creationCylinder) {
CylindricalTrackingGeometry::DetectorStore dStore;
auto cSurfaces = cGeometry.surfacesCylinder(dStore, 8.4, 36., 0.15, 0.145, 72,
Expand Down

0 comments on commit f8d0446

Please sign in to comment.