Skip to content

Commit

Permalink
feat: build open data detector from dd4hep in experimental detector s…
Browse files Browse the repository at this point in the history
…chema (#2766)

This PR includes everything to build the first version of the open data
detector in the `Experimental::Detector` schema, purely by using variant
parameters.

For doing this, I needed to introduce:
- place holder proto binning (the range will be set after surface
parsing)
- a new method in the `ActsExamples::DD4hepDetector` 
- some more python bindings

Currently, the `thirdparty/OpenDataDetector` is not yet updated, this
will be done in a follow-up PR.

---------

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
  • Loading branch information
asalzburger and kodiakhq[bot] authored Dec 12, 2023
1 parent 15203d1 commit ff03f7d
Show file tree
Hide file tree
Showing 24 changed files with 510 additions and 208 deletions.
15 changes: 13 additions & 2 deletions Core/include/Acts/Detector/LayerStructureBuilder.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,16 @@ class LayerStructureBuilder : public IInternalStructureBuilder {
std::vector<ProtoSupport> supports = {};
/// Definition of Binnings
std::vector<ProtoBinning> binnings = {};
/// Optional extent (if already parsed), will trigger binning autorange
/// check
std::optional<Extent> extent = std::nullopt;
/// Minimum number of surfaces to build an internal structure
/// - otherwise the tryAll options is used
unsigned int nMinimalSurfaces = 4u;
/// Polyhedron approximations
unsigned int nSegments = 1u;
/// Full closed phi binning
bool fullPhiBinning = true;
/// Extra information, mainly for screen output
std::string auxiliary = "";
};
Expand All @@ -107,17 +112,23 @@ class LayerStructureBuilder : public IInternalStructureBuilder {
///
/// @param gctx the geometry context at the creation of the internal structure
///
/// This will take the surfaces from the surfaces provider and use the binning
/// description to create an internal indexed surface structure.
///
/// @note if the configuration provides an extent, the range of the binning
/// will be checked againstit and adapted if necessary
///
/// @return a consistent set of detector volume internals
InternalStructure construct(const GeometryContext& gctx) const final;

private:
/// configuration object
/// Configuration object
Config m_cfg;

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

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

Expand Down
26 changes: 23 additions & 3 deletions Core/include/Acts/Detector/ProtoBinning.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,12 @@ struct ProtoBinning {
std::size_t exp = 0u)
: binValue(bValue), boundaryType(bType), expansion(exp) {
if (minE >= maxE) {
throw std::invalid_argument(
"ProtoBinning: Invalid binning, min edge needs to be smaller than "
"max edge.");
std::string msg = "ProtoBinning: Invalid binning for value '";
msg += binningValueNames()[bValue];
msg += "', min edge (" + std::to_string(minE) + ") ";
msg += " needs to be smaller than max edge (";
msg += std::to_string(maxE) + ").";
throw std::invalid_argument(msg);
}
if (nbins < 1u) {
throw std::invalid_argument(
Expand All @@ -86,6 +89,23 @@ struct ProtoBinning {
}
}

/// Placeholder constructors - for equidistant binning
///
/// @note this is designed to give a binning prescription
/// when the actual extent is not yet evaluated, only works
/// for equidistant binning obviously
///
/// @param bValue the value/cast in which this is binned
/// @param bType the axis boundary type
/// @param nbins the number of bins
/// @param exp the expansion (in bins)
ProtoBinning(BinningValue bValue, Acts::detail::AxisBoundaryType bType,
std::size_t nbins, std::size_t exp = 0u)
: binValue(bValue),
boundaryType(bType),
edges(nbins + 1, 0.),
expansion(exp) {}

std::size_t bins() const { return edges.size() - 1u; }
};

Expand Down
85 changes: 76 additions & 9 deletions Core/src/Detector/LayerStructureBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "Acts/Detector/LayerStructureBuilder.hpp"

#include "Acts/Definitions/Algebra.hpp"
#include "Acts/Detector/ProtoBinning.hpp"
#include "Acts/Detector/detail/GridAxisGenerators.hpp"
#include "Acts/Detector/detail/IndexedSurfacesGenerator.hpp"
#include "Acts/Detector/detail/ReferenceGenerators.hpp"
Expand Down Expand Up @@ -38,6 +39,47 @@ class DetectorVolume;

namespace {

/// Check autorange for a given binning
///
/// @param pBinning the proto binning
/// @param extent the extent
/// @param fullPhi indicates whether the full phi range is used
///
void adaptBinningRage(std::vector<Acts::Experimental::ProtoBinning>& pBinning,
const Acts::Extent& extent, bool fullPhiBinning) {
for (auto& pb : pBinning) {
// Starting values
Acts::ActsScalar vmin = pb.edges.front();
Acts::ActsScalar vmax = pb.edges.back();
// Get the number of bins
std::size_t nBins = pb.bins();
// Check if extent overwrites that
if (extent.constrains(pb.binValue)) {
const auto& range = extent.range(pb.binValue);
// Patch the edges values from the range
vmin = range.min();
vmax = range.max();
} else if (pb.binValue == Acts::binPhi && fullPhiBinning) {
vmin = -M_PI;
vmax = M_PI;
pb.boundaryType = Acts::detail::AxisBoundaryType::Closed;
}
// Possibly update the edges
if (pb.axisType == Acts::detail::AxisType::Equidistant) {
Acts::ActsScalar binWidth = (vmax - vmin) / nBins;
// Fill the edges
pb.edges = {vmin};
pb.edges.resize(nBins + 1);
for (std::size_t ib = 0; ib <= nBins; ++ib) {
pb.edges[ib] = vmin + ib * binWidth;
}
} else {
pb.edges.front() = vmin;
pb.edges.back() = vmax;
}
}
}

/// Helper for 1-dimensional generators
///
/// @tparam aType is the axis boundary type: closed or bound
Expand Down Expand Up @@ -204,9 +246,14 @@ Acts::Experimental::LayerStructureBuilder::construct(
}
// To correctly attach the support structures, estimate the extent
Extent internalExtent;
for (const auto& s : internalSurfaces) {
auto sPolyhedron = s->polyhedronRepresentation(gctx, m_cfg.nSegments);
internalExtent.extend(sPolyhedron.extent(), support.constraints);
if (m_cfg.extent.has_value()) {
internalExtent = m_cfg.extent.value();
} else {
// Estimate the extent from the surfaces
for (const auto& s : internalSurfaces) {
auto sPolyhedron = s->polyhedronRepresentation(gctx, m_cfg.nSegments);
internalExtent.extend(sPolyhedron.extent(), support.constraints);
}
}
// Use the support bulder helper to add support surfaces
detail::SupportHelper::addSupport(
Expand All @@ -216,14 +263,22 @@ Acts::Experimental::LayerStructureBuilder::construct(
}

if (internalSurfaces.size() >= m_cfg.nMinimalSurfaces) {
if (m_cfg.binnings.empty()) {
// Copy as we might patch it with the surface extent
auto binnings = m_cfg.binnings;

if (binnings.empty()) {
ACTS_DEBUG(
"No surface binning provided, navigation will be 'tryAll' "
"(potentially slow).");
} else if (m_cfg.binnings.size() == 1u) {
} else if (binnings.size() == 1u) {
// Check if autorange for binning applies
if (m_cfg.extent.has_value()) {
ACTS_DEBUG("- adapting the proto binning range to the surface extent.");
adaptBinningRage(binnings, m_cfg.extent.value(), m_cfg.fullPhiBinning);
}
ACTS_DEBUG("- 1-dimensional surface binning detected.");
// Capture the binning
auto binning = m_cfg.binnings[0u];
auto binning = binnings[0u];
if (binning.boundaryType == Acts::detail::AxisBoundaryType::Closed) {
ACTS_VERBOSE("-- closed binning option.");
internalCandidatesUpdater =
Expand All @@ -235,11 +290,23 @@ Acts::Experimental::LayerStructureBuilder::construct(
createUpdater<Acts::detail::AxisBoundaryType::Bound>(
gctx, internalSurfaces, assignToAll, binning);
}
} else if (m_cfg.binnings.size() == 2u) {
} else if (binnings.size() == 2u) {
// Check if autorange for binning applies
if (m_cfg.extent.has_value()) {
ACTS_DEBUG(
"- adapting the proto binning range(s) to the surface extent.");
adaptBinningRage(binnings, m_cfg.extent.value(), m_cfg.fullPhiBinning);
}
// Sort the binning for conventions
std::sort(binnings.begin(), binnings.end(),
[](const ProtoBinning& a, const ProtoBinning& b) {
return a.binValue < b.binValue;
});

ACTS_DEBUG("- 2-dimensional surface binning detected.");
// Capture the binnings
const auto& binning0 = m_cfg.binnings[0u];
const auto& binning1 = m_cfg.binnings[1u];
const auto& binning0 = binnings[0u];
const auto& binning1 = binnings[1u];

if (binning0.boundaryType == Acts::detail::AxisBoundaryType::Closed) {
ACTS_VERBOSE("-- closed/bound binning option.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@
#pragma once

#include "Acts/MagneticField/MagneticFieldProvider.hpp"
#include "Acts/Plugins/DD4hep/DD4hepFieldAdapter.hpp"
#include "Acts/Plugins/DD4hep/DD4hepDetectorElement.hpp"
#include "Acts/Plugins/DD4hep/DD4hepDetectorStructure.hpp"
#include "ActsExamples/DD4hepDetector/DD4hepGeometryService.hpp"

#include <memory>
#include <tuple>
#include <utility>
#include <vector>

Expand All @@ -23,6 +25,10 @@ class Detector;
namespace Acts {
class TrackingGeometry;
class IMaterialDecorator;
class DD4hepFieldAdapter;
namespace Experimental {
class Detector;
} // namespace Experimental
} // namespace Acts

namespace ActsExamples {
Expand All @@ -33,20 +39,57 @@ namespace ActsExamples {
namespace DD4hep {

struct DD4hepDetector {
/// @brief The context decorators
using ContextDecorators =
std::vector<std::shared_ptr<ActsExamples::IContextDecorator>>;

/// @brief The tracking geometry
using TrackingGeometryPtr = std::shared_ptr<const Acts::TrackingGeometry>;

DD4hepDetector();
DD4hepDetector(std::shared_ptr<DD4hepGeometryService> geometryService);
~DD4hepDetector();
/// @brief The detector geometry
using DetectorPtr = std::shared_ptr<const Acts::Experimental::Detector>;

/// @brief Default constructor
DD4hepDetector() = default;
/// @brief Constructor from geometry service
/// @param _geometryService the geometry service
DD4hepDetector(std::shared_ptr<DD4hepGeometryService> _geometryService);
/// @brief Default destructor
~DD4hepDetector() = default;

std::shared_ptr<DD4hepGeometryService> geometryService;
/// @brief The DD4hep geometry service
std::shared_ptr<DD4hepGeometryService> geometryService = nullptr;

// @brief the compact file names
std::vector<std::string> compactFiles = {};

/// @brief Build the tracking geometry from the DD4hep geometry
///
/// @param config is the configuration of the geometry service
/// @param mdecorator is the material decorator provided
///
/// @return a pair of tracking geometry and context decorators
std::pair<TrackingGeometryPtr, ContextDecorators> finalize(
DD4hepGeometryService::Config config,
std::shared_ptr<const Acts::IMaterialDecorator> mdecorator);

/// @brief Build the detector from the DD4hep geometry
///
/// @param gctx is the geometry context
/// @param options is the options struct for the building process
///
/// @note the lifetime of the detector store has to exceed that of the
/// detector object as the converted surfaces point back to the
/// detector elements
///
/// @return a tuple of detector, context decorators, and the element store
std::tuple<DetectorPtr, ContextDecorators, Acts::DD4hepDetectorElement::Store>
finalize(
const Acts::GeometryContext& gctx,
const Acts::Experimental::DD4hepDetectorStructure::Options& options = {});

/// @brief Access to the DD4hep field
/// @return a shared pointer to the DD4hep field
std::shared_ptr<Acts::DD4hepFieldAdapter> field() const;
};

Expand Down
33 changes: 29 additions & 4 deletions Examples/Detectors/DD4hepDetector/src/DD4hepDetector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,20 @@
#include <cstddef>
#include <memory>
#include <stdexcept>
#include <string>

#include <DD4hep/DetElement.h>
#include <DD4hep/Detector.h>
#include <DD4hep/Fields.h>
#include <boost/program_options.hpp>

namespace ActsExamples {
namespace DD4hep {

DD4hepDetector::DD4hepDetector() = default;

DD4hepDetector::DD4hepDetector(
std::shared_ptr<DD4hepGeometryService> _geometryService)
: geometryService(std::move(_geometryService)) {}

DD4hepDetector::~DD4hepDetector() = default;

auto DD4hepDetector::finalize(
ActsExamples::DD4hep::DD4hepGeometryService::Config config,
std::shared_ptr<const Acts::IMaterialDecorator> mdecorator)
Expand All @@ -51,6 +50,32 @@ auto DD4hepDetector::finalize(
std::move(dd4tGeometry), std::move(dd4ContextDecorators));
}

auto DD4hepDetector::finalize(
const Acts::GeometryContext& gctx,
const Acts::Experimental::DD4hepDetectorStructure::Options& options)
-> std::tuple<DetectorPtr, ContextDecorators,
Acts::DD4hepDetectorElement::Store> {
if (geometryService == nullptr) {
throw std::runtime_error{
"No DD4hep geometry service configured, can not build "
"TrackingGeometry."};
}

auto world = geometryService->geometry();
// Build the detector structure
Acts::Experimental::DD4hepDetectorStructure dd4hepStructure(
Acts::getDefaultLogger("DD4hepDetectorStructure", options.logLevel));

/// @return a detector and the detector store
auto [detector, detectorElements] =
dd4hepStructure.construct(gctx, world, options);

// Prepare the return objects
ContextDecorators contextDecorators = {};

return {detector, contextDecorators, detectorElements};
}

std::shared_ptr<Acts::DD4hepFieldAdapter> DD4hepDetector::field() const {
const auto& detector = geometryService->detector();

Expand Down
Loading

0 comments on commit ff03f7d

Please sign in to comment.