Skip to content

Commit

Permalink
feat: CuboidalContainerBuilder + UnitTest (#2882)
Browse files Browse the repository at this point in the history
Adding the CuboidalContainerBuilder class, analogous to the already existing CylindricalContainerBuilder. 

The builder can creates DetectorComponents at the Navigation Level through connecting volumes in 1D.

Container connection is not functional for now, due to the issues in the CuboidalDetectorHelper.

A small unit test is shipped together with the class. Misconfiguration and volume connection in 1D are checked.
  • Loading branch information
ssdetlab committed Jan 19, 2024
1 parent b6480f7 commit 3f6f183
Show file tree
Hide file tree
Showing 5 changed files with 484 additions and 0 deletions.
110 changes: 110 additions & 0 deletions Core/include/Acts/Detector/CuboidalContainerBuilder.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// 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/Detector/Blueprint.hpp"
#include "Acts/Detector/DetectorComponents.hpp"
#include "Acts/Detector/interface/IDetectorComponentBuilder.hpp"
#include "Acts/Geometry/GeometryContext.hpp"
#include "Acts/Utilities/BinningType.hpp"
#include "Acts/Utilities/Logger.hpp"

#include <memory>
#include <string>
#include <vector>

namespace Acts {
namespace Experimental {

class IRootVolumeFinderBuilder;
class IGeometryIdGenerator;

/// @brief A dedicated container builder for cuboid detector containers
///
/// It relies on the detailed implementation of the CuboidDetectorHelper
/// and allows for DetectorVolume attachment in x/y/z
///
/// There exists an option to create this container builder (recursively)
/// from a blueprint tree, which attempts to fill in the gap volumes
/// accordingly.
///
/// @note the builder expects a fully consistent set of sub volume builders
/// that will be executed in a chain
///
/// @note allowed BinningValue(s) for the cuboid container builder are
/// {binX}, {binY}, {binZ}.
///
/// @note Connecting containers isn't functional yet due to the underlying
/// issues in the CuboidDetectorHelper
///
class CuboidalContainerBuilder : public IDetectorComponentBuilder {
public:
/// Nested configuration object
struct Config {
/// The configured volume builders
std::vector<std::shared_ptr<const IDetectorComponentBuilder>> builders = {};
/// Binning prescription of attachment
BinningValue binning{};
/// The root volume finder
std::shared_ptr<const IRootVolumeFinderBuilder> rootVolumeFinderBuilder =
nullptr;
/// The geometry id generator
std::shared_ptr<const IGeometryIdGenerator> geoIdGenerator = nullptr;
/// An eventual reverse geometry id generation
bool geoIdReverseGen = false;
/// Auxiliary information, mainly for screen output
std::string auxiliary = "";
};

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

/// Constructor from blueprint and logging level
///
/// It will create recursively the builders of sub volumes
///
/// @param bpNode is the entry blue print node
/// @param logLevel is the logging output level for the builder tools
///
/// @note no checking is being done on consistency of the blueprint,
/// it is assumed it has passed first through gap filling via the
/// blueprint helper.
///
/// @note that the naming of the builders is taken from the bluprint nodes
///
/// @return a cylindrical container builder representing this blueprint
CuboidalContainerBuilder(const Acts::Experimental::Blueprint::Node& bpNode,
Acts::Logging::Level logLevel = Acts::Logging::INFO);

/// The final implementation of the cylindrical container builder
///
/// @param gctx The geometry context for this call
///
/// @return an outgoing detector component
DetectorComponent 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
1 change: 1 addition & 0 deletions Core/src/Detector/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ target_sources(
detail/SupportHelper.cpp
detail/IndexedGridFiller.cpp
CylindricalContainerBuilder.cpp
CuboidalContainerBuilder.cpp
Detector.cpp
DetectorBuilder.cpp
DetectorVolume.cpp
Expand Down
185 changes: 185 additions & 0 deletions Core/src/Detector/CuboidalContainerBuilder.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
// 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/CuboidalContainerBuilder.hpp"

#include "Acts/Detector/DetectorComponents.hpp"
#include "Acts/Detector/DetectorVolumeBuilder.hpp"
#include "Acts/Detector/VolumeStructureBuilder.hpp"
#include "Acts/Detector/detail/CuboidalDetectorHelper.hpp"
#include "Acts/Detector/interface/IGeometryIdGenerator.hpp"
#include "Acts/Detector/interface/IRootVolumeFinderBuilder.hpp"
#include "Acts/Navigation/DetectorVolumeFinders.hpp"

#include <algorithm>
#include <ostream>
#include <stdexcept>
#include <utility>

namespace Acts {
namespace Experimental {
class DetectorVolume;
} // namespace Experimental
} // namespace Acts

Acts::Experimental::CuboidalContainerBuilder::CuboidalContainerBuilder(
const Acts::Experimental::CuboidalContainerBuilder::Config& cfg,
std::unique_ptr<const Acts::Logger> logger)
: IDetectorComponentBuilder(), m_cfg(cfg), m_logger(std::move(logger)) {
// Check if builders are present
if (m_cfg.builders.empty()) {
throw std::invalid_argument(
"CuboidalContainerBuilder: no sub builders provided.");
}
// Check if binning value is correctly chosen
if (m_cfg.binning != Acts::binX && m_cfg.binning != Acts::binY &&
m_cfg.binning != Acts::binZ) {
throw std::invalid_argument(
"CuboidalContainerBuilder: Invalid binning value. Only Acts::binX, "
"Acts::binY, Acts::binZ are supported.");
}
}

Acts::Experimental::CuboidalContainerBuilder::CuboidalContainerBuilder(
const Acts::Experimental::Blueprint::Node& bpNode,
Acts::Logging::Level logLevel)
: IDetectorComponentBuilder(),
m_logger(getDefaultLogger(bpNode.name + "_cont", logLevel)) {
if (bpNode.boundsType != VolumeBounds::BoundsType::eCuboid) {
throw std::invalid_argument(
"CuboidalContainerBuilder: boundary type must be cuboid - for "
"building from a blueprint node.");
}

std::vector<std::shared_ptr<const IDetectorComponentBuilder>> builders;
for (const auto& child : bpNode.children) {
if (child->isLeaf()) {
// Volume structure
VolumeStructureBuilder::Config vsCfg;
vsCfg.transform = child->transform;
vsCfg.boundsType = child->boundsType;
vsCfg.boundValues = child->boundaryValues;
vsCfg.auxiliary = "*** acts auto-generated shape builder ***";
auto vsBuilder = std::make_shared<VolumeStructureBuilder>(
vsCfg, getDefaultLogger(child->name + "_shape", logLevel));
// Detector volume builder
DetectorVolumeBuilder::Config dvCfg;
dvCfg.name = child->name;
dvCfg.externalsBuilder = vsBuilder;
dvCfg.internalsBuilder = child->internalsBuilder;
dvCfg.auxiliary = "*** acts auto-generated volume builder ***";
// Add the builder
m_cfg.builders.push_back(std::make_shared<DetectorVolumeBuilder>(
dvCfg, getDefaultLogger(child->name, logLevel)));
} else {
// This evokes the recursive stepping down the tree
m_cfg.builders.push_back(
std::make_shared<CuboidalContainerBuilder>(*child, logLevel));
}
}
// Check if builders are present
if (m_cfg.builders.empty()) {
throw std::invalid_argument(
"CuboidalContainerBuilder: no sub builders provided.");
}
if (bpNode.binning.size() != 1) {
throw std::invalid_argument(
"CuboidalContainerBuilder: >1D binning is not supported for cuboid "
"containers.");
}
m_cfg.binning = bpNode.binning.at(0);
// Check if binning value is correctly chosen
if (m_cfg.binning != Acts::binX && m_cfg.binning != Acts::binY &&
m_cfg.binning != Acts::binZ) {
throw std::invalid_argument(
"CuboidalContainerBuilder: Invalid binning value. Only Acts::binX, "
"Acts::binY, Acts::binZ are supported.");
}

m_cfg.auxiliary = "*** acts auto-generated from proxy ***";
m_cfg.geoIdGenerator = bpNode.geoIdGenerator;
m_cfg.rootVolumeFinderBuilder = bpNode.rootVolumeFinderBuilder;
}

Acts::Experimental::DetectorComponent
Acts::Experimental::CuboidalContainerBuilder::construct(
const GeometryContext& gctx) const {
// Return container object
DetectorComponent::PortalContainer rContainer;
bool atNavigationLevel = true;

// Create the indivudal components, collect for both outcomes
std::vector<DetectorComponent> components;
ACTS_DEBUG("Building container from " << m_cfg.builders.size()
<< " components.");
// Check through the component volumes - if every builder only
// built exactly one volume, you are at pure navigation level
// Collect the volumes
std::vector<std::shared_ptr<DetectorVolume>> volumes;
std::vector<DetectorComponent::PortalContainer> containers;
std::vector<std::shared_ptr<DetectorVolume>> rootVolumes;
// Run through the builders
std::for_each(
m_cfg.builders.begin(), m_cfg.builders.end(), [&](const auto& builder) {
auto [cVolumes, cContainer, cRoots] = builder->construct(gctx);
atNavigationLevel = (atNavigationLevel && cVolumes.size() == 1u);
ACTS_VERBOSE("Number of volumes: " << cVolumes.size());
// Collect individual components, volumes, containers, roots
volumes.insert(volumes.end(), cVolumes.begin(), cVolumes.end());
containers.push_back(cContainer);
rootVolumes.insert(rootVolumes.end(), cRoots.volumes.begin(),
cRoots.volumes.end());
});
// Navigation level detected, connect volumes (cleaner and faster than
// connect containers)
if (atNavigationLevel) {
ACTS_VERBOSE(
"Component volumes are at navigation level: connecting volumes.");
// Connect volumes
rContainer = Acts::Experimental::detail::CuboidalDetectorHelper::connect(
gctx, volumes, m_cfg.binning, {}, logger().level());

} else {
ACTS_VERBOSE("Components contain sub containers: connect containers.");
// Connect containers
rContainer = Acts::Experimental::detail::CuboidalDetectorHelper::connect(
gctx, containers, m_cfg.binning, {}, logger().level());
}
ACTS_VERBOSE("Number of root volumes: " << rootVolumes.size());

// Geometry Id generation
if (m_cfg.geoIdGenerator != nullptr) {
ACTS_DEBUG("Assigning geometry ids to the detector");
auto cache = m_cfg.geoIdGenerator->generateCache();
if (m_cfg.geoIdReverseGen) {
std::for_each(rootVolumes.rbegin(), rootVolumes.rend(), [&](auto& v) {
m_cfg.geoIdGenerator->assignGeometryId(cache, *v);
ACTS_VERBOSE("-> Assigning geometry id to volume " << v->name());
});
} else {
std::for_each(rootVolumes.begin(), rootVolumes.end(), [&](auto& v) {
m_cfg.geoIdGenerator->assignGeometryId(cache, *v);
ACTS_VERBOSE("-> Assigning geometry id to volume " << v->name());
});
}
}

// Check if a root volume finder is provided
if (m_cfg.rootVolumeFinderBuilder) {
// Return the container
return Acts::Experimental::DetectorComponent{
volumes, rContainer,
RootDetectorVolumes{
rootVolumes,
m_cfg.rootVolumeFinderBuilder->construct(gctx, rootVolumes)}};
}

// Return the container
return Acts::Experimental::DetectorComponent{
volumes, rContainer, RootDetectorVolumes{rootVolumes, tryRootVolumes()}};
}
1 change: 1 addition & 0 deletions Tests/UnitTests/Core/Detector/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ add_unittest(BlueprintHelper BlueprintHelperTests.cpp)
add_unittest(CylindricalContainerBuilder CylindricalContainerBuilderTests.cpp)
add_unittest(CylindricalDetectorFromBlueprint CylindricalDetectorFromBlueprintTests.cpp)
add_unittest(CuboidalDetectorHelper CuboidalDetectorHelperTests.cpp)
add_unittest(CuboidalContainerBuilder CuboidalContainerBuilderTests.cpp)
add_unittest(CylindricalDetectorHelper CylindricalDetectorHelperTests.cpp)
add_unittest(Detector DetectorTests.cpp)
add_unittest(DetectorBuilder DetectorBuilderTests.cpp)
Expand Down
Loading

0 comments on commit 3f6f183

Please sign in to comment.