From 8c91d41dfea66dc25b05a6aad1ba8b04c0966f13 Mon Sep 17 00:00:00 2001 From: Andreas Stefl Date: Wed, 4 Sep 2024 11:54:21 +0200 Subject: [PATCH 1/3] feat: Memory dense `MeasurementContainer` for Examples (#3528) - Implement a `MeasurementContainer` which packs the numbers densely - Implement proxy types to read and write from/to the container - Handle all downstream changes The motivation for this is to use the memory efficiently and potentially improve track finding performance in the Examples framework. blocked by - https://github.com/acts-project/acts/pull/3529 --- .../Acts/EventData/SubspaceHelpers.hpp | 34 +- .../Digitization/MeasurementCreation.hpp | 9 +- .../src/DigitizationAlgorithm.cpp | 5 +- .../Digitization/src/MeasurementCreation.cpp | 47 +- .../TrackFinding/src/HoughTransformSeeder.cpp | 3 +- .../TrackFinding/src/SpacePointMaker.cpp | 3 +- .../src/TrackFittingAlgorithm.cpp | 4 +- Examples/Framework/CMakeLists.txt | 1 + .../Framework/ML/src/NeuralCalibrator.cpp | 32 +- .../ActsExamples/EventData/Measurement.hpp | 630 ++++++++++++------ .../Framework/src/EventData/Measurement.cpp | 75 +++ .../src/EventData/MeasurementCalibration.cpp | 16 +- .../src/EventData/ScalingCalibrator.cpp | 32 +- Examples/Io/Csv/src/CsvMeasurementReader.cpp | 15 +- Examples/Io/Csv/src/CsvMeasurementWriter.cpp | 4 +- .../ActsExamples/Io/EDM4hep/EDM4hepUtil.hpp | 10 +- .../EDM4hep/src/EDM4hepMeasurementReader.cpp | 5 +- .../EDM4hep/src/EDM4hepMeasurementWriter.cpp | 3 +- Examples/Io/EDM4hep/src/EDM4hepUtil.cpp | 17 +- Examples/Io/Root/src/RootAthenaDumpReader.cpp | 2 +- .../Io/Root/src/RootMeasurementWriter.cpp | 8 +- .../Examples/EventData/MeasurementTests.cpp | 59 +- .../Io/Csv/MeasurementReaderWriterTests.cpp | 10 +- 23 files changed, 693 insertions(+), 331 deletions(-) create mode 100644 Examples/Framework/src/EventData/Measurement.cpp diff --git a/Core/include/Acts/EventData/SubspaceHelpers.hpp b/Core/include/Acts/EventData/SubspaceHelpers.hpp index 8a27e7d7208..49a59e1dae7 100644 --- a/Core/include/Acts/EventData/SubspaceHelpers.hpp +++ b/Core/include/Acts/EventData/SubspaceHelpers.hpp @@ -39,7 +39,7 @@ inline static bool checkSubspaceIndices(const Container& container, if (subspaceSize > fullSize) { return false; } - if (container.size() != subspaceSize) { + if (static_cast(container.size()) != subspaceSize) { return false; } for (auto it = container.begin(); it != container.end();) { @@ -115,6 +115,36 @@ class SubspaceHelperBase { auto begin() const { return self().begin(); } auto end() const { return self().end(); } + bool contains(std::uint8_t index) const { + return std::find(begin(), end(), index) != end(); + } + std::size_t indexOf(std::uint8_t index) const { + auto it = std::find(begin(), end(), index); + return it != end() ? std::distance(begin(), it) : kFullSize; + } + + template + ActsVector expandVector( + const Eigen::DenseBase& vector) const { + ActsVector result = ActsVector::Zero(); + for (auto [i, index] : enumerate(*this)) { + result(index) = vector(i); + } + return result; + } + + template + FullSquareMatrix expandMatrix( + const Eigen::DenseBase& matrix) const { + FullSquareMatrix result = FullSquareMatrix::Zero(); + for (auto [i, indexI] : enumerate(*this)) { + for (auto [j, indexJ] : enumerate(*this)) { + result(indexI, indexJ) = matrix(i, j); + } + } + return result; + } + FullSquareMatrix fullProjector() const { FullSquareMatrix result = FullSquareMatrix::Zero(); for (auto [i, index] : enumerate(*this)) { @@ -168,6 +198,7 @@ class VariableSubspaceHelper bool empty() const { return m_indices.empty(); } std::size_t size() const { return m_indices.size(); } + const Container& indices() const { return m_indices; } IndexType operator[](std::size_t i) const { return m_indices[i]; } @@ -215,6 +246,7 @@ class FixedSubspaceHelper bool empty() const { return m_indices.empty(); } std::size_t size() const { return m_indices.size(); } + const Container& indices() const { return m_indices; } IndexType operator[](std::uint32_t i) const { return m_indices[i]; } diff --git a/Examples/Algorithms/Digitization/include/ActsExamples/Digitization/MeasurementCreation.hpp b/Examples/Algorithms/Digitization/include/ActsExamples/Digitization/MeasurementCreation.hpp index 3d9476815c9..e168858cfd1 100644 --- a/Examples/Algorithms/Digitization/include/ActsExamples/Digitization/MeasurementCreation.hpp +++ b/Examples/Algorithms/Digitization/include/ActsExamples/Digitization/MeasurementCreation.hpp @@ -1,6 +1,6 @@ // This file is part of the Acts project. // -// Copyright (C) 2021 CERN for the benefit of the Acts project +// Copyright (C) 2021-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 @@ -40,9 +40,10 @@ struct DigitizedParameters { /// /// To be used also by the e I/O system /// -/// @return a variant measurement -Measurement createMeasurement(const DigitizedParameters& dParams, - const IndexSourceLink& isl) noexcept(false); +/// @return the measurement proxy +ActsExamples::VariableBoundMeasurementProxy createMeasurement( + MeasurementContainer& container, const DigitizedParameters& dParams, + const IndexSourceLink& isl) noexcept(false); /// Construct the constituents of a measurement. /// diff --git a/Examples/Algorithms/Digitization/src/DigitizationAlgorithm.cpp b/Examples/Algorithms/Digitization/src/DigitizationAlgorithm.cpp index f3b04ac0aff..3ac84f45bac 100644 --- a/Examples/Algorithms/Digitization/src/DigitizationAlgorithm.cpp +++ b/Examples/Algorithms/Digitization/src/DigitizationAlgorithm.cpp @@ -1,6 +1,6 @@ // This file is part of the Acts project. // -// Copyright (C) 2021 CERN for the benefit of the Acts project +// Copyright (C) 2021-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 @@ -268,8 +268,7 @@ ActsExamples::ProcessCode ActsExamples::DigitizationAlgorithm::execute( // be added at the end. sourceLinks.insert(sourceLinks.end(), sourceLink); - measurements.emplace_back( - createMeasurement(dParameters, sourceLink)); + createMeasurement(measurements, dParameters, sourceLink); clusters.emplace_back(std::move(dParameters.cluster)); // this digitization does hit merging so there can be more than one // mapping entry for each digitized hit. diff --git a/Examples/Algorithms/Digitization/src/MeasurementCreation.cpp b/Examples/Algorithms/Digitization/src/MeasurementCreation.cpp index 7338b431f8c..5a9aee23c70 100644 --- a/Examples/Algorithms/Digitization/src/MeasurementCreation.cpp +++ b/Examples/Algorithms/Digitization/src/MeasurementCreation.cpp @@ -1,6 +1,6 @@ // This file is part of the Acts project. // -// Copyright (C) 2021 CERN for the benefit of the Acts project +// Copyright (C) 2021-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 @@ -8,36 +8,35 @@ #include "ActsExamples/Digitization/MeasurementCreation.hpp" +#include "Acts/EventData/MeasurementHelpers.hpp" #include "Acts/EventData/SourceLink.hpp" #include "ActsExamples/EventData/IndexSourceLink.hpp" +#include "ActsExamples/EventData/Measurement.hpp" #include #include #include -ActsExamples::Measurement ActsExamples::createMeasurement( - const DigitizedParameters& dParams, const IndexSourceLink& isl) { +ActsExamples::VariableBoundMeasurementProxy ActsExamples::createMeasurement( + MeasurementContainer& container, const DigitizedParameters& dParams, + const IndexSourceLink& isl) { Acts::SourceLink sl{isl}; - switch (dParams.indices.size()) { - case 1u: { - auto [indices, par, cov] = measurementConstituents<1>(dParams); - return ActsExamples::Measurement(std::move(sl), indices, par, cov); - } - case 2u: { - auto [indices, par, cov] = measurementConstituents<2>(dParams); - return ActsExamples::Measurement(std::move(sl), indices, par, cov); - }; - case 3u: { - auto [indices, par, cov] = measurementConstituents<3>(dParams); - return ActsExamples::Measurement(std::move(sl), indices, par, cov); - }; - case 4u: { - auto [indices, par, cov] = measurementConstituents<4>(dParams); - return ActsExamples::Measurement(std::move(sl), indices, par, cov); - }; - default: - std::string errorMsg = "Invalid/mismatching measurement dimension: " + - std::to_string(dParams.indices.size()); - throw std::runtime_error(errorMsg.c_str()); + + if (dParams.indices.size() > 4u) { + std::string errorMsg = "Invalid/mismatching measurement dimension: " + + std::to_string(dParams.indices.size()); + throw std::runtime_error(errorMsg.c_str()); } + + return Acts::visit_measurement( + dParams.indices.size(), [&](auto dim) -> VariableBoundMeasurementProxy { + auto [indices, par, cov] = measurementConstituents(dParams); + FixedBoundMeasurementProxy measurement = + container.makeMeasurement(); + measurement.setSourceLink(sl); + measurement.setSubspaceIndices(indices); + measurement.parameters() = par; + measurement.covariance() = cov; + return measurement; + }); } diff --git a/Examples/Algorithms/TrackFinding/src/HoughTransformSeeder.cpp b/Examples/Algorithms/TrackFinding/src/HoughTransformSeeder.cpp index 25d9d7f2e25..3c16d146d65 100644 --- a/Examples/Algorithms/TrackFinding/src/HoughTransformSeeder.cpp +++ b/Examples/Algorithms/TrackFinding/src/HoughTransformSeeder.cpp @@ -529,7 +529,8 @@ void ActsExamples::HoughTransformSeeder::addMeasurements( // are transformed to the bound space where we do know their location. // if the local parameters are not measured, this results in a // zero location, which is a reasonable default fall-back. - const auto& measurement = measurements[sourceLink.index()]; + const ConstVariableBoundMeasurementProxy measurement = + measurements.getMeasurement(sourceLink.index()); assert(measurement.contains(Acts::eBoundLoc0) && "Measurement does not contain the required bound loc0"); diff --git a/Examples/Algorithms/TrackFinding/src/SpacePointMaker.cpp b/Examples/Algorithms/TrackFinding/src/SpacePointMaker.cpp index 49802b57087..0605e1b4bc4 100644 --- a/Examples/Algorithms/TrackFinding/src/SpacePointMaker.cpp +++ b/Examples/Algorithms/TrackFinding/src/SpacePointMaker.cpp @@ -126,7 +126,8 @@ ActsExamples::ProcessCode ActsExamples::SpacePointMaker::execute( spOpt.paramCovAccessor = [&measurements](Acts::SourceLink slink) { const auto islink = slink.get(); - const auto& meas = measurements[islink.index()]; + const ConstVariableBoundMeasurementProxy meas = + measurements.getMeasurement(islink.index()); return std::make_pair(meas.fullParameters(), meas.fullCovariance()); }; diff --git a/Examples/Algorithms/TrackFitting/src/TrackFittingAlgorithm.cpp b/Examples/Algorithms/TrackFitting/src/TrackFittingAlgorithm.cpp index f80f20570c5..0a352d71fe6 100644 --- a/Examples/Algorithms/TrackFitting/src/TrackFittingAlgorithm.cpp +++ b/Examples/Algorithms/TrackFitting/src/TrackFittingAlgorithm.cpp @@ -18,6 +18,7 @@ #include "Acts/Surfaces/PerigeeSurface.hpp" #include "Acts/Surfaces/Surface.hpp" #include "Acts/Utilities/Result.hpp" +#include "ActsExamples/EventData/Measurement.hpp" #include "ActsExamples/EventData/MeasurementCalibration.hpp" #include "ActsExamples/EventData/ProtoTrack.hpp" #include "ActsExamples/Framework/AlgorithmContext.hpp" @@ -126,7 +127,8 @@ ActsExamples::ProcessCode ActsExamples::TrackFittingAlgorithm::execute( // Fill the source links via their indices from the container for (auto hitIndex : protoTrack) { - const auto& measurement = measurements.at(hitIndex); + ConstVariableBoundMeasurementProxy measurement = + measurements.getMeasurement(hitIndex); trackSourceLinks.push_back(measurement.sourceLink()); } diff --git a/Examples/Framework/CMakeLists.txt b/Examples/Framework/CMakeLists.txt index bfe33f2e8e6..692db8f2f15 100644 --- a/Examples/Framework/CMakeLists.txt +++ b/Examples/Framework/CMakeLists.txt @@ -5,6 +5,7 @@ set(ActsExamplesFramework_SOURCES) add_library( ActsExamplesFramework SHARED + src/EventData/Measurement.cpp src/EventData/MeasurementCalibration.cpp src/EventData/ScalingCalibrator.cpp src/Framework/IAlgorithm.cpp diff --git a/Examples/Framework/ML/src/NeuralCalibrator.cpp b/Examples/Framework/ML/src/NeuralCalibrator.cpp index 77af3920a6a..54003b3bfc1 100644 --- a/Examples/Framework/ML/src/NeuralCalibrator.cpp +++ b/Examples/Framework/ML/src/NeuralCalibrator.cpp @@ -1,6 +1,6 @@ // This file is part of the Acts project. // -// Copyright (C) 2023 CERN for the benefit of the Acts project +// Copyright (C) 2023-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 @@ -106,7 +106,8 @@ void ActsExamples::NeuralCalibrator::calibrate( const Acts::Surface& referenceSurface = trackState.referenceSurface(); auto trackParameters = trackState.parameters(); - const auto& measurement = measurements[idxSourceLink.index()]; + const ConstVariableBoundMeasurementProxy measurement = + measurements.getMeasurement(idxSourceLink.index()); assert(measurement.contains(Acts::eBoundLoc0) && "Measurement does not contain the required bound loc0"); @@ -171,21 +172,24 @@ void ActsExamples::NeuralCalibrator::calibrate( std::size_t iLoc0 = m_nComponents + iMax * 2; std::size_t iVar0 = 3 * m_nComponents + iMax * 2; - Measurement measurementCopy = measurement; - measurementCopy.parameters()[boundLoc0] = output[iLoc0]; - measurementCopy.parameters()[boundLoc1] = output[iLoc0 + 1]; - measurementCopy.covariance()(boundLoc0, boundLoc0) = output[iVar0]; - measurementCopy.covariance()(boundLoc1, boundLoc1) = output[iVar0 + 1]; - Acts::visit_measurement(measurement.size(), [&](auto N) -> void { constexpr std::size_t kMeasurementSize = decltype(N)::value; + const ConstFixedBoundMeasurementProxy fixedMeasurement = + measurement; + + Acts::ActsVector calibratedParameters = + fixedMeasurement.parameters(); + Acts::ActsSquareMatrix calibratedCovariance = + fixedMeasurement.covariance(); + + calibratedParameters[boundLoc0] = output[iLoc0]; + calibratedParameters[boundLoc1] = output[iLoc0 + 1]; + calibratedCovariance(boundLoc0, boundLoc0) = output[iVar0]; + calibratedCovariance(boundLoc1, boundLoc1) = output[iVar0 + 1]; trackState.allocateCalibrated(kMeasurementSize); - trackState.calibrated() = - measurementCopy.parameters(); - trackState.calibratedCovariance() = - measurementCopy.covariance(); - trackState.setSubspaceIndices( - measurementCopy.subspaceIndices()); + trackState.calibrated() = calibratedParameters; + trackState.calibratedCovariance() = calibratedCovariance; + trackState.setSubspaceIndices(fixedMeasurement.subspaceIndices()); }); } diff --git a/Examples/Framework/include/ActsExamples/EventData/Measurement.hpp b/Examples/Framework/include/ActsExamples/EventData/Measurement.hpp index 87c4c546c08..31193f6818d 100644 --- a/Examples/Framework/include/ActsExamples/EventData/Measurement.hpp +++ b/Examples/Framework/include/ActsExamples/EventData/Measurement.hpp @@ -8,6 +8,7 @@ #pragma once +#include "Acts/Definitions/Algebra.hpp" #include "Acts/Definitions/TrackParametrization.hpp" #include "Acts/EventData/MeasurementHelpers.hpp" #include "Acts/EventData/SourceLink.hpp" @@ -28,253 +29,460 @@ namespace ActsExamples { -/// A measurement of a variable-size subspace of the full parameters. +template +class MeasurementProxyBase; +template +class FixedMeasurementProxy; +template +class VariableMeasurementProxy; + +template +using FixedBoundMeasurementProxy = + FixedMeasurementProxy; +template +using ConstFixedBoundMeasurementProxy = + FixedMeasurementProxy; +using VariableBoundMeasurementProxy = + VariableMeasurementProxy; +using ConstVariableBoundMeasurementProxy = + VariableMeasurementProxy; + +/// @brief A container to store and access measurements /// -/// @tparam indices_t Parameter index type, determines the full parameter space +/// This container stores measurements of different sizes and provides +/// access to them through fixed-size and variable-size proxies. /// -/// The measurement intentionally does not store a pointer/reference to the -/// reference object in the geometry hierarchy, i.e. the surface or volume. The -/// reference object can already be identified via the geometry identifier -/// provided by the source link. Since a measurement **must** be anchored within -/// the geometry hierarchy, all measurement surfaces and volumes **must** -/// provide valid geometry identifiers. In all use-cases, e.g. Kalman filtering, -/// a pointer/reference to the reference object is available before the -/// measurement is accessed; e.g. the propagator provides the surface pointer -/// during navigation, which is then used to lookup possible measurements. -/// -/// The pointed-to geometry object would differ depending on the parameter type. -/// This means either, that there needs to be an additional variable type or -/// that a pointer to a base object is stored (requiring a `dynamic_cast` later -/// on). Both variants add additional complications. Since the geometry object -/// is not required anyway (as discussed above), not storing it removes all -/// these complications altogether. -template -class VariableSizeMeasurement { +/// The measurements are stored densely in a flat buffer and the proxies +/// provide access to the individual measurements. +class MeasurementContainer { public: - static constexpr std::size_t kFullSize = - Acts::detail::kParametersSize; - - using Scalar = Acts::ActsScalar; + using Index = std::size_t; + template + using FixedProxy = FixedMeasurementProxy; + template + using ConstFixedProxy = FixedMeasurementProxy; + using VariableProxy = VariableMeasurementProxy; + using ConstVariableProxy = VariableMeasurementProxy; + + MeasurementContainer(); + + /// @brief Get the number of measurements + /// @return The number of measurements + std::size_t size() const; + + /// @brief Reserve space for a number of measurements + /// @param size The number of measurements to reserve space for + void reserve(std::size_t size); + + /// @brief Add a measurement of a given size + /// @param size The size of the measurement + /// @return The index of the added measurement + Index addMeasurement(std::uint8_t size); + + /// @brief Get a variable-size measurement proxy + /// @param index The index of the measurement + /// @return The variable-size measurement proxy + VariableProxy getMeasurement(Index index); + /// @brief Get a const variable-size measurement proxy + /// @param index The index of the measurement + /// @return The const variable-size measurement proxy + ConstVariableProxy getMeasurement(Index index) const; + + /// @brief Get a fixed-size measurement proxy + /// @tparam Size The size of the measurement + /// @param index The index of the measurement + /// @return The fixed-size measurement proxy + template + FixedProxy getMeasurement(Index index) { + return FixedProxy{*this, index}; + } + /// @brief Get a const fixed-size measurement proxy + /// @tparam Size The size of the measurement + /// @param index The index of the measurement + /// @return The const fixed-size measurement proxy + template + ConstFixedProxy getMeasurement(Index index) const { + return ConstFixedProxy{*this, index}; + } - using SubspaceIndex = std::uint8_t; - using SubspaceIndices = - boost::container::static_vector; - - /// Vector type containing for measured parameter values. - template - using ParametersVector = Eigen::Matrix; - template - using ParametersVectorMap = Eigen::Map>; - template - using ConstParametersVectorMap = Eigen::Map>; - using EffectiveParametersVector = Eigen::Matrix; - using EffectiveParametersVectorMap = Eigen::Map; - using ConstEffectiveParametersVectorMap = - Eigen::Map; - - /// Matrix type for the measurement covariance. - template - using CovarianceMatrix = Eigen::Matrix; - template - using CovarianceMatrixMap = Eigen::Map>; - template - using ConstCovarianceMatrixMap = Eigen::Map>; - using EffectiveCovarianceMatrix = - Eigen::Matrix; - using EffectiveCovarianceMatrixMap = Eigen::Map; - using ConstEffectiveCovarianceMatrixMap = - Eigen::Map; - - using FullParametersVector = Acts::ActsVector; - using FullCovarianceMatrix = Acts::ActsSquareMatrix; - - using ProjectionMatrix = Eigen::Matrix; - using ExpansionMatrix = Eigen::Matrix; - - /// Construct from source link, subset indices, and measured data. - /// - /// @tparam parameters_t Input parameters vector type - /// @tparam covariance_t Input covariance matrix type - /// @param source The link that connects to the underlying detector readout - /// @param subspaceIndices Which parameters are measured - /// @param params Measured parameters values - /// @param cov Measured parameters covariance - /// - /// @note The indices must be ordered and must describe/match the content - /// of parameters and covariance. - template - VariableSizeMeasurement( - Acts::SourceLink source, - const std::array& subspaceIndices, - const Eigen::MatrixBase& params, - const Eigen::MatrixBase& cov) - : m_source(std::move(source)) { - static_assert(kSize == parameters_t::RowsAtCompileTime, - "Parameter size mismatch"); - static_assert(kSize == covariance_t::RowsAtCompileTime, - "Covariance rows mismatch"); - static_assert(kSize == covariance_t::ColsAtCompileTime, - "Covariance cols mismatch"); - - m_subspaceIndices.resize(subspaceIndices.size()); - std::transform(subspaceIndices.begin(), subspaceIndices.end(), - m_subspaceIndices.begin(), [](auto index) { - return static_cast(index); - }); - - parameters() = params; - covariance() = cov; + /// @brief Make a measurement of a given size + /// @param size The size of the measurement + /// @return The variable-size measurement proxy + VariableProxy makeMeasurement(std::uint8_t size); + /// @brief Make a fixed-size measurement + /// @tparam Size The size of the measurement + /// @return The fixed-size measurement proxy + template + FixedProxy makeMeasurement() { + return getMeasurement(addMeasurement(Size)); } - /// A measurement can only be constructed with valid parameters. - VariableSizeMeasurement() = delete; - VariableSizeMeasurement(const VariableSizeMeasurement&) = default; - VariableSizeMeasurement(VariableSizeMeasurement&&) = default; - ~VariableSizeMeasurement() = default; - VariableSizeMeasurement& operator=(const VariableSizeMeasurement&) = default; - VariableSizeMeasurement& operator=(VariableSizeMeasurement&&) = default; - /// Source link that connects to the underlying detector readout. - const Acts::SourceLink& sourceLink() const { return m_source; } + template + class IteratorImpl { + public: + using value_type = + std::conditional_t; + using reference = value_type; + using pointer = value_type*; + using difference_type = std::ptrdiff_t; + using iterator_category = std::forward_iterator_tag; + + using Container = std::conditional_t; + + IteratorImpl(Container& container, std::size_t index) + : m_container(container), m_index(index) {} - constexpr std::size_t size() const { return m_subspaceIndices.size(); } + reference operator*() const { return m_container.getMeasurement(m_index); } + + pointer operator->() const { return &operator*(); } + + IteratorImpl& operator++() { + ++m_index; + return *this; + } + + IteratorImpl operator++(int) { + auto copy = *this; + ++*this; + return copy; + } + + bool operator==(const IteratorImpl& other) const { + return m_index == other.m_index; + } - /// Check if a specific parameter is part of this measurement. + bool operator!=(const IteratorImpl& other) const { + return !(*this == other); + } + + private: + Container& m_container; + Index m_index; + }; + + using iterator = IteratorImpl; + using const_iterator = IteratorImpl; + + iterator begin(); + iterator end(); + const_iterator begin() const; + const_iterator end() const; + const_iterator cbegin() const; + const_iterator cend() const; + + public: + struct MeasurementEntry { + std::size_t subspaceIndexOffset{}; + std::size_t parameterOffset{}; + std::size_t covarianceOffset{}; + std::uint8_t size{}; + }; + + std::vector m_entries; + + std::vector> m_sourceLinks; + std::vector m_subspaceIndices; + std::vector m_parameters; + std::vector m_covariances; +}; + +/// @brief Base class for measurement proxies +/// +/// This class provides common functionality for fixed-size and variable-size +/// measurement proxies. +/// +/// @tparam Derived The derived measurement proxy class +/// @tparam FullSize The full size of the measurement +/// @tparam ReadOnly Whether the proxy is read-only +template +class MeasurementProxyBase { + public: + using Index = MeasurementContainer::Index; + using SubspaceIndex = std::uint8_t; + using Scalar = double; + + using FullVector = Acts::ActsVector; + using FullSquareMatrix = Acts::ActsSquareMatrix; + + using Container = std::conditional_t; + + MeasurementProxyBase(Container& container_, Index index_) + : m_container(&container_), m_index(index_) {} + template + MeasurementProxyBase( + const MeasurementProxyBase& other) + requires(ReadOnly == OtherReadOnly || ReadOnly) + : m_container(&other.container()), m_index(other.index()) {} + + /// @brief Get the container of the measurement + /// @return The container of the measurement + Container& container() const { return *m_container; } + /// @brief Get the index of the measurement + /// @return The index of the measurement + Index index() const { return m_index; } + + /// @brief Get the size of the measurement + /// @return The size of the measurement + std::size_t size() const { return container().m_entries.at(m_index).size; } + + /// @brief Check if the measurement contains a subspace index + /// @param i The subspace index + /// @return True if the measurement contains the subspace index + template bool contains(indices_t i) const { - return std::find(m_subspaceIndices.begin(), m_subspaceIndices.end(), i) != - m_subspaceIndices.end(); + return self().subspaceHelper().contains(i); } + /// @brief Get the index of a subspace index in the measurement + /// @param i The subspace index + /// @return The index of the subspace index in the measurement + template std::size_t indexOf(indices_t i) const { - auto it = std::find(m_subspaceIndices.begin(), m_subspaceIndices.end(), i); - assert(it != m_subspaceIndices.end()); - return std::distance(m_subspaceIndices.begin(), it); + return self().subspaceHelper().indexOf(i); } - /// The measurement indices - const SubspaceIndices& subspaceIndices() const { return m_subspaceIndices; } + /// @brief Set the source link of the measurement + /// @param sourceLink The source link + void setSourceLink(const Acts::SourceLink& sourceLink) + requires(!ReadOnly) + { + container().m_sourceLinks.at(m_index) = sourceLink; + } - template - Acts::SubspaceIndices subspaceIndices() const { - assert(dim == size()); - Acts::SubspaceIndices result; - std::copy(m_subspaceIndices.begin(), m_subspaceIndices.end(), - result.begin()); - return result; + /// @brief Get the source link of the measurement + /// @return The source link + const Acts::SourceLink& sourceLink() const { + return container().m_sourceLinks.at(m_index).value(); } - Acts::BoundSubspaceIndices boundSubsetIndices() const - requires(std::is_same_v) + /// @brief Set the subspace indices of the measurement + /// @param indices The subspace indices + template + void setSubspaceIndices(const IndexContainer& indices) + requires(!ReadOnly) { - Acts::BoundSubspaceIndices result = Acts::kBoundSubspaceIndicesInvalid; - std::copy(m_subspaceIndices.begin(), m_subspaceIndices.end(), - result.begin()); - return result; + assert(checkSubspaceIndices(indices, FullSize, size()) && + "Invalid indices"); + std::transform(indices.begin(), indices.end(), + self().subspaceIndexVector().begin(), + [](auto index) { return static_cast(index); }); } - template - ConstParametersVectorMap parameters() const { - assert(dim == size()); - return ConstParametersVectorMap{m_params.data()}; + /// @brief Get the measurement as a full-size vector + /// @return The full-size measurement vector + FullVector fullParameters() const { + return self().subspaceHelper().expandVector(self().parameters()); } - template - ParametersVectorMap parameters() { - assert(dim == size()); - return ParametersVectorMap{m_params.data()}; - } - ConstEffectiveParametersVectorMap parameters() const { - return ConstEffectiveParametersVectorMap{m_params.data(), - static_cast(size())}; + + /// @brief Get the covariance as a full-size square matrix + /// @return The full-size covariance matrix + FullSquareMatrix fullCovariance() const { + return self().subspaceHelper().expandMatrix(self().covariance()); } - EffectiveParametersVectorMap parameters() { - return EffectiveParametersVectorMap{m_params.data(), - static_cast(size())}; + + /// @brief Copy the data from another measurement + /// @tparam OtherDerived The derived measurement proxy class of the other + /// measurement + /// @param other The other measurement proxy + template + void copyFrom(const OtherDerived& other) + requires(!ReadOnly) + { + assert(size() == other.size() && "Size mismatch"); + setSourceLink(other.sourceLink()); + self().subspaceIndexVector() = other.subspaceIndexVector(); + self().parameters() = other.parameters(); + self().covariance() = other.covariance(); } - template - ConstCovarianceMatrixMap covariance() const { - assert(dim == size()); - return ConstCovarianceMatrixMap{m_cov.data()}; + protected: + Derived& self() { return static_cast(*this); } + const Derived& self() const { return static_cast(*this); } + + Container* m_container; + Index m_index; +}; + +/// @brief Fixed-size measurement proxy +/// +/// This class provides access to a fixed-size measurement in a measurement +/// container. +/// +/// @tparam FullSize The full size of the measurement +/// @tparam Size The size of the measurement +/// @tparam ReadOnly Whether the proxy is read-only +template +class FixedMeasurementProxy + : public MeasurementProxyBase< + FixedMeasurementProxy, FullSize, ReadOnly> { + public: + using Base = + MeasurementProxyBase, + FullSize, ReadOnly>; + using Index = typename Base::Index; + using SubspaceIndex = typename Base::SubspaceIndex; + using Scalar = typename Base::Scalar; + using Container = typename Base::Container; + + using SubspaceHelper = Acts::FixedSubspaceHelper; + + using SubspaceVector = Eigen::Matrix; + using SubspaceVectorMap = + std::conditional_t, + Eigen::Map>; + + using ParametersVector = Eigen::Matrix; + using ParametersVectorMap = + std::conditional_t, + Eigen::Map>; + + using CovarianceMatrix = Eigen::Matrix; + using CovarianceMatrixMap = + std::conditional_t, + Eigen::Map>; + + FixedMeasurementProxy(Container& container_, Index index_) + : Base(container_, index_) { + assert(container().m_entries.at(index()).size == Size && "Size mismatch"); } - template - CovarianceMatrixMap covariance() { - assert(dim == size()); - return CovarianceMatrixMap{m_cov.data()}; + template + FixedMeasurementProxy( + const MeasurementProxyBase& other) + requires(ReadOnly == OtherReadOnly || ReadOnly) + : Base(other) { + assert(container().m_entries.at(index()).size == Size && "Size mismatch"); } - ConstEffectiveCovarianceMatrixMap covariance() const { - return ConstEffectiveCovarianceMatrixMap{m_cov.data(), - static_cast(size()), - static_cast(size())}; + + using Base::container; + using Base::index; + + /// @brief Get the size of the measurement + /// @return The size of the measurement + static constexpr std::size_t size() { return Size; } + + /// @brief Get the subspace helper for the measurement + /// @return The subspace helper + SubspaceHelper subspaceHelper() const { + return SubspaceHelper{subspaceIndexVector()}; } - EffectiveCovarianceMatrixMap covariance() { - return EffectiveCovarianceMatrixMap{m_cov.data(), - static_cast(size()), - static_cast(size())}; + + /// @brief Get the subspace indices of the measurement + /// @return The subspace indices + Acts::SubspaceIndices subspaceIndices() const { + return subspaceHelper().indices(); } - FullParametersVector fullParameters() const { - FullParametersVector result = FullParametersVector::Zero(); - for (std::size_t i = 0; i < size(); ++i) { - result[m_subspaceIndices[i]] = parameters()[i]; - } - return result; + /// @brief Get the subspace index vector of the measurement + /// @return The subspace index vector + SubspaceVectorMap subspaceIndexVector() const { + return SubspaceVectorMap{ + container().m_subspaceIndices.data() + + container().m_entries.at(index()).subspaceIndexOffset}; } - FullCovarianceMatrix fullCovariance() const { - FullCovarianceMatrix result = FullCovarianceMatrix::Zero(); - for (std::size_t i = 0; i < size(); ++i) { - for (std::size_t j = 0; j < size(); ++j) { - result(m_subspaceIndices[i], m_subspaceIndices[j]) = covariance()(i, j); - } - } - return result; + /// @brief Get the parameters of the measurement + /// @return The parameters + ParametersVectorMap parameters() const { + return ParametersVectorMap{ + container().m_parameters.data() + + container().m_entries.at(index()).parameterOffset}; } - private: - Acts::SourceLink m_source; - SubspaceIndices m_subspaceIndices; - std::array m_params{}; - std::array m_cov{}; + /// @brief Get the covariance of the measurement + /// @return The covariance + CovarianceMatrixMap covariance() const { + return CovarianceMatrixMap{ + container().m_covariances.data() + + container().m_entries.at(index()).covarianceOffset}; + } }; -/// Construct a variable-size measurement for the given indices. -/// -/// @tparam parameters_t Input parameters vector type -/// @tparam covariance_t Input covariance matrix type -/// @tparam indices_t Parameter index type, determines the full parameter space -/// @tparam tail_indices_t Helper types required to support variadic arguments; -/// all types must be convertibale to `indices_t`. -/// @param source The link that connects to the underlying detector readout -/// @param params Measured parameters values -/// @param cov Measured parameters covariance -/// @param index0 Required parameter index, measurement must be at least 1d -/// @param tailIndices Additional parameter indices for larger measurements -/// @return Variable-size measurement w/ the correct type and given inputs +/// @brief Variable-size measurement proxy /// -/// @note The indices must be ordered and must be consistent with the content of -/// parameters and covariance. -template -VariableSizeMeasurement makeVariableSizeMeasurement( - Acts::SourceLink source, const Eigen::MatrixBase& params, - const Eigen::MatrixBase& cov, indices_t index0, - tail_indices_t... tailIndices) { - using IndexContainer = std::array; - return {std::move(source), IndexContainer{index0, tailIndices...}, params, - cov}; -} - -/// Type that can hold all possible bound measurements. -using BoundVariableMeasurement = VariableSizeMeasurement; - -/// Variable measurement type that can contain all possible combinations. -using Measurement = BoundVariableMeasurement; - -/// Container of measurements. +/// This class provides access to a variable-size measurement in a measurement +/// container. /// -/// In contrast to the source links, the measurements themself must not be -/// orderable. The source links stored in the measurements are treated -/// as opaque here and no ordering is enforced on the stored measurements. -using MeasurementContainer = std::vector; +/// @tparam FullSize The full size of the measurement +/// @tparam ReadOnly Whether the proxy is read-only +template +class VariableMeasurementProxy + : public MeasurementProxyBase, + FullSize, ReadOnly> { + public: + using Base = + MeasurementProxyBase, + FullSize, ReadOnly>; + using Index = typename Base::Index; + using SubspaceIndex = typename Base::SubspaceIndex; + using Scalar = typename Base::Scalar; + using Container = typename Base::Container; + + using SubspaceHelper = Acts::VariableSubspaceHelper; + + using SubspaceVector = Eigen::Matrix; + using SubspaceVectorMap = + std::conditional_t, + Eigen::Map>; + + using ParametersVector = Eigen::Matrix; + using ParametersVectorMap = + std::conditional_t, + Eigen::Map>; + + using CovarianceMatrix = + Eigen::Matrix; + using CovarianceMatrixMap = + std::conditional_t, + Eigen::Map>; + + VariableMeasurementProxy(Container& container_, Index index_) + : Base(container_, index_) {} + template + VariableMeasurementProxy( + const MeasurementProxyBase& other) + requires(ReadOnly == OtherReadOnly || ReadOnly) + : Base(other) {} + + using Base::container; + using Base::index; + + /// @brief Get the subspace helper for the measurement + /// @return The subspace helper + SubspaceHelper subspaceHelper() const { + return SubspaceHelper{subspaceIndexVector()}; + } + + /// @brief Get the subspace indices of the measurement + /// @return The subspace indices + SubspaceVectorMap subspaceIndexVector() const { + const auto size = static_cast(this->size()); + return SubspaceVectorMap{ + container().m_subspaceIndices.data() + + container().m_entries.at(index()).subspaceIndexOffset, + size}; + } + + /// @brief Get the parameters of the measurement + /// @return The parameters + ParametersVectorMap parameters() const { + const auto size = static_cast(this->size()); + return ParametersVectorMap{ + container().m_parameters.data() + + container().m_entries.at(index()).parameterOffset, + size}; + } + + /// @brief Get the covariance of the measurement + /// @return The covariance + CovarianceMatrixMap covariance() const { + const auto size = static_cast(this->size()); + return CovarianceMatrixMap{ + container().m_covariances.data() + + container().m_entries.at(index()).covarianceOffset, + size, size}; + } +}; } // namespace ActsExamples diff --git a/Examples/Framework/src/EventData/Measurement.cpp b/Examples/Framework/src/EventData/Measurement.cpp new file mode 100644 index 00000000000..d9ae3520d86 --- /dev/null +++ b/Examples/Framework/src/EventData/Measurement.cpp @@ -0,0 +1,75 @@ +// 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/. + +#include "ActsExamples/EventData/Measurement.hpp" + +namespace ActsExamples { + +MeasurementContainer::MeasurementContainer() = default; + +std::size_t MeasurementContainer::size() const { + return m_entries.size(); +} + +void MeasurementContainer::reserve(std::size_t size) { + m_sourceLinks.reserve(size); + m_subspaceIndices.reserve(size * 2); + m_parameters.reserve(size * 2); + m_covariances.reserve(size * 2 * 2); +} + +std::size_t MeasurementContainer::addMeasurement(std::uint8_t size) { + m_entries.push_back({m_subspaceIndices.size(), m_parameters.size(), + m_covariances.size(), size}); + m_sourceLinks.emplace_back(); + m_subspaceIndices.resize(m_subspaceIndices.size() + size); + m_parameters.resize(m_parameters.size() + size); + m_covariances.resize(m_covariances.size() + size * size); + return m_entries.size() - 1; +} + +MeasurementContainer::VariableProxy MeasurementContainer::getMeasurement( + std::size_t index) { + return VariableProxy{*this, index}; +} + +MeasurementContainer::ConstVariableProxy MeasurementContainer::getMeasurement( + std::size_t index) const { + return ConstVariableProxy{*this, index}; +} + +MeasurementContainer::VariableProxy MeasurementContainer::makeMeasurement( + std::uint8_t size) { + return getMeasurement(addMeasurement(size)); +} + +MeasurementContainer::iterator MeasurementContainer::begin() { + return iterator{*this, 0}; +} + +MeasurementContainer::iterator MeasurementContainer::end() { + return iterator{*this, m_entries.size()}; +} + +MeasurementContainer::const_iterator MeasurementContainer::begin() const { + return const_iterator{*this, 0}; +} + +MeasurementContainer::const_iterator MeasurementContainer::end() const { + return const_iterator{*this, m_entries.size()}; +} + +MeasurementContainer::const_iterator MeasurementContainer::cbegin() const { + return const_iterator{*this, 0}; +} + +MeasurementContainer::const_iterator MeasurementContainer::cend() const { + return const_iterator{*this, m_entries.size()}; +} + +} // namespace ActsExamples diff --git a/Examples/Framework/src/EventData/MeasurementCalibration.cpp b/Examples/Framework/src/EventData/MeasurementCalibration.cpp index 560593dbd1f..84b1c5a2d45 100644 --- a/Examples/Framework/src/EventData/MeasurementCalibration.cpp +++ b/Examples/Framework/src/EventData/MeasurementCalibration.cpp @@ -6,11 +6,12 @@ // 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 "ActsExamples/EventData/MeasurementCalibration.hpp" + #include "Acts/Definitions/TrackParametrization.hpp" #include "Acts/EventData/SourceLink.hpp" #include "ActsExamples/EventData/IndexSourceLink.hpp" #include "ActsExamples/EventData/Measurement.hpp" -#include #include #include @@ -31,18 +32,19 @@ void ActsExamples::PassThroughCalibrator::calibrate( assert((idxSourceLink.index() < measurements.size()) && "Source link index is outside the container bounds"); - const auto& measurement = measurements[idxSourceLink.index()]; + const ConstVariableBoundMeasurementProxy measurement = + measurements.getMeasurement(idxSourceLink.index()); Acts::visit_measurement(measurement.size(), [&](auto N) -> void { constexpr std::size_t kMeasurementSize = decltype(N)::value; + const ConstFixedBoundMeasurementProxy fixedMeasurement = + measurement; trackState.allocateCalibrated(kMeasurementSize); - trackState.calibrated() = - measurement.parameters(); + trackState.calibrated() = fixedMeasurement.parameters(); trackState.calibratedCovariance() = - measurement.covariance(); - trackState.setSubspaceIndices( - measurement.subspaceIndices()); + fixedMeasurement.covariance(); + trackState.setSubspaceIndices(fixedMeasurement.subspaceIndices()); }); } diff --git a/Examples/Framework/src/EventData/ScalingCalibrator.cpp b/Examples/Framework/src/EventData/ScalingCalibrator.cpp index 805d8cdef15..183dc3cb7d0 100644 --- a/Examples/Framework/src/EventData/ScalingCalibrator.cpp +++ b/Examples/Framework/src/EventData/ScalingCalibrator.cpp @@ -1,6 +1,6 @@ // This file is part of the Acts project. // -// Copyright (C) 2023 CERN for the benefit of the Acts project +// Copyright (C) 2023-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 @@ -151,7 +151,8 @@ void ActsExamples::ScalingCalibrator::calibrate( const Cluster& cl = clusters->at(idxSourceLink.index()); ConstantTuple ct = m_calib_maps.at(mgid).at(cl.sizeLoc0, cl.sizeLoc1); - const auto& measurement = measurements[idxSourceLink.index()]; + const ConstVariableBoundMeasurementProxy measurement = + measurements.getMeasurement(idxSourceLink.index()); assert(measurement.contains(Acts::eBoundLoc0) && "Measurement does not contain the required bound loc0"); @@ -161,21 +162,24 @@ void ActsExamples::ScalingCalibrator::calibrate( auto boundLoc0 = measurement.indexOf(Acts::eBoundLoc0); auto boundLoc1 = measurement.indexOf(Acts::eBoundLoc1); - Measurement measurementCopy = measurement; - measurementCopy.parameters()[boundLoc0] += ct.x_offset; - measurementCopy.parameters()[boundLoc1] += ct.y_offset; - measurementCopy.covariance()(boundLoc0, boundLoc0) *= ct.x_scale; - measurementCopy.covariance()(boundLoc1, boundLoc1) *= ct.y_scale; - Acts::visit_measurement(measurement.size(), [&](auto N) -> void { constexpr std::size_t kMeasurementSize = decltype(N)::value; + const ConstFixedBoundMeasurementProxy fixedMeasurement = + measurement; + + Acts::ActsVector calibratedParameters = + fixedMeasurement.parameters(); + Acts::ActsSquareMatrix calibratedCovariance = + fixedMeasurement.covariance(); + + calibratedParameters[boundLoc0] += ct.x_offset; + calibratedParameters[boundLoc1] += ct.y_offset; + calibratedCovariance(boundLoc0, boundLoc0) *= ct.x_scale; + calibratedCovariance(boundLoc1, boundLoc1) *= ct.y_scale; trackState.allocateCalibrated(kMeasurementSize); - trackState.calibrated() = - measurementCopy.parameters(); - trackState.calibratedCovariance() = - measurementCopy.covariance(); - trackState.setSubspaceIndices( - measurementCopy.subspaceIndices()); + trackState.calibrated() = calibratedParameters; + trackState.calibratedCovariance() = calibratedCovariance; + trackState.setSubspaceIndices(fixedMeasurement.subspaceIndices()); }); } diff --git a/Examples/Io/Csv/src/CsvMeasurementReader.cpp b/Examples/Io/Csv/src/CsvMeasurementReader.cpp index e1c31d52b63..ee036ea86a9 100644 --- a/Examples/Io/Csv/src/CsvMeasurementReader.cpp +++ b/Examples/Io/Csv/src/CsvMeasurementReader.cpp @@ -193,11 +193,14 @@ ActsExamples::ProcessCode ActsExamples::CsvMeasurementReader::read( readMeasurementsByGeometryId(m_cfg.inputDir, ctx.eventNumber); // Prepare containers for the hit data using the framework event data types - GeometryIdMultimap orderedMeasurements; + MeasurementContainer tmpMeasurements; + GeometryIdMultimap orderedMeasurements; IndexMultimap measurementSimHitsMap; IndexSourceLinkContainer sourceLinks; // need list here for stable addresses std::list sourceLinkStorage; + + tmpMeasurements.reserve(measurementData.size()); orderedMeasurements.reserve(measurementData.size()); // Safe long as we have single particle to sim hit association measurementSimHitsMap.reserve(measurementData.size()); @@ -251,14 +254,15 @@ ActsExamples::ProcessCode ActsExamples::CsvMeasurementReader::read( // the measurement will be stored is known before adding it. const Index index = orderedMeasurements.size(); IndexSourceLink& sourceLink = sourceLinkStorage.emplace_back(geoId, index); - auto measurement = createMeasurement(dParameters, sourceLink); + auto measurement = + createMeasurement(tmpMeasurements, dParameters, sourceLink); // Due to the previous sorting of the raw hit data by geometry id, new // measurements should always end up at the end of the container. previous // elements were not touched; cluster indices remain stable and can // be used to identify the m. - auto inserted = orderedMeasurements.emplace_hint( - orderedMeasurements.end(), geoId, std::move(measurement)); + auto inserted = orderedMeasurements.emplace_hint(orderedMeasurements.end(), + geoId, measurement); if (std::next(inserted) != orderedMeasurements.end()) { ACTS_FATAL("Something went horribly wrong with the hit sorting"); return ProcessCode::ABORT; @@ -269,7 +273,8 @@ ActsExamples::ProcessCode ActsExamples::CsvMeasurementReader::read( MeasurementContainer measurements; for (auto& [_, meas] : orderedMeasurements) { - measurements.emplace_back(std::move(meas)); + auto measurement = measurements.makeMeasurement(meas.size()); + measurement.copyFrom(meas); } // Generate measurement-particles-map diff --git a/Examples/Io/Csv/src/CsvMeasurementWriter.cpp b/Examples/Io/Csv/src/CsvMeasurementWriter.cpp index 0f65fca0d20..f31c812ba46 100644 --- a/Examples/Io/Csv/src/CsvMeasurementWriter.cpp +++ b/Examples/Io/Csv/src/CsvMeasurementWriter.cpp @@ -14,6 +14,7 @@ #include "ActsExamples/EventData/Cluster.hpp" #include "ActsExamples/EventData/Index.hpp" #include "ActsExamples/EventData/IndexSourceLink.hpp" +#include "ActsExamples/EventData/Measurement.hpp" #include "ActsExamples/Framework/AlgorithmContext.hpp" #include "ActsExamples/Io/Csv/CsvInputOutput.hpp" #include "ActsExamples/Utilities/Paths.hpp" @@ -92,7 +93,8 @@ ActsExamples::ProcessCode ActsExamples::CsvMeasurementWriter::writeT( << " measurements in this event."); for (Index measIdx = 0u; measIdx < measurements.size(); ++measIdx) { - const auto& measurement = measurements[measIdx]; + const ConstVariableBoundMeasurementProxy measurement = + measurements.getMeasurement(measIdx); auto simHitIndices = makeRange(measurementSimHitsMap.equal_range(measIdx)); for (auto [_, simHitIdx] : simHitIndices) { diff --git a/Examples/Io/EDM4hep/include/ActsExamples/Io/EDM4hep/EDM4hepUtil.hpp b/Examples/Io/EDM4hep/include/ActsExamples/Io/EDM4hep/EDM4hepUtil.hpp index 432f5a28323..6e6bac7a154 100644 --- a/Examples/Io/EDM4hep/include/ActsExamples/Io/EDM4hep/EDM4hepUtil.hpp +++ b/Examples/Io/EDM4hep/include/ActsExamples/Io/EDM4hep/EDM4hepUtil.hpp @@ -91,10 +91,10 @@ void writeSimHit(const ActsFatras::Hit& from, edm4hep::MutableSimTrackerHit to, /// Known issues: /// - cluster channels are read from inappropriate fields /// - local 2D coordinates and time are read from position -Measurement readMeasurement(const edm4hep::TrackerHitPlane& from, - const edm4hep::TrackerHitCollection* fromClusters, - Cluster* toCluster, - const MapGeometryIdFrom& geometryMapper); +VariableBoundMeasurementProxy readMeasurement( + MeasurementContainer& container, const edm4hep::TrackerHitPlane& from, + const edm4hep::TrackerHitCollection* fromClusters, Cluster* toCluster, + const MapGeometryIdFrom& geometryMapper); /// Writes a measurement cluster to EDM4hep. /// @@ -106,7 +106,7 @@ Measurement readMeasurement(const edm4hep::TrackerHitPlane& from, /// Known issues: /// - cluster channels are written to inappropriate fields /// - local 2D coordinates and time are written to position -void writeMeasurement(const Measurement& from, +void writeMeasurement(const ConstVariableBoundMeasurementProxy& from, edm4hep::MutableTrackerHitPlane to, const Cluster* fromCluster, edm4hep::TrackerHitCollection& toClusters, diff --git a/Examples/Io/EDM4hep/src/EDM4hepMeasurementReader.cpp b/Examples/Io/EDM4hep/src/EDM4hepMeasurementReader.cpp index f24e64ccd8e..19606206e0e 100644 --- a/Examples/Io/EDM4hep/src/EDM4hepMeasurementReader.cpp +++ b/Examples/Io/EDM4hep/src/EDM4hepMeasurementReader.cpp @@ -66,11 +66,10 @@ ProcessCode EDM4hepMeasurementReader::read(const AlgorithmContext& ctx) { for (const auto& trackerHitPlane : trackerHitPlaneCollection) { Cluster cluster; - auto measurement = EDM4hepUtil::readMeasurement( - trackerHitPlane, &trackerHitRawCollection, &cluster, + EDM4hepUtil::readMeasurement( + measurements, trackerHitPlane, &trackerHitRawCollection, &cluster, [](std::uint64_t cellId) { return Acts::GeometryIdentifier(cellId); }); - measurements.push_back(std::move(measurement)); clusters.push_back(std::move(cluster)); } diff --git a/Examples/Io/EDM4hep/src/EDM4hepMeasurementWriter.cpp b/Examples/Io/EDM4hep/src/EDM4hepMeasurementWriter.cpp index 6b26db80362..f9b35e7edb1 100644 --- a/Examples/Io/EDM4hep/src/EDM4hepMeasurementWriter.cpp +++ b/Examples/Io/EDM4hep/src/EDM4hepMeasurementWriter.cpp @@ -55,7 +55,8 @@ ActsExamples::ProcessCode EDM4hepMeasurementWriter::writeT( << " measurements in this event."); for (Index hitIdx = 0u; hitIdx < measurements.size(); ++hitIdx) { - const auto& from = measurements[hitIdx]; + ConstVariableBoundMeasurementProxy from = + measurements.getMeasurement(hitIdx); const Cluster* fromCluster = clusters.empty() ? nullptr : &clusters[hitIdx]; auto to = hitsPlane.create(); diff --git a/Examples/Io/EDM4hep/src/EDM4hepUtil.cpp b/Examples/Io/EDM4hep/src/EDM4hepUtil.cpp index 6a7adfe98c5..dc863b135ab 100644 --- a/Examples/Io/EDM4hep/src/EDM4hepUtil.cpp +++ b/Examples/Io/EDM4hep/src/EDM4hepUtil.cpp @@ -18,6 +18,7 @@ #include "ActsExamples/Digitization/MeasurementCreation.hpp" #include "ActsExamples/EventData/Index.hpp" #include "ActsExamples/EventData/IndexSourceLink.hpp" +#include "ActsExamples/EventData/Measurement.hpp" #include "ActsExamples/EventData/SimHit.hpp" #include "ActsExamples/Validation/TrackClassification.hpp" @@ -144,8 +145,8 @@ void EDM4hepUtil::writeSimHit(const ActsFatras::Hit& from, to.setEDep(-delta4[Acts::eEnergy] / Acts::UnitConstants::GeV); } -Measurement EDM4hepUtil::readMeasurement( - const edm4hep::TrackerHitPlane& from, +VariableBoundMeasurementProxy EDM4hepUtil::readMeasurement( + MeasurementContainer& container, const edm4hep::TrackerHitPlane& from, const edm4hep::TrackerHitCollection* fromClusters, Cluster* toCluster, const MapGeometryIdFrom& geometryMapper) { // no need for digitization as we only want to identify the sensor @@ -172,7 +173,7 @@ Measurement EDM4hepUtil::readMeasurement( dParameters.values.push_back(pos.z); dParameters.variances.push_back(cov[5]); - auto to = createMeasurement(dParameters, sourceLink); + auto to = createMeasurement(container, dParameters, sourceLink); if (fromClusters != nullptr) { for (const auto objectId : from.getRawHits()) { @@ -196,11 +197,11 @@ Measurement EDM4hepUtil::readMeasurement( return to; } -void EDM4hepUtil::writeMeasurement(const Measurement& from, - edm4hep::MutableTrackerHitPlane to, - const Cluster* fromCluster, - edm4hep::TrackerHitCollection& toClusters, - const MapGeometryIdTo& geometryMapper) { +void EDM4hepUtil::writeMeasurement( + const ConstVariableBoundMeasurementProxy& from, + edm4hep::MutableTrackerHitPlane to, const Cluster* fromCluster, + edm4hep::TrackerHitCollection& toClusters, + const MapGeometryIdTo& geometryMapper) { Acts::GeometryIdentifier geoId = from.sourceLink().template get().geometryId(); diff --git a/Examples/Io/Root/src/RootAthenaDumpReader.cpp b/Examples/Io/Root/src/RootAthenaDumpReader.cpp index 8300237ef9e..001a8d90bdb 100644 --- a/Examples/Io/Root/src/RootAthenaDumpReader.cpp +++ b/Examples/Io/Root/src/RootAthenaDumpReader.cpp @@ -372,7 +372,7 @@ ActsExamples::ProcessCode ActsExamples::RootAthenaDumpReader::read( IndexSourceLink sl(Acts::GeometryIdentifier{CLmoduleID[im]}, im); - measurements.push_back(createMeasurement(digiPars, sl)); + createMeasurement(measurements, digiPars, sl); // Create measurement particles map and particles container for (const auto& [subevt, bc] : Acts::zip(CLparticleLink_eventIndex->at(im), diff --git a/Examples/Io/Root/src/RootMeasurementWriter.cpp b/Examples/Io/Root/src/RootMeasurementWriter.cpp index 6dc10e3b4b1..88b66e192f0 100644 --- a/Examples/Io/Root/src/RootMeasurementWriter.cpp +++ b/Examples/Io/Root/src/RootMeasurementWriter.cpp @@ -13,6 +13,7 @@ #include "ActsExamples/EventData/AverageSimHits.hpp" #include "ActsExamples/EventData/Index.hpp" #include "ActsExamples/EventData/IndexSourceLink.hpp" +#include "ActsExamples/EventData/Measurement.hpp" #include "ActsExamples/Framework/AlgorithmContext.hpp" #include "ActsExamples/Utilities/Range.hpp" @@ -152,9 +153,9 @@ struct RootMeasurementWriter::DigitizationTree { /// Convenience function to fill bound parameters /// /// @param m The measurement - void fillBoundMeasurement(const Measurement& m) { + void fillBoundMeasurement(const ConstVariableBoundMeasurementProxy& m) { for (unsigned int i = 0; i < m.size(); ++i) { - auto ib = m.subspaceIndices()[i]; + auto ib = m.subspaceIndexVector()[i]; recBound[ib] = m.parameters()[i]; varBound[ib] = m.covariance()(i, i); @@ -266,7 +267,8 @@ ProcessCode RootMeasurementWriter::writeT( std::lock_guard lock(m_writeMutex); for (Index hitIdx = 0u; hitIdx < measurements.size(); ++hitIdx) { - const auto& meas = measurements[hitIdx]; + const ConstVariableBoundMeasurementProxy meas = + measurements.getMeasurement(hitIdx); Acts::GeometryIdentifier geoId = meas.sourceLink().template get().geometryId(); diff --git a/Tests/UnitTests/Examples/EventData/MeasurementTests.cpp b/Tests/UnitTests/Examples/EventData/MeasurementTests.cpp index bab0b16e9e9..0156e01f03e 100644 --- a/Tests/UnitTests/Examples/EventData/MeasurementTests.cpp +++ b/Tests/UnitTests/Examples/EventData/MeasurementTests.cpp @@ -50,16 +50,19 @@ std::default_random_engine rng(123); BOOST_AUTO_TEST_SUITE(EventDataMeasurement) BOOST_DATA_TEST_CASE(VariableBoundOne, bd::make(boundIndices), index) { + MeasurementContainer container; + auto [params, cov] = generateParametersCovariance(rng); - auto meas = makeVariableSizeMeasurement(source, params, cov, index); + + FixedBoundMeasurementProxy<1> meas = container.makeMeasurement<1>(); + meas.setSourceLink(source); + meas.setSubspaceIndices(std::array{index}); + meas.parameters() = params; + meas.covariance() = cov; BOOST_CHECK_EQUAL(meas.size(), 1); for (auto i : boundIndices) { - if (i == index) { - BOOST_CHECK(meas.contains(i)); - } else { - BOOST_CHECK(!meas.contains(i)); - } + BOOST_CHECK_EQUAL(meas.contains(i), i == index); } BOOST_CHECK_EQUAL(meas.parameters(), params); BOOST_CHECK_EQUAL(meas.covariance(), cov); @@ -68,10 +71,17 @@ BOOST_DATA_TEST_CASE(VariableBoundOne, bd::make(boundIndices), index) { } BOOST_AUTO_TEST_CASE(VariableBoundAll) { + MeasurementContainer container; + auto [params, cov] = generateBoundParametersCovariance(rng); - auto meas = makeVariableSizeMeasurement(source, params, cov, eBoundLoc0, - eBoundLoc1, eBoundPhi, eBoundTheta, - eBoundQOverP, eBoundTime); + + FixedBoundMeasurementProxy meas = + container.makeMeasurement(); + meas.setSourceLink(source); + meas.setSubspaceIndices(std::array{eBoundLoc0, eBoundLoc1, eBoundTime, + eBoundPhi, eBoundTheta, eBoundQOverP}); + meas.parameters() = params; + meas.covariance() = cov; BOOST_CHECK_EQUAL(meas.size(), eBoundSize); for (auto i : boundIndices) { @@ -83,22 +93,35 @@ BOOST_AUTO_TEST_CASE(VariableBoundAll) { } BOOST_AUTO_TEST_CASE(VariableBoundReassign) { - // generate w/ a single parameter - auto [par1, cov1] = generateParametersCovariance(rng); - auto meas = makeVariableSizeMeasurement(source, par1, cov1, eBoundTheta); - BOOST_CHECK_EQUAL(meas.size(), 1); + MeasurementContainer container; + + // generate w/ two parameter + auto [params1, cov1] = generateParametersCovariance(rng); + + VariableBoundMeasurementProxy meas = container.makeMeasurement(2); + meas.setSourceLink(source); + meas.setSubspaceIndices(std::array{eBoundPhi, eBoundTheta}); + meas.parameters() = params1; + meas.covariance() = cov1; + + BOOST_CHECK_EQUAL(meas.size(), 2); BOOST_CHECK(!meas.contains(eBoundLoc0)); BOOST_CHECK(!meas.contains(eBoundLoc1)); BOOST_CHECK(!meas.contains(eBoundTime)); - BOOST_CHECK(!meas.contains(eBoundPhi)); + BOOST_CHECK(meas.contains(eBoundPhi)); BOOST_CHECK(meas.contains(eBoundTheta)); BOOST_CHECK(!meas.contains(eBoundQOverP)); // reassign w/ all parameters - auto [parN, covN] = generateBoundParametersCovariance(rng); - meas = makeVariableSizeMeasurement(source, parN, covN, eBoundLoc0, eBoundLoc1, - eBoundPhi, eBoundTheta, eBoundQOverP, - eBoundTime); + auto [paramsN, covN] = generateBoundParametersCovariance(rng); + + meas = container.makeMeasurement(eBoundSize); + meas.setSourceLink(source); + meas.setSubspaceIndices(std::array{eBoundLoc0, eBoundLoc1, eBoundTime, + eBoundPhi, eBoundTheta, eBoundQOverP}); + meas.parameters() = paramsN; + meas.covariance() = covN; + BOOST_CHECK_EQUAL(meas.size(), eBoundSize); BOOST_CHECK(meas.contains(eBoundLoc0)); BOOST_CHECK(meas.contains(eBoundLoc1)); diff --git a/Tests/UnitTests/Examples/Io/Csv/MeasurementReaderWriterTests.cpp b/Tests/UnitTests/Examples/Io/Csv/MeasurementReaderWriterTests.cpp index 4b4b6d78bbe..7fa64dcb128 100644 --- a/Tests/UnitTests/Examples/Io/Csv/MeasurementReaderWriterTests.cpp +++ b/Tests/UnitTests/Examples/Io/Csv/MeasurementReaderWriterTests.cpp @@ -50,11 +50,11 @@ BOOST_AUTO_TEST_CASE(CsvMeasurementRoundTrip) { Acts::Vector2 p = Acts::Vector2::Random(); Acts::SquareMatrix2 c = Acts::SquareMatrix2::Random(); - BoundVariableMeasurement m(Acts::SourceLink{sl}, - std::array{Acts::eBoundLoc0, Acts::eBoundLoc1}, - p, c); - - measOriginal.push_back(m); + FixedBoundMeasurementProxy<2> m = measOriginal.makeMeasurement<2>(); + m.setSourceLink(Acts::SourceLink(sl)); + m.setSubspaceIndices(std::array{Acts::eBoundLoc0, Acts::eBoundLoc1}); + m.parameters() = p; + m.covariance() = c; ActsExamples::Cluster cl; From 464675980c50a6070f5635127ee27fd29de5a679 Mon Sep 17 00:00:00 2001 From: "Alexander J. Pfleger" <70842573+AJPfleger@users.noreply.github.com> Date: Wed, 4 Sep 2024 15:10:24 +0200 Subject: [PATCH 2/3] ci(sonarcloud): fix shallow clone (#3590) sonarcloud was complaining about a shallow clone: image image --- .github/workflows/sonarcloud.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml index f7dfa2bdb97..1c0fc64ff27 100644 --- a/.github/workflows/sonarcloud.yml +++ b/.github/workflows/sonarcloud.yml @@ -23,6 +23,8 @@ jobs: - name: "Checkout repository" uses: actions/checkout@v4 + with: + fetch-depth: 0 # To prevent shallow clone - name: 'Download artifact' uses: actions/github-script@v7 From 9e1d9efa3ef3fa06c41ebb5b3a23b15443aa74b9 Mon Sep 17 00:00:00 2001 From: Andreas Salzburger Date: Wed, 4 Sep 2024 18:27:55 +0200 Subject: [PATCH 3/3] feat: detray geometry/material conversion in examples (#3579) This PR builds upon #3546 (and others) and introduces the first tests to run detray geometry conversion in memory and then write out json files. It also introduces a `DetrayPropagator` which implements the `ActsExamples::PropagatorInterface` and hence can be run with the `ActsExamples::PropagationAlgorithm`, see below: ![Screenshot 2024-09-02 at 11 08 54](https://github.com/user-attachments/assets/fad997ff-cd99-4911-837c-2a8201ac7533) *NOTE* The `propagate()` method of `detray` is currently not const, hence the propagator needs to be `mutable` for the moment, a fix for detray is in preparation. Currently possible: - [x] geometry writing (validated) - [ ] material writing (validated) - will be part of a followup PR @niermann999 @stephenswat can you please have a look at the implementation of the `DetrayStore` and the `DetrayHotStore` in particular, the idea here is to not expose `vecmem` specifics to the python bindings. For handling `Traccc/Detray` in the `ActsFramework` I propose the following: - pure geometry conversion and e.g. writing to files can be done with the `Python/src/Detray.cpp` binding code - all the rest goes into `ActsExamples/Algorithms/Framework/Traccc` with its associated bindings. @EleniXoch FYI --- .gitlab-ci.yml | 2 +- .../Material/HomogeneousSurfaceMaterial.hpp | 3 +- Examples/Algorithms/CMakeLists.txt | 1 + .../Propagation/PropagationAlgorithm.hpp | 4 - Examples/Algorithms/Traccc/CMakeLists.txt | 17 +++ .../ActsExamples/Traccc/DetrayPropagator.hpp | 132 ++++++++++++++++ .../ActsExamples/Traccc/DetrayStore.hpp | 55 +++++++ Examples/Python/CMakeLists.txt | 3 + Examples/Python/src/Detray.cpp | 34 ++++- Examples/Python/src/ModuleEntry.cpp | 2 + Examples/Python/src/Traccc.cpp | 92 ++++++++++++ Examples/Python/src/TracccStub.cpp | 13 ++ .../Plugins/Detray/DetrayConversionUtils.hpp | 12 +- .../Acts/Plugins/Detray/DetrayConverter.hpp | 49 ++++-- .../Detray/DetrayMaterialConverter.hpp | 21 ++- Plugins/Detray/src/DetrayConverter.cpp | 4 +- .../Detray/src/DetrayGeometryConverter.cpp | 7 +- .../Detray/src/DetrayMaterialConverter.cpp | 108 +++++++++---- .../CommonHelpers/CylindricalDetector.cpp | 115 ++++++++++++++ .../CommonHelpers/CylindricalDetector.hpp | 91 +++++++++++ Tests/CommonHelpers/CMakeLists.txt | 1 + .../CylindricalContainerBuilderTests.cpp | 53 +------ .../ActSVG/DetectorSvgConverterTests.cpp | 129 +--------------- Tests/UnitTests/Plugins/Detray/CMakeLists.txt | 1 + .../Plugins/Detray/DetrayConverterTests.cpp | 51 +++++++ .../Detray/DetrayGeometryConverterTests.cpp | 142 +----------------- .../Detray/DetrayMaterialConverterTests.cpp | 34 ++++- 27 files changed, 783 insertions(+), 393 deletions(-) create mode 100644 Examples/Algorithms/Traccc/CMakeLists.txt create mode 100644 Examples/Algorithms/Traccc/include/ActsExamples/Traccc/DetrayPropagator.hpp create mode 100644 Examples/Algorithms/Traccc/include/ActsExamples/Traccc/DetrayStore.hpp create mode 100644 Examples/Python/src/Traccc.cpp create mode 100644 Examples/Python/src/TracccStub.cpp create mode 100644 Tests/CommonHelpers/Acts/Tests/CommonHelpers/CylindricalDetector.cpp create mode 100644 Tests/CommonHelpers/Acts/Tests/CommonHelpers/CylindricalDetector.hpp create mode 100644 Tests/UnitTests/Plugins/Detray/DetrayConverterTests.cpp diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 843980dd82a..084d0c2dbb2 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -74,7 +74,7 @@ build_exatrkx_cpu: - git checkout $HEAD_SHA - cd .. - mkdir build - # Here we only do a minimal build without examples to save ressources + # Here we only do a minimal build without examples to save resources - > cmake -B build -S src --preset=gitlab-ci-exatrkx diff --git a/Core/include/Acts/Material/HomogeneousSurfaceMaterial.hpp b/Core/include/Acts/Material/HomogeneousSurfaceMaterial.hpp index 01a1e2702a0..541bbaeca1e 100644 --- a/Core/include/Acts/Material/HomogeneousSurfaceMaterial.hpp +++ b/Core/include/Acts/Material/HomogeneousSurfaceMaterial.hpp @@ -78,7 +78,8 @@ class HomogeneousSurfaceMaterial : public ISurfaceMaterial { /// @copydoc ISurfaceMaterial::materialSlab(const Vector3&) const /// /// @note the input parameter is ignored - const MaterialSlab& materialSlab(const Vector3& gp) const final; + const MaterialSlab& materialSlab(const Vector3& gp = Vector3{0., 0., + 0.}) const final; /// The inherited methods - for MaterialSlab access using ISurfaceMaterial::materialSlab; diff --git a/Examples/Algorithms/CMakeLists.txt b/Examples/Algorithms/CMakeLists.txt index bdfc0195b7a..01cc0beaf7f 100644 --- a/Examples/Algorithms/CMakeLists.txt +++ b/Examples/Algorithms/CMakeLists.txt @@ -9,6 +9,7 @@ add_subdirectory(Geometry) add_subdirectory(MaterialMapping) add_subdirectory(Printers) add_subdirectory(Propagation) +add_subdirectory_if(Traccc ACTS_BUILD_PLUGIN_TRACCC) add_subdirectory(TrackFinding) add_subdirectory_if(TrackFindingExaTrkX ACTS_BUILD_EXAMPLES_EXATRKX) add_subdirectory_if(TrackFindingML ACTS_BUILD_PLUGIN_ONNX) diff --git a/Examples/Algorithms/Propagation/include/ActsExamples/Propagation/PropagationAlgorithm.hpp b/Examples/Algorithms/Propagation/include/ActsExamples/Propagation/PropagationAlgorithm.hpp index bbebf9b8625..1cd006d3b45 100644 --- a/Examples/Algorithms/Propagation/include/ActsExamples/Propagation/PropagationAlgorithm.hpp +++ b/Examples/Algorithms/Propagation/include/ActsExamples/Propagation/PropagationAlgorithm.hpp @@ -23,10 +23,6 @@ #include #include -namespace Acts { -class Surface; -} - namespace ActsExamples { class PropagatorInterface; diff --git a/Examples/Algorithms/Traccc/CMakeLists.txt b/Examples/Algorithms/Traccc/CMakeLists.txt new file mode 100644 index 00000000000..11837f12298 --- /dev/null +++ b/Examples/Algorithms/Traccc/CMakeLists.txt @@ -0,0 +1,17 @@ +add_library(ActsExamplesTraccc INTERFACE) + +target_include_directories( + ActsExamplesTraccc + INTERFACE $ +) + +target_link_libraries( + ActsExamplesTraccc + INTERFACE + ActsCore + ActsExamplesFramework + ActsExamplesPropagation + ActsPluginDetray +) + +install(TARGETS ActsExamplesTraccc LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) diff --git a/Examples/Algorithms/Traccc/include/ActsExamples/Traccc/DetrayPropagator.hpp b/Examples/Algorithms/Traccc/include/ActsExamples/Traccc/DetrayPropagator.hpp new file mode 100644 index 00000000000..8c42ecbeab0 --- /dev/null +++ b/Examples/Algorithms/Traccc/include/ActsExamples/Traccc/DetrayPropagator.hpp @@ -0,0 +1,132 @@ +// 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/Geometry/GeometryIdentifier.hpp" +#include "Acts/Utilities/Logger.hpp" +#include "Acts/Utilities/Result.hpp" +#include "ActsExamples/EventData/PropagationSummary.hpp" +#include "ActsExamples/Propagation/PropagationAlgorithm.hpp" +#include "ActsExamples/Propagation/PropagatorInterface.hpp" +#include "ActsExamples/Traccc/DetrayStore.hpp" + +#include +#include + +namespace ActsExamples { + +/// Define the algebra type +using DetrayAlgebraType = typename Acts::DetrayHostDetector::algebra_type; + +/// Type that holds the intersection information +using DetrayIntersection = + detray::intersection2D; + +/// Inspector that records all encountered surfaces +using DetrayObjectTracer = + detray::navigation::object_tracer; + +/// Inspector that prints the navigator state from within the navigator's +/// method calls (cannot be done with an actor) +using DetrayPrintInspector = detray::navigation::print_inspector; + +template +class DetrayPropagator : public PropagatorInterface { + public: + /// Create a DetrayPropagator + /// + /// @param propagator The actual detray propagator to wrap + /// @param detrayStore The detray store to access the detector + /// @param logger The logger instance + DetrayPropagator(propagator_t&& propagator, + std::shared_ptr detrayStore, + std::unique_ptr logger = + Acts::getDefaultLogger("DetrayPropagator", + Acts::Logging::INFO)) + : PropagatorInterface(), + m_propagator(std::move(propagator)), + m_detrayStore(std::move(detrayStore)), + m_logger(std::move(logger)) {} + + ///@brief Execute a propagation for charged particle parameters + /// + ///@param context The algorithm context + ///@param cfg The propagation algorithm configuration + ///@param logger A logger wrapper instance + ///@param startParameters The start parameters + ///@return PropagationOutput + Acts::Result execute( + const AlgorithmContext& context, + [[maybe_unused]] const PropagationAlgorithm::Config& cfg, + const Acts::Logger& logger, + const Acts::BoundTrackParameters& startParameters) const final { + // Get the geometry context form the algorithm context + const auto& geoContext = context.geoContext; + // Get the track information + const Acts::Vector3 position = startParameters.position(geoContext); + const Acts::Vector3 direction = startParameters.momentum().normalized(); + + ACTS_VERBOSE("Starting propagation at " << position.transpose() + << " with direction " + << direction.transpose()); + + // Now follow that ray with the same track and check, if we find + // the same volumes and distances along the way + detray::free_track_parameters track( + {position.x(), position.y(), position.z()}, 0.f, + {direction.x(), direction.y(), direction.z()}, + startParameters.charge()); + + typename propagator_t::state propagation(track, m_detrayStore->detector); + + // Run the actual propagation + m_propagator.propagate(propagation); + + // Retrieve navigation information + auto& inspector = propagation._navigation.inspector(); + auto& objectTracer = inspector.template get(); + + PropagationSummary summary(startParameters); + // Translate the objects into the steps + for (const auto& object : objectTracer.object_trace) { + // Get the position of the object + const auto& dposition = object.pos; + const auto& sfDesription = object.intersection.sf_desc; + const auto sf = + detray::tracking_surface{m_detrayStore->detector, sfDesription}; + Acts::GeometryIdentifier geoID{sf.source()}; + // Create a step from the object + Acts::detail::Step step; + step.position = Acts::Vector3(dposition[0], dposition[1], dposition[2]); + step.geoID = geoID; + step.navDir = object.intersection.direction ? Acts::Direction::Forward + : Acts::Direction::Backward; + summary.steps.emplace_back(step); + } + RecordedMaterial recordedMaterial; + return std::pair{std::move(summary), std::move(recordedMaterial)}; + } + + private: + /// The propagator @todo fix when propagate() method is const in detray + mutable propagator_t m_propagator; + + /// The detray detector store and memory resource + std::shared_ptr m_detrayStore = nullptr; + + /// The logging instance + std::unique_ptr m_logger = nullptr; + + const Acts::Logger& logger() const { return *m_logger; } +}; + +} // namespace ActsExamples diff --git a/Examples/Algorithms/Traccc/include/ActsExamples/Traccc/DetrayStore.hpp b/Examples/Algorithms/Traccc/include/ActsExamples/Traccc/DetrayStore.hpp new file mode 100644 index 00000000000..c2ccb9f6119 --- /dev/null +++ b/Examples/Algorithms/Traccc/include/ActsExamples/Traccc/DetrayStore.hpp @@ -0,0 +1,55 @@ +// 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/Plugins/Detray/DetrayConversionUtils.hpp" +#include "Acts/Plugins/Detray/DetrayConverter.hpp" + +#include + +#include +#include + +namespace ActsExamples { + +// The Detray host store that is used to store the detector +// and the associated memory resource +template +struct DetrayStore { + // Constructor from arguments + DetrayStore(std::shared_ptr mSource, + Acts::DetrayHostDetector&& det) + : memoryResource(std::move(mSource)), detector(std::move(det)) {} + + // The memory resource + std::shared_ptr memoryResource = nullptr; + // The detray detector instance + Acts::DetrayHostDetector detector; + + // Create a Detray detector and store it with its memory Source in + /// + /// @param gctx the geometry context + /// @param detector the detector to be converted + /// @param options the conversion options + static inline std::shared_ptr> create( + const Acts::GeometryContext& gctx, + const Acts::Experimental::Detector& detector, + const Acts::DetrayConverter::Options& options) { + auto memoryResource = std::make_shared(); + auto DetrayHostDetector = Acts::DetrayConverter().convert<>( + gctx, detector, *memoryResource, options); + + return std::make_shared>( + memoryResource, std::move(DetrayHostDetector)); + } +}; + +using DetrayHostStore = DetrayStore; + +} // namespace ActsExamples diff --git a/Examples/Python/CMakeLists.txt b/Examples/Python/CMakeLists.txt index 13408e53f1d..ff1e5901f43 100644 --- a/Examples/Python/CMakeLists.txt +++ b/Examples/Python/CMakeLists.txt @@ -99,9 +99,12 @@ if(ACTS_BUILD_PLUGIN_TRACCC) target_sources(ActsPythonBindings PRIVATE src/Detray.cpp) target_link_libraries(ActsPythonBindings PUBLIC ActsPluginCovfie) target_sources(ActsPythonBindings PRIVATE src/Covfie.cpp) + target_link_libraries(ActsPythonBindings PUBLIC ActsExamplesTraccc) + target_sources(ActsPythonBindings PRIVATE src/Traccc.cpp) else() target_sources(ActsPythonBindings PRIVATE src/DetrayStub.cpp) target_sources(ActsPythonBindings PRIVATE src/CovfieStub.cpp) + target_sources(ActsPythonBindings PRIVATE src/TracccStub.cpp) endif() if(ACTS_BUILD_PLUGIN_ACTSVG) diff --git a/Examples/Python/src/Detray.cpp b/Examples/Python/src/Detray.cpp index 5239ad3ce36..d254a51443d 100644 --- a/Examples/Python/src/Detray.cpp +++ b/Examples/Python/src/Detray.cpp @@ -30,12 +30,42 @@ namespace Acts::Python { void addDetray(Context& ctx) { auto [m, mex] = ctx.get("main", "examples"); + auto detray = m.def_submodule("detray"); { py::class_, - std::shared_ptr>>(m, + std::shared_ptr>>(detray, "detray_detector"); } - { mex.def("writeToJson", &DetrayConverter::writeToJson); } + { + // This test function will convert an ACTS detector into a detray detector + // and write it to the corresponding json files. + // + // The memory resource and the detector are destroyed after the function + detray.def("writeToJson", [](const GeometryContext& gctx, + const Experimental::Detector& detector) { + auto memoryResource = vecmem::host_memory_resource(); + + DetrayConverter::Options options; + options.writeToJson = true; + options.convertMaterial = false; + options.convertSurfaceGrids = true; + auto DetrayHostDetector = + DetrayConverter().convert<>(gctx, detector, memoryResource, options); + }); + } + + { + auto converter = py::class_(detray, "DetrayConverter"); + + auto options = py::class_(converter, "Options") + .def(py::init<>()); + + ACTS_PYTHON_STRUCT_BEGIN(options, DetrayConverter::Options); + ACTS_PYTHON_MEMBER(convertMaterial); + ACTS_PYTHON_MEMBER(convertSurfaceGrids); + ACTS_PYTHON_MEMBER(writeToJson); + ACTS_PYTHON_STRUCT_END(); + } } } // namespace Acts::Python diff --git a/Examples/Python/src/ModuleEntry.cpp b/Examples/Python/src/ModuleEntry.cpp index 6edb715e0cd..791eabc327b 100644 --- a/Examples/Python/src/ModuleEntry.cpp +++ b/Examples/Python/src/ModuleEntry.cpp @@ -83,6 +83,7 @@ void addObj(Context& ctx); void addOnnx(Context& ctx); void addOnnxNeuralCalibrator(Context& ctx); void addCovfie(Context& ctx); +void addTraccc(Context& ctx); void addHashing(Context& ctx); } // namespace Acts::Python @@ -151,5 +152,6 @@ PYBIND11_MODULE(ActsPythonBindings, m) { addOnnx(ctx); addOnnxNeuralCalibrator(ctx); addCovfie(ctx); + addTraccc(ctx); addHashing(ctx); } diff --git a/Examples/Python/src/Traccc.cpp b/Examples/Python/src/Traccc.cpp new file mode 100644 index 00000000000..d620a5584f7 --- /dev/null +++ b/Examples/Python/src/Traccc.cpp @@ -0,0 +1,92 @@ +// 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/. + +// 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/. + +#include "Acts/Plugins/Detray/DetrayConversionUtils.hpp" +#include "Acts/Plugins/Detray/DetrayConverter.hpp" +#include "Acts/Plugins/Python/Utilities.hpp" +#include "ActsExamples/Propagation/PropagatorInterface.hpp" +#include "ActsExamples/Traccc/DetrayPropagator.hpp" +#include "ActsExamples/Traccc/DetrayStore.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace py = pybind11; +using namespace pybind11::literals; + +using namespace Acts; +using namespace ActsExamples; + +namespace Acts::Python { + +void addTraccc(Context& ctx) { + auto [m, mex] = ctx.get("main", "examples"); + + auto traccc = mex.def_submodule("traccc"); + + /// Define host detray store + { + py::class_>( + traccc, "DetrayHostStore"); + + /// Convert the detector and create a DetrayHostStore + /// + /// @param gctx the geometry context + /// @param detector the detector to be converted + /// @param options the conversion options + traccc.def("convertDetectorHost", [](const GeometryContext& gctx, + const Experimental::Detector& detector, + DetrayConverter::Options options) { + return DetrayHostStore::create(gctx, detector, options); + }); + } + + /// Define the DetrayPropagator + { + traccc.def("createPropagatorHost", [](std::shared_ptr + detrayStore) { + std::shared_ptr detrayProagator = nullptr; + + /// Aggregation of multiple inspectors + using DetrayInspector = + detray::aggregate_inspector; + + // Navigation with inspection + using DetrayNavigator = + detray::navigator; + // Line stepper + using DetrayLineStepper = + detray::line_stepper; + + // Propagator with empty actor chain + using Propagator = detray::propagator>; + + Propagator propagator; + detrayProagator = + std::make_shared>( + std::move(propagator), detrayStore); + return detrayProagator; + }); + } +} +} // namespace Acts::Python diff --git a/Examples/Python/src/TracccStub.cpp b/Examples/Python/src/TracccStub.cpp new file mode 100644 index 00000000000..65b802a77b4 --- /dev/null +++ b/Examples/Python/src/TracccStub.cpp @@ -0,0 +1,13 @@ +// 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/. + +#include "Acts/Plugins/Python/Utilities.hpp" + +namespace Acts::Python { +void addTraccc(Context& /*ctx*/) {} +} // namespace Acts::Python diff --git a/Plugins/Detray/include/Acts/Plugins/Detray/DetrayConversionUtils.hpp b/Plugins/Detray/include/Acts/Plugins/Detray/DetrayConversionUtils.hpp index 6e5a6788a63..f3a111f7aee 100644 --- a/Plugins/Detray/include/Acts/Plugins/Detray/DetrayConversionUtils.hpp +++ b/Plugins/Detray/include/Acts/Plugins/Detray/DetrayConversionUtils.hpp @@ -20,20 +20,10 @@ namespace Acts { -using DetrayDetector = detray::detector; +using DetrayHostDetector = detray::detector; namespace DetrayConversionUtils { -/// Detray conversion options -struct Options { - /// Option to switch on/off the material conversion - bool convertMaterial = true; - /// Option to switch on/off the surface grid conversin - bool convertSurfaceGrids = true; - /// Option to switch on/off the export to json - bool writeToJson = false; -}; - /// Detray conversion cache object /// /// This object is used to synchronize link information between the diff --git a/Plugins/Detray/include/Acts/Plugins/Detray/DetrayConverter.hpp b/Plugins/Detray/include/Acts/Plugins/Detray/DetrayConverter.hpp index ffb799cc29b..24c59968ec5 100644 --- a/Plugins/Detray/include/Acts/Plugins/Detray/DetrayConverter.hpp +++ b/Plugins/Detray/include/Acts/Plugins/Detray/DetrayConverter.hpp @@ -26,6 +26,16 @@ using namespace Experimental; class DetrayConverter { public: + /// Detray conversion options + struct Options { + /// Option to switch on/off the material conversion + bool convertMaterial = true; + /// Option to switch on/off the surface grid conversin + bool convertSurfaceGrids = true; + /// Option to switch on/off the export to json + bool writeToJson = false; + }; + /// Constructor with logger DetrayConverter(std::unique_ptr logger = getDefaultLogger("DetrayConverter", Logging::INFO)); @@ -38,11 +48,9 @@ class DetrayConverter { /// @param options the conversion options /// /// @returns a detector of requested return type - template - detector_t convert( - const GeometryContext& gctx, const Detector& detector, - vecmem::memory_resource& mr, - [[maybe_unused]] const DetrayConversionUtils::Options& options = {}) { + template + detector_t convert(const GeometryContext& gctx, const Detector& detector, + vecmem::memory_resource& mr, const Options& options) { // The building cache object DetrayConversionUtils::GeometryIdCache geoIdCache; @@ -56,16 +64,30 @@ class DetrayConverter { logger()); detray::io::geometry_reader::convert(detectorBuilder, names, detectorPayload); - // (2) material + + // (2a) homogeneous material + if constexpr (detray::detail::has_homogeneous_material_v) { + if (options.convertMaterial) { + detray::io::detector_homogeneous_material_payload materialSlabsPayload = + DetrayMaterialConverter::convertHomogeneousSurfaceMaterial( + geoIdCache, detector, logger()); + detray::io::homogeneous_material_reader::convert( + detectorBuilder, names, std::move(materialSlabsPayload)); + } + } + + // (2b) material grids if constexpr (detray::detail::has_material_grids_v) { if (options.convertMaterial) { detray::io::detector_grids_payload - materialPayload = - DetrayMaterialConverter::convertSurfaceMaterialGrids( + materialGridsPayload = + DetrayMaterialConverter::convertGridSurfaceMaterial( geoIdCache, detector, logger()); - detray::io::material_map_reader<>::convert( - detectorBuilder, names, materialPayload); + detray::io::material_map_reader>::convert(detectorBuilder, names, + std::move( + materialGridsPayload)); } } @@ -91,9 +113,10 @@ class DetrayConverter { /// @param dDetector is the detray detector (converted) /// @param names a name map for the detector volumes /// @param writer_cfg the writer configuration - static void writeToJson(const DetrayDetector& dDetector, - const typename DetrayDetector::name_map& names = {}, - detray::io::detector_writer_config writer_cfg = {}); + static void writeToJson( + const DetrayHostDetector& dDetector, + const typename DetrayHostDetector::name_map& names = {}, + detray::io::detector_writer_config writer_cfg = {}); private: /// The logger instance diff --git a/Plugins/Detray/include/Acts/Plugins/Detray/DetrayMaterialConverter.hpp b/Plugins/Detray/include/Acts/Plugins/Detray/DetrayMaterialConverter.hpp index d0edc1beda7..fecdec364b8 100644 --- a/Plugins/Detray/include/Acts/Plugins/Detray/DetrayMaterialConverter.hpp +++ b/Plugins/Detray/include/Acts/Plugins/Detray/DetrayMaterialConverter.hpp @@ -8,6 +8,7 @@ #pragma once +#include "Acts/Material/HomogeneousSurfaceMaterial.hpp" #include "Acts/Material/ISurfaceMaterial.hpp" #include "Acts/Material/MaterialSlab.hpp" #include "Acts/Plugins/Detray/DetrayConversionUtils.hpp" @@ -32,16 +33,28 @@ namespace DetrayMaterialConverter { detray::io::material_slab_payload convertMaterialSlab( const MaterialSlab& materialSlab); -/// Conversion method for surface material objects +/// Conversion method for homogeneous material /// +/// @param geoIdCache object to have the link association from the geometry building /// @param detector the detector object /// @param logger the logger object for screen output /// +/// @return the volume_payload for portals and volumes by @param volume acts object +detray::io::detector_homogeneous_material_payload +convertHomogeneousSurfaceMaterial( + const DetrayConversionUtils::GeometryIdCache& geoIdCache, + const Experimental::Detector& detector, const Logger& logger); + +/// Conversion method for grid based surface material +/// +/// @param material the material (Binned/Grid) +/// @param logger the logger object for screen output +/// /// @return a surface material detray::io::grid_payload -convertSurfaceMaterial(const ISurfaceMaterial& material, - const Acts::Logger& logger); +convertGridSurfaceMaterial(const ISurfaceMaterial& material, + const Acts::Logger& logger); /// Conversion method for material grids /// @@ -52,7 +65,7 @@ convertSurfaceMaterial(const ISurfaceMaterial& material, /// @return the volume_payload for portals and volumes by @param volume acts object detray::io::detector_grids_payload -convertSurfaceMaterialGrids( +convertGridSurfaceMaterial( const DetrayConversionUtils::GeometryIdCache& geoIdCache, const Experimental::Detector& detector, const Logger& logger); diff --git a/Plugins/Detray/src/DetrayConverter.cpp b/Plugins/Detray/src/DetrayConverter.cpp index 3b72b998ab5..d35ff7cb6ba 100644 --- a/Plugins/Detray/src/DetrayConverter.cpp +++ b/Plugins/Detray/src/DetrayConverter.cpp @@ -13,8 +13,8 @@ Acts::DetrayConverter::DetrayConverter( : m_logger(std::move(logger)) {} void Acts::DetrayConverter::writeToJson( - const DetrayDetector& dDetector, - const typename DetrayDetector::name_map& names, + const DetrayHostDetector& dDetector, + const typename DetrayHostDetector::name_map& names, detray::io::detector_writer_config writer_cfg) { writer_cfg.format(detray::io::format::json); detray::io::write_detector(dDetector, names, writer_cfg); diff --git a/Plugins/Detray/src/DetrayGeometryConverter.cpp b/Plugins/Detray/src/DetrayGeometryConverter.cpp index 9e531e0be21..4c1902c6b13 100644 --- a/Plugins/Detray/src/DetrayGeometryConverter.cpp +++ b/Plugins/Detray/src/DetrayGeometryConverter.cpp @@ -47,10 +47,9 @@ int findVolume( detray::io::transform_payload Acts::DetrayGeometryConverter::convertTransform( const Transform3& t) { detray::io::transform_payload tfPayload; - auto translation = t.translation(); + Vector3 translation = t.translation(); tfPayload.tr = {translation.x(), translation.y(), translation.z()}; - - const auto rotation = t.rotation(); + RotationMatrix3 rotation = t.rotation().transpose(); tfPayload.rot = {rotation(0, 0), rotation(0, 1), rotation(0, 2), rotation(1, 0), rotation(1, 1), rotation(1, 2), rotation(2, 0), rotation(2, 1), rotation(2, 2)}; @@ -260,7 +259,7 @@ Acts::DetrayGeometryConverter::convertPortal( // Write surface with invalid link auto portalPayload = convertSurface(gctx, *surfaceAdjusted, true); using NavigationLink = - typename DetrayDetector::surface_type::navigation_link; + typename DetrayHostDetector::surface_type::navigation_link; portalPayload.mask.volume_link.link = std::numeric_limits::max(); diff --git a/Plugins/Detray/src/DetrayMaterialConverter.cpp b/Plugins/Detray/src/DetrayMaterialConverter.cpp index eefd2c59712..14fdd2163df 100644 --- a/Plugins/Detray/src/DetrayMaterialConverter.cpp +++ b/Plugins/Detray/src/DetrayMaterialConverter.cpp @@ -11,6 +11,7 @@ #include "Acts/Detector/Detector.hpp" #include "Acts/Material/BinnedSurfaceMaterial.hpp" #include "Acts/Material/HomogeneousSurfaceMaterial.hpp" +#include "Acts/Material/ProtoSurfaceMaterial.hpp" #include "Acts/Plugins/Detray/DetrayConversionUtils.hpp" #include "Acts/Surfaces/Surface.hpp" #include "Acts/Utilities/BinUtility.hpp" @@ -34,19 +35,6 @@ struct MaterialSurfaceSelector { } }; -/// This creates dummy axes to allow homogeneous material for the moment -/// to be represented as grid surface material -std::vector homogeneousAxesPayloads() { - Acts::BinningData bDataX(Acts::BinningValue::binX, -1, 1); - bDataX.option = Acts::BinningOption::closed; - Acts::BinningData bDataY(Acts::BinningValue::binY, -1, 1); - bDataY.option = Acts::BinningOption::closed; - auto axisPayloadX = Acts::DetrayConversionUtils::convertBinningData(bDataX); - auto axisPayloadY = Acts::DetrayConversionUtils::convertBinningData(bDataY); - - return {axisPayloadX, axisPayloadY}; -} - } // namespace detray::io::material_slab_payload @@ -63,30 +51,77 @@ Acts::DetrayMaterialConverter::convertMaterialSlab( return slab; } +detray::io::detector_homogeneous_material_payload +Acts::DetrayMaterialConverter::convertHomogeneousSurfaceMaterial( + const DetrayConversionUtils::GeometryIdCache& geoIdCache, + const Experimental::Detector& detector, const Logger& logger) { + detray::io::detector_homogeneous_material_payload materialPayload; + + for (const auto volume : detector.volumes()) { + auto volumeIndex = geoIdCache.volumeLinks.find(volume->geometryId()); + if (volumeIndex != geoIdCache.volumeLinks.end()) { + // The volume material payload & its link + detray::io::material_volume_payload volumePayload; + detray::io::single_link_payload volumeLink; + volumeLink.link = volumeIndex->second; + volumePayload.volume_link = volumeLink; + // Now run through surfaces and portals to find the material + MaterialSurfaceSelector selector; + volume->visitSurfaces(selector); + ACTS_DEBUG("DetrayMaterialConverter: found " + << selector.surfaces.size() + << " surfaces/portals with material in volume " + << volume->name()); + for (const auto surface : selector.surfaces) { + const auto* surfaceMaterial = surface->surfaceMaterial(); + auto homogeneousMaterial = + dynamic_cast(surfaceMaterial); + if (homogeneousMaterial != nullptr) { + // Convert the material slab + auto materialSlab = homogeneousMaterial->materialSlab(); + detray::io::material_slab_payload slabPayload = + convertMaterialSlab(materialSlab); + // Find the surfaces and assign + auto surfaceIndices = + geoIdCache.localSurfaceLinks.equal_range(surface->geometryId()); + // Loop over the equal range and fill one grid each, this is needed + // as the initial portal could be split into multiple surfaces + for (auto itr = surfaceIndices.first; itr != surfaceIndices.second; + ++itr) { + // Make an identified link copy for every matching surface + detray::io::single_link_payload surfaceLink; + surfaceLink.link = itr->second; + slabPayload.surface = surfaceLink; + volumePayload.mat_slabs.push_back(slabPayload); + } + } + } + materialPayload.volumes.push_back(volumePayload); + } else { + ACTS_WARNING("DetrayMaterialConverter: volume " << volume->name() + << " not found in cache"); + } + } + + return materialPayload; +} + detray::io::grid_payload -Acts::DetrayMaterialConverter::convertSurfaceMaterial( +Acts::DetrayMaterialConverter::convertGridSurfaceMaterial( const ISurfaceMaterial& material, const Logger& logger) { detray::io::grid_payload materialGrid; // Check the material types - // (1) homogeneous -> 1 x 1 bin grid with closed axes + // (1) homogeneous -> skip auto homogeneousMaterial = dynamic_cast(&material); if (homogeneousMaterial != nullptr) { - ACTS_VERBOSE( - "DetrayMaterialConverter: found homogeneous surface material, this " - "will be modelled as a 1x1 bin grid"); - // A single bin entry: convert it and fill it - detray::io::material_slab_payload slab = convertMaterialSlab( - homogeneousMaterial->materialSlab(Vector3{0., 0., 0.})); - detray::io::grid_bin_payload slabBin{ - {0, 0}, {slab}}; - // Filling axes and bins - materialGrid.axes = homogeneousAxesPayloads(); - materialGrid.bins = {slabBin}; + ACTS_DEBUG( + "DetrayMaterialConverter: found homogeneous surface material, ignored " + "as this should be handled by the homogeneous material conversion."); return materialGrid; } // (2) - binned material -> convert into grid structure @@ -148,7 +183,8 @@ Acts::DetrayMaterialConverter::convertSurfaceMaterial( } else if (bVal0 == BinningValue::binX && bVal1 == BinningValue::binY) { gridIndexType = detray::io::material_id::rectangle2_map; } else { - std::runtime_error("Unsupported binning for Detray"); + std::runtime_error( + "DetrayMaterialConverter: Unsupported binning for Detray"); } detray::io::typed_link_payload linkPayload{ @@ -180,13 +216,23 @@ Acts::DetrayMaterialConverter::convertSurfaceMaterial( return materialGrid; } + if (dynamic_cast(&material) != nullptr || + dynamic_cast(&material) != + nullptr) { + ACTS_WARNING( + "DetrayMaterialConverter: ProtoSurfaceMaterial and " + "ProtoGridSurfaceMaterial are not being translated, consider to switch " + "material conversion off."); + return materialGrid; + } + throw std::invalid_argument( "DetrayMaterialConverter: unknown surface material type detected."); } detray::io::detector_grids_payload -Acts::DetrayMaterialConverter::convertSurfaceMaterialGrids( +Acts::DetrayMaterialConverter::convertGridSurfaceMaterial( const DetrayConversionUtils::GeometryIdCache& geoIdCache, const Experimental::Detector& detector, const Logger& logger) { // The material grid payload @@ -217,7 +263,11 @@ Acts::DetrayMaterialConverter::convertSurfaceMaterialGrids( auto surfaceIndices = geoIdCache.localSurfaceLinks.equal_range(surface->geometryId()); DetrayMaterialGrid materialGrid = - convertSurfaceMaterial(*surface->surfaceMaterial(), logger); + convertGridSurfaceMaterial(*surface->surfaceMaterial(), logger); + // Ignore if an empty payload is returned + if (materialGrid.axes.empty() && materialGrid.bins.empty()) { + continue; + } // Loop over the equal range and fill one grid each, this is needed // as the initial portal could be split into multiple surfaces for (auto itr = surfaceIndices.first; itr != surfaceIndices.second; diff --git a/Tests/CommonHelpers/Acts/Tests/CommonHelpers/CylindricalDetector.cpp b/Tests/CommonHelpers/Acts/Tests/CommonHelpers/CylindricalDetector.cpp new file mode 100644 index 00000000000..d5aec8bf735 --- /dev/null +++ b/Tests/CommonHelpers/Acts/Tests/CommonHelpers/CylindricalDetector.cpp @@ -0,0 +1,115 @@ +// 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/. + +#include "Acts/Tests/CommonHelpers/CylindricalDetector.hpp" + +#include "Acts/Detector/CylindricalContainerBuilder.hpp" +#include "Acts/Detector/DetectorBuilder.hpp" +#include "Acts/Detector/DetectorComponents.hpp" +#include "Acts/Detector/DetectorVolume.hpp" +#include "Acts/Detector/GeometryIdGenerator.hpp" +#include "Acts/Geometry/CylinderVolumeBounds.hpp" +#include "Acts/Geometry/GeometryContext.hpp" +#include "Acts/Material/BinnedSurfaceMaterial.hpp" +#include "Acts/Material/HomogeneousSurfaceMaterial.hpp" +#include "Acts/Material/Material.hpp" +#include "Acts/Material/MaterialSlab.hpp" +#include "Acts/Navigation/DetectorVolumeFinders.hpp" +#include "Acts/Navigation/InternalNavigation.hpp" +#include "Acts/Surfaces/CylinderBounds.hpp" +#include "Acts/Surfaces/CylinderSurface.hpp" +#include "Acts/Surfaces/DiscSurface.hpp" +#include "Acts/Surfaces/RadialBounds.hpp" + +#include + +auto materialSlab = + Acts::MaterialSlab(Acts::Material::fromMolarDensity(1, 2, 3, 4, 5), 1.); + +using namespace Acts; +using namespace Acts::Experimental; + +std::shared_ptr Acts::Test::buildCylindricalDetector( + const Acts::GeometryContext& tContext) { + auto material = + std::make_shared(materialSlab); + + auto beampipe = std::make_shared< + CylindricalVolumeBuilder>( + Transform3::Identity(), CylinderVolumeBounds(0., 50., 400.), + CylinderBounds(25., 380.), "BeamPipe", material); + + // Declare a negative disc builder + Transform3 negZ = Transform3::Identity(); + negZ.pretranslate(Vector3(0., 0., -300.)); + auto endcapN = + std::make_shared>( + negZ, CylinderVolumeBounds(50., 140., 100.), RadialBounds(60., 120.), + "NegativeEndcap", material); + + // Declare a barrel sub builder + auto barrel0 = std::make_shared< + CylindricalVolumeBuilder>( + Transform3::Identity(), CylinderVolumeBounds(50., 80., 200.), + CylinderBounds(65., 180.), "Barrel0", material); + + // Declare a barrel sub builder + auto barrel1 = std::make_shared< + CylindricalVolumeBuilder>( + Transform3::Identity(), CylinderVolumeBounds(80., 110., 200.), + CylinderBounds(95., 180.), "Barrel1", material); + + // Declare a barrel sub builder + auto barrel2 = std::make_shared< + CylindricalVolumeBuilder>( + Transform3::Identity(), CylinderVolumeBounds(110., 140., 200.), + CylinderBounds(125., 180.), "Barrel2", material); + + // Create the barrel container builder + CylindricalContainerBuilder::Config barrelRCfg; + barrelRCfg.builders = {barrel0, barrel1, barrel2}; + barrelRCfg.binning = {BinningValue::binR}; + + auto barrel = std::make_shared( + barrelRCfg, getDefaultLogger("BarrelBuilderR", Logging::INFO)); + + Transform3 posZ = Transform3::Identity(); + posZ.pretranslate(Vector3(0., 0., 300.)); + auto endcapP = + std::make_shared>( + posZ, CylinderVolumeBounds(50., 140., 100.), RadialBounds(60., 120.), + "PositiveEndcap", material); + + // Create the barrel container builder + CylindricalContainerBuilder::Config barrelEndcapCfg; + barrelEndcapCfg.builders = {endcapN, barrel, endcapP}; + barrelEndcapCfg.binning = {BinningValue::binZ}; + + auto barrelEndcap = std::make_shared( + barrelEndcapCfg, getDefaultLogger("BarrelEndcapBuilder", Logging::INFO)); + + // Create the barrel container builder + CylindricalContainerBuilder::Config detectorCfg; + detectorCfg.builders = {beampipe, barrelEndcap}; + detectorCfg.binning = {BinningValue::binR}; + + auto containerBuilder = std::make_shared( + detectorCfg, getDefaultLogger("DetectorBuilder", Logging::INFO)); + + // Detector builder + auto gigConfig = GeometryIdGenerator::Config(); + auto gig = std::make_shared(gigConfig); + + Acts::Experimental::DetectorBuilder::Config dCfg; + dCfg.auxiliary = "*** Test : Cylindrical Detector ***"; + dCfg.name = "CylindricalDetector"; + dCfg.builder = containerBuilder; + dCfg.geoIdGenerator = gig; + + return DetectorBuilder(dCfg).construct(tContext); +} diff --git a/Tests/CommonHelpers/Acts/Tests/CommonHelpers/CylindricalDetector.hpp b/Tests/CommonHelpers/Acts/Tests/CommonHelpers/CylindricalDetector.hpp new file mode 100644 index 00000000000..b8b5e4df21d --- /dev/null +++ b/Tests/CommonHelpers/Acts/Tests/CommonHelpers/CylindricalDetector.hpp @@ -0,0 +1,91 @@ +// 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/Detector/DetectorComponents.hpp" +#include "Acts/Detector/DetectorVolume.hpp" +#include "Acts/Detector/PortalGenerators.hpp" +#include "Acts/Detector/interface/IDetectorComponentBuilder.hpp" +#include "Acts/Geometry/CylinderVolumeBounds.hpp" +#include "Acts/Navigation/DetectorVolumeFinders.hpp" +#include "Acts/Navigation/InternalNavigation.hpp" + +#include +#include + +namespace Acts { + +class ISurfaceMaterial; + +using namespace Experimental; + +namespace Test { + +/// @brief A mockup volume builder, it generates volumes with +/// a single surface filled in in order to use the CylindricalContainerBuilder +/// infrastructure. +template +class CylindricalVolumeBuilder : public IDetectorComponentBuilder { + public: + CylindricalVolumeBuilder( + const Transform3& transform, const CylinderVolumeBounds& vBounds, + const surface_bounds_type& sBounds, const std::string& vName, + std::shared_ptr material = nullptr) + : IDetectorComponentBuilder(), + m_transform(transform), + m_volumeBounds(vBounds), + m_surfaceBounds(sBounds), + m_name(vName), + m_material(std::move(material)) {} + + DetectorComponent construct( + [[maybe_unused]] const GeometryContext& gctx) const final { + // The outgoing root volumes + std::vector> rootVolumes; + + // Ingredients + auto surface = Surface::makeShared( + (m_transform), std::make_shared(m_surfaceBounds)); + surface->assignSurfaceMaterial(m_material); + + auto bounds = std::make_unique(m_volumeBounds); + auto portalGenerator = defaultPortalGenerator(); + auto volume = DetectorVolumeFactory::construct( + portalGenerator, gctx, m_name, m_transform, std::move(bounds), + {surface}, {}, tryNoVolumes(), tryAllPortalsAndSurfaces()); + + // Add to the roots + rootVolumes.push_back(volume); + + DetectorComponent::PortalContainer dContainer; + for (auto [ip, p] : enumerate(volume->portalPtrs())) { + dContainer[ip] = p; + } + return DetectorComponent{ + {volume}, + dContainer, + RootDetectorVolumes{rootVolumes, tryRootVolumes()}}; + } + + private: + Transform3 m_transform; + CylinderVolumeBounds m_volumeBounds; + surface_bounds_type m_surfaceBounds; + std::string m_name; + std::shared_ptr m_material = nullptr; +}; + +/// @brief A mockup container builder, it generates a container with +/// serval cylindrical volumes in it. +std::shared_ptr buildCylindricalDetector( + const GeometryContext& tContext); + +} // namespace Test +} // namespace Acts diff --git a/Tests/CommonHelpers/CMakeLists.txt b/Tests/CommonHelpers/CMakeLists.txt index 12d42d851ab..84417f3ddb4 100644 --- a/Tests/CommonHelpers/CMakeLists.txt +++ b/Tests/CommonHelpers/CMakeLists.txt @@ -5,6 +5,7 @@ add_library( ActsTestsCommonHelpers SHARED Acts/Tests/CommonHelpers/DataDirectory.cpp + Acts/Tests/CommonHelpers/CylindricalDetector.cpp ) target_compile_definitions( ActsTestsCommonHelpers diff --git a/Tests/UnitTests/Core/Detector/CylindricalContainerBuilderTests.cpp b/Tests/UnitTests/Core/Detector/CylindricalContainerBuilderTests.cpp index 5fe880f57dd..fa15d5eb076 100644 --- a/Tests/UnitTests/Core/Detector/CylindricalContainerBuilderTests.cpp +++ b/Tests/UnitTests/Core/Detector/CylindricalContainerBuilderTests.cpp @@ -26,6 +26,7 @@ #include "Acts/Surfaces/DiscSurface.hpp" #include "Acts/Surfaces/RadialBounds.hpp" #include "Acts/Surfaces/Surface.hpp" +#include "Acts/Tests/CommonHelpers/CylindricalDetector.hpp" #include "Acts/Utilities/BinningType.hpp" #include "Acts/Utilities/Enumerate.hpp" #include "Acts/Utilities/Logger.hpp" @@ -40,61 +41,11 @@ #include using namespace Acts; +using namespace Acts::Test; using namespace Acts::Experimental; GeometryContext tContext; -/// @brief A mockup volume builder, it generates volumes with -/// a single surface filled in in order to use the CylindricalContainerBuilder -/// infrastructure. -template -class CylindricalVolumeBuilder : public IDetectorComponentBuilder { - public: - CylindricalVolumeBuilder(const Transform3& transform, - const CylinderVolumeBounds& vBounds, - const surface_bounds_type& sBounds, - const std::string& vName) - : IDetectorComponentBuilder(), - m_transform(transform), - m_volumeBounds(vBounds), - m_surfaceBounds(sBounds), - m_name(vName) {} - - DetectorComponent construct( - [[maybe_unused]] const GeometryContext& gctx) const final { - // The outgoing root volumes - std::vector> rootVolumes; - - // Ingredients - auto surface = Surface::makeShared( - (m_transform), std::make_shared(m_surfaceBounds)); - - auto bounds = std::make_unique(m_volumeBounds); - auto portalGenerator = defaultPortalGenerator(); - auto volume = DetectorVolumeFactory::construct( - portalGenerator, tContext, m_name, m_transform, std::move(bounds), - {surface}, {}, tryNoVolumes(), tryAllPortalsAndSurfaces()); - - // Add to the roots - rootVolumes.push_back(volume); - - DetectorComponent::PortalContainer dContainer; - for (auto [ip, p] : enumerate(volume->portalPtrs())) { - dContainer[ip] = p; - } - return DetectorComponent{ - {volume}, - dContainer, - RootDetectorVolumes{rootVolumes, tryRootVolumes()}}; - } - - private: - Transform3 m_transform; - CylinderVolumeBounds m_volumeBounds; - surface_bounds_type m_surfaceBounds; - std::string m_name; -}; - class VolumeGeoIdGenerator : public IGeometryIdGenerator { public: struct Cache { diff --git a/Tests/UnitTests/Plugins/ActSVG/DetectorSvgConverterTests.cpp b/Tests/UnitTests/Plugins/ActSVG/DetectorSvgConverterTests.cpp index cb28a9766ff..b4080848402 100644 --- a/Tests/UnitTests/Plugins/ActSVG/DetectorSvgConverterTests.cpp +++ b/Tests/UnitTests/Plugins/ActSVG/DetectorSvgConverterTests.cpp @@ -27,6 +27,7 @@ #include "Acts/Surfaces/DiscSurface.hpp" #include "Acts/Surfaces/RadialBounds.hpp" #include "Acts/Surfaces/Surface.hpp" +#include "Acts/Tests/CommonHelpers/CylindricalDetector.hpp" #include "Acts/Utilities/BinningType.hpp" #include "Acts/Utilities/Enumerate.hpp" #include "Acts/Utilities/Logger.hpp" @@ -36,139 +37,15 @@ #include using namespace Acts; +using namespace Acts::Test; using namespace Acts::Experimental; GeometryContext tContext; -/// @brief A mockup volume builder, it generates volumes with -/// a single surface filled in in order to use the CylindricalContainerBuilder -/// infrastructure. -template -class CylindricalVolumeBuilder : public IDetectorComponentBuilder { - public: - CylindricalVolumeBuilder(const Transform3& transform, - const CylinderVolumeBounds& vBounds, - const surface_bounds_type& sBounds, - const std::string& vName) - : IDetectorComponentBuilder(), - m_transform(transform), - m_volumeBounds(vBounds), - m_surfaceBounds(sBounds), - m_name(vName) {} - - DetectorComponent construct( - [[maybe_unused]] const GeometryContext& gctx) const final { - // The outgoing root volumes - std::vector> rootVolumes; - - // Ingredients - auto surface = Surface::makeShared( - (m_transform), std::make_shared(m_surfaceBounds)); - - auto bounds = std::make_unique(m_volumeBounds); - auto portalGenerator = defaultPortalGenerator(); - auto volume = DetectorVolumeFactory::construct( - portalGenerator, tContext, m_name, m_transform, std::move(bounds), - {surface}, {}, tryNoVolumes(), tryAllPortalsAndSurfaces()); - - // Add to the roots - rootVolumes.push_back(volume); - - DetectorComponent::PortalContainer dContainer; - for (auto [ip, p] : enumerate(volume->portalPtrs())) { - dContainer[ip] = p; - } - return DetectorComponent{ - {volume}, - dContainer, - RootDetectorVolumes{rootVolumes, tryRootVolumes()}}; - } - - private: - Transform3 m_transform; - CylinderVolumeBounds m_volumeBounds; - surface_bounds_type m_surfaceBounds; - std::string m_name; -}; - BOOST_AUTO_TEST_SUITE(ActSvg) BOOST_AUTO_TEST_CASE(CylindricalDetector) { - // Declare a barrel sub builder - auto beampipe = std::make_shared< - CylindricalVolumeBuilder>( - Transform3::Identity(), CylinderVolumeBounds(0., 50., 400.), - CylinderBounds(25., 380.), "BeamPipe"); - - // Declare a negative disc builder - Transform3 negZ = Transform3::Identity(); - negZ.pretranslate(Vector3(0., 0., -300.)); - auto endcapN = - std::make_shared>( - negZ, CylinderVolumeBounds(50., 140., 100.), RadialBounds(60., 120.), - "NegativeEndcap"); - - // Declare a barrel sub builder - auto barrel0 = std::make_shared< - CylindricalVolumeBuilder>( - Transform3::Identity(), CylinderVolumeBounds(50., 80., 200.), - CylinderBounds(65., 180.), "Barrel0"); - - // Declare a barrel sub builder - auto barrel1 = std::make_shared< - CylindricalVolumeBuilder>( - Transform3::Identity(), CylinderVolumeBounds(80., 110., 200.), - CylinderBounds(95., 180.), "Barrel1"); - - // Declare a barrel sub builder - auto barrel2 = std::make_shared< - CylindricalVolumeBuilder>( - Transform3::Identity(), CylinderVolumeBounds(110., 140., 200.), - CylinderBounds(125., 180.), "Barrel2"); - - // Create the barrel container builder - CylindricalContainerBuilder::Config barrelRCfg; - barrelRCfg.builders = {barrel0, barrel1, barrel2}; - barrelRCfg.binning = {BinningValue::binR}; - - auto barrel = std::make_shared( - barrelRCfg, getDefaultLogger("BarrelBuilderR", Logging::VERBOSE)); - - Transform3 posZ = Transform3::Identity(); - posZ.pretranslate(Vector3(0., 0., 300.)); - auto endcapP = - std::make_shared>( - posZ, CylinderVolumeBounds(50., 140., 100.), RadialBounds(60., 120.), - "PositiveEndcap"); - - // Create the barrel container builder - CylindricalContainerBuilder::Config barrelEndcapCfg; - barrelEndcapCfg.builders = {endcapN, barrel, endcapP}; - barrelEndcapCfg.binning = {BinningValue::binZ}; - - auto barrelEndcap = std::make_shared( - barrelEndcapCfg, - getDefaultLogger("BarrelEndcapBuilder", Logging::VERBOSE)); - - // Create the barrel container builder - CylindricalContainerBuilder::Config detectorCfg; - detectorCfg.builders = {beampipe, barrelEndcap}; - detectorCfg.binning = {BinningValue::binR}; - - auto containerBuilder = std::make_shared( - detectorCfg, getDefaultLogger("DetectorBuilder", Logging::VERBOSE)); - - // Detector builder - auto gigConfig = GeometryIdGenerator::Config(); - auto gig = std::make_shared(gigConfig); - - Acts::Experimental::DetectorBuilder::Config dCfg; - dCfg.auxiliary = "*** Test : Cylindrical Detector ***"; - dCfg.name = "CylindricalDetector"; - dCfg.builder = containerBuilder; - dCfg.geoIdGenerator = gig; - - auto detector = DetectorBuilder(dCfg).construct(tContext); + auto detector = buildCylindricalDetector(tContext); Acts::Svg::DetectorConverter::Options detectorOptions; auto pDetector = Acts::Svg::DetectorConverter::convert(tContext, *detector, diff --git a/Tests/UnitTests/Plugins/Detray/CMakeLists.txt b/Tests/UnitTests/Plugins/Detray/CMakeLists.txt index ea326ba2ecb..f621fa1261d 100644 --- a/Tests/UnitTests/Plugins/Detray/CMakeLists.txt +++ b/Tests/UnitTests/Plugins/Detray/CMakeLists.txt @@ -1,3 +1,4 @@ set(unittest_extra_libraries ActsPluginDetray) +add_unittest(DetrayConverter DetrayConverterTests.cpp) add_unittest(DetrayGeometryConverter DetrayGeometryConverterTests.cpp) add_unittest(DetrayMaterialConverter DetrayMaterialConverterTests.cpp) diff --git a/Tests/UnitTests/Plugins/Detray/DetrayConverterTests.cpp b/Tests/UnitTests/Plugins/Detray/DetrayConverterTests.cpp new file mode 100644 index 00000000000..2e6377f2cdc --- /dev/null +++ b/Tests/UnitTests/Plugins/Detray/DetrayConverterTests.cpp @@ -0,0 +1,51 @@ +// 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/. + +#include + +#include "Acts/Plugins/Detray/DetrayConverter.hpp" +#include "Acts/Tests/CommonHelpers/CylindricalDetector.hpp" + +#include +#include + +using namespace Acts; +using namespace Acts::Experimental; +using namespace Acts::Test; + +GeometryContext tContext; + +auto logger = + Acts::getDefaultLogger("DetrayConverterTests", Acts::Logging::INFO); + +BOOST_AUTO_TEST_SUITE(DetrayConversion) + +BOOST_AUTO_TEST_CASE(DetrayConversion) { + // Load the detector from the Test utilities + auto detector = buildCylindricalDetector(tContext); + + DetrayConverter::Options options; + + vecmem::host_memory_resource memoryResource; + + auto detrayDetector = + DetrayConverter(std::move(logger)) + .convert<>(tContext, *detector, memoryResource, options); + + BOOST_CHECK_EQUAL(detrayDetector.volumes().size(), 6u); + // Beampipe : original 3 -> split into 5 + // Nec: original 4 -> split into 6 + // Layer0: original 4 -> left at 4 + // Layer1: original 4 -> left at 4 + // Layer2: original 4 -> left at 4 + // Pec: original 4 -> split into 6 + // + portals to itself, one per volume 6 + BOOST_CHECK_EQUAL(detrayDetector.portals().size(), 35u); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/Tests/UnitTests/Plugins/Detray/DetrayGeometryConverterTests.cpp b/Tests/UnitTests/Plugins/Detray/DetrayGeometryConverterTests.cpp index c37ff7bf96f..41e283ccc49 100644 --- a/Tests/UnitTests/Plugins/Detray/DetrayGeometryConverterTests.cpp +++ b/Tests/UnitTests/Plugins/Detray/DetrayGeometryConverterTests.cpp @@ -9,24 +9,14 @@ #include #include "Acts/Definitions/Algebra.hpp" -#include "Acts/Detector/CylindricalContainerBuilder.hpp" -#include "Acts/Detector/Detector.hpp" -#include "Acts/Detector/DetectorBuilder.hpp" -#include "Acts/Detector/DetectorComponents.hpp" #include "Acts/Detector/DetectorVolume.hpp" -#include "Acts/Detector/GeometryIdGenerator.hpp" -#include "Acts/Detector/PortalGenerators.hpp" -#include "Acts/Detector/interface/IDetectorComponentBuilder.hpp" #include "Acts/Geometry/CylinderVolumeBounds.hpp" #include "Acts/Geometry/GeometryContext.hpp" -#include "Acts/Navigation/DetectorVolumeFinders.hpp" -#include "Acts/Navigation/InternalNavigation.hpp" #include "Acts/Plugins/Detray/DetrayGeometryConverter.hpp" #include "Acts/Surfaces/CylinderBounds.hpp" #include "Acts/Surfaces/CylinderSurface.hpp" -#include "Acts/Surfaces/DiscSurface.hpp" -#include "Acts/Surfaces/RadialBounds.hpp" #include "Acts/Surfaces/Surface.hpp" +#include "Acts/Tests/CommonHelpers/CylindricalDetector.hpp" #include "Acts/Tests/CommonHelpers/FloatComparisons.hpp" #include "Acts/Utilities/BinningType.hpp" #include "Acts/Utilities/Enumerate.hpp" @@ -39,63 +29,13 @@ using namespace Acts; using namespace Acts::Experimental; +using namespace Acts::Test; GeometryContext tContext; auto logger = Acts::getDefaultLogger("DetrayGeometryConverterTests", Acts::Logging::INFO); -/// @brief A mockup volume builder, it generates volumes with -/// a single surface filled in in order to use the CylindricalContainerBuilder -/// infrastructure. -template -class CylindricalVolumeBuilder : public IDetectorComponentBuilder { - public: - CylindricalVolumeBuilder(const Transform3& transform, - const CylinderVolumeBounds& vBounds, - const surface_bounds_type& sBounds, - const std::string& vName) - : IDetectorComponentBuilder(), - m_transform(transform), - m_volumeBounds(vBounds), - m_surfaceBounds(sBounds), - m_name(vName) {} - - DetectorComponent construct( - [[maybe_unused]] const GeometryContext& gctx) const final { - // The outgoing root volumes - std::vector> rootVolumes; - - // Ingredients - auto surface = Surface::makeShared( - (m_transform), std::make_shared(m_surfaceBounds)); - - auto bounds = std::make_unique(m_volumeBounds); - auto portalGenerator = defaultPortalGenerator(); - auto volume = DetectorVolumeFactory::construct( - portalGenerator, tContext, m_name, m_transform, std::move(bounds), - {surface}, {}, tryNoVolumes(), tryAllPortalsAndSurfaces()); - - // Add to the roots - rootVolumes.push_back(volume); - - DetectorComponent::PortalContainer dContainer; - for (auto [ip, p] : enumerate(volume->portalPtrs())) { - dContainer[ip] = p; - } - return DetectorComponent{ - {volume}, - dContainer, - RootDetectorVolumes{rootVolumes, tryRootVolumes()}}; - } - - private: - Transform3 m_transform; - CylinderVolumeBounds m_volumeBounds; - surface_bounds_type m_surfaceBounds; - std::string m_name; -}; - BOOST_AUTO_TEST_SUITE(DetrayConversion) BOOST_AUTO_TEST_CASE(DetrayTransformConversion) { @@ -113,7 +53,7 @@ BOOST_AUTO_TEST_CASE(DetrayTransformConversion) { CHECK_CLOSE_ABS(payload.tr[2u], 3., std::numeric_limits::epsilon()); // Rotation is correctly translated - const auto rotation = transform.rotation(); + RotationMatrix3 rotation = transform.rotation().transpose(); CHECK_CLOSE_ABS(payload.rot[0u], rotation(0, 0), std::numeric_limits::epsilon()); CHECK_CLOSE_ABS(payload.rot[1u], rotation(0, 1), @@ -190,80 +130,8 @@ BOOST_AUTO_TEST_CASE(DetrayVolumeConversion) { } BOOST_AUTO_TEST_CASE(CylindricalDetector) { - // Declare a barrel sub builder - auto beampipe = std::make_shared< - CylindricalVolumeBuilder>( - Transform3::Identity(), CylinderVolumeBounds(0., 50., 400.), - CylinderBounds(25., 380.), "BeamPipe"); - - // Declare a negative disc builder - Transform3 negZ = Transform3::Identity(); - negZ.pretranslate(Vector3(0., 0., -300.)); - auto endcapN = - std::make_shared>( - negZ, CylinderVolumeBounds(50., 140., 100.), RadialBounds(60., 120.), - "NegativeEndcap"); - - // Declare a barrel sub builder - auto barrel0 = std::make_shared< - CylindricalVolumeBuilder>( - Transform3::Identity(), CylinderVolumeBounds(50., 80., 200.), - CylinderBounds(65., 180.), "Barrel0"); - - // Declare a barrel sub builder - auto barrel1 = std::make_shared< - CylindricalVolumeBuilder>( - Transform3::Identity(), CylinderVolumeBounds(80., 110., 200.), - CylinderBounds(95., 180.), "Barrel1"); - - // Declare a barrel sub builder - auto barrel2 = std::make_shared< - CylindricalVolumeBuilder>( - Transform3::Identity(), CylinderVolumeBounds(110., 140., 200.), - CylinderBounds(125., 180.), "Barrel2"); - - // Create the barrel container builder - CylindricalContainerBuilder::Config barrelRCfg; - barrelRCfg.builders = {barrel0, barrel1, barrel2}; - barrelRCfg.binning = {BinningValue::binR}; - - auto barrel = std::make_shared( - barrelRCfg, getDefaultLogger("BarrelBuilderR", Logging::INFO)); - - Transform3 posZ = Transform3::Identity(); - posZ.pretranslate(Vector3(0., 0., 300.)); - auto endcapP = - std::make_shared>( - posZ, CylinderVolumeBounds(50., 140., 100.), RadialBounds(60., 120.), - "PositiveEndcap"); - - // Create the barrel container builder - CylindricalContainerBuilder::Config barrelEndcapCfg; - barrelEndcapCfg.builders = {endcapN, barrel, endcapP}; - barrelEndcapCfg.binning = {BinningValue::binZ}; - - auto barrelEndcap = std::make_shared( - barrelEndcapCfg, getDefaultLogger("BarrelEndcapBuilder", Logging::INFO)); - - // Create the barrel container builder - CylindricalContainerBuilder::Config detectorCfg; - detectorCfg.builders = {beampipe, barrelEndcap}; - detectorCfg.binning = {BinningValue::binR}; - - auto containerBuilder = std::make_shared( - detectorCfg, getDefaultLogger("DetectorBuilder", Logging::INFO)); - - // Detector builder - auto gigConfig = GeometryIdGenerator::Config(); - auto gig = std::make_shared(gigConfig); - - Acts::Experimental::DetectorBuilder::Config dCfg; - dCfg.auxiliary = "*** Test : Cylindrical Detector ***"; - dCfg.name = "CylindricalDetector"; - dCfg.builder = containerBuilder; - dCfg.geoIdGenerator = gig; - - auto detector = DetectorBuilder(dCfg).construct(tContext); + // Load the detector from the Test utilities + auto detector = buildCylindricalDetector(tContext); // Convert the detector DetrayConversionUtils::GeometryIdCache geoIdCache; diff --git a/Tests/UnitTests/Plugins/Detray/DetrayMaterialConverterTests.cpp b/Tests/UnitTests/Plugins/Detray/DetrayMaterialConverterTests.cpp index 0d5aa41bcc8..702d7a25001 100644 --- a/Tests/UnitTests/Plugins/Detray/DetrayMaterialConverterTests.cpp +++ b/Tests/UnitTests/Plugins/Detray/DetrayMaterialConverterTests.cpp @@ -8,12 +8,14 @@ #include +#include "Acts/Geometry/GeometryContext.hpp" #include "Acts/Material/BinnedSurfaceMaterial.hpp" #include "Acts/Material/HomogeneousSurfaceMaterial.hpp" #include "Acts/Material/Material.hpp" #include "Acts/Material/MaterialSlab.hpp" #include "Acts/Plugins/Detray/DetrayConversionUtils.hpp" #include "Acts/Plugins/Detray/DetrayMaterialConverter.hpp" +#include "Acts/Tests/CommonHelpers/CylindricalDetector.hpp" #include "Acts/Tests/CommonHelpers/FloatComparisons.hpp" #include "Acts/Utilities/BinUtility.hpp" #include "Acts/Utilities/Logger.hpp" @@ -21,6 +23,8 @@ #include #include +auto tContext = Acts::GeometryContext(); + BOOST_AUTO_TEST_SUITE(DetrayConversion) // These tests check the conversion to the payload objects, the full test @@ -69,6 +73,20 @@ BOOST_AUTO_TEST_CASE(DetrayMaterialSlabConversion) { std::numeric_limits::epsilon()); } +BOOST_AUTO_TEST_CASE(DetrayHomogeneousMaterialConversion) { + // Convert homogeneous material + Acts::HomogeneousSurfaceMaterial homMaterial(materialSlab12345); + + detray::io::grid_payload + payload = Acts::DetrayMaterialConverter::convertGridSurfaceMaterial( + homMaterial, *logger); + + // Check the payload - empty, this should run through another converter + BOOST_CHECK(payload.axes.empty()); + BOOST_CHECK(payload.bins.empty()); +} + BOOST_AUTO_TEST_CASE(DetrayBinnedMaterialConversionX) { // Create a binned material in 4 bins in x direction Acts::BinUtility binUtility(4u, -2., 2., Acts::BinningOption::open, @@ -83,7 +101,7 @@ BOOST_AUTO_TEST_CASE(DetrayBinnedMaterialConversionX) { detray::io::grid_payload - payload = Acts::DetrayMaterialConverter::convertSurfaceMaterial( + payload = Acts::DetrayMaterialConverter::convertGridSurfaceMaterial( binnedMaterial, *logger); // Check the payload // - we fake 2D always for detray to minimize the number of containers @@ -143,7 +161,7 @@ BOOST_AUTO_TEST_CASE(DetrayBinnedMaterialConversionY) { detray::io::grid_payload - payload = Acts::DetrayMaterialConverter::convertSurfaceMaterial( + payload = Acts::DetrayMaterialConverter::convertGridSurfaceMaterial( binnedMaterial, *logger); // Check the payload // - we fake 2D always for detray to minimize the number of containers @@ -207,7 +225,7 @@ BOOST_AUTO_TEST_CASE(DetrayBinnedMaterialConversionXY) { detray::io::grid_payload - payload = Acts::DetrayMaterialConverter::convertSurfaceMaterial( + payload = Acts::DetrayMaterialConverter::convertGridSurfaceMaterial( binnedMaterial, *logger); // Check the payload BOOST_CHECK_EQUAL(payload.axes.size(), 2u); @@ -260,7 +278,7 @@ BOOST_AUTO_TEST_CASE(DetrayBinnedMaterialConversionR) { detray::io::grid_payload - payload = Acts::DetrayMaterialConverter::convertSurfaceMaterial( + payload = Acts::DetrayMaterialConverter::convertGridSurfaceMaterial( binnedMaterial, *logger); // Check the payload // - we fake 2D always for detray to minimize the number of containers @@ -303,7 +321,7 @@ BOOST_AUTO_TEST_CASE(DetrayBinnedMaterialConversionRPhi) { detray::io::grid_payload - payload = Acts::DetrayMaterialConverter::convertSurfaceMaterial( + payload = Acts::DetrayMaterialConverter::convertGridSurfaceMaterial( binnedMaterial, *logger); // Check the payload BOOST_CHECK_EQUAL(payload.axes.size(), 2u); @@ -337,7 +355,7 @@ BOOST_AUTO_TEST_CASE(DetrayBinnedMaterialConversionZ) { detray::io::grid_payload - payload = Acts::DetrayMaterialConverter::convertSurfaceMaterial( + payload = Acts::DetrayMaterialConverter::convertGridSurfaceMaterial( binnedMaterial, *logger); // Check the payload @@ -380,7 +398,7 @@ BOOST_AUTO_TEST_CASE(DetrayBinnedMaterialConversionZPhi) { detray::io::grid_payload - payload = Acts::DetrayMaterialConverter::convertSurfaceMaterial( + payload = Acts::DetrayMaterialConverter::convertGridSurfaceMaterial( binnedMaterial, *logger); // Check the payload BOOST_CHECK_EQUAL(payload.axes.size(), 2u); @@ -409,7 +427,7 @@ BOOST_AUTO_TEST_CASE(DetrayBinnedMaterialConversionInvalid) { Acts::BinnedSurfaceMaterial(binUtility, {materialSlabs0, materialSlabs1}, 0., Acts::MappingType::Default); - BOOST_CHECK_THROW(Acts::DetrayMaterialConverter::convertSurfaceMaterial( + BOOST_CHECK_THROW(Acts::DetrayMaterialConverter::convertGridSurfaceMaterial( binnedMaterial, *logger), std::invalid_argument); }