diff --git a/Examples/Detectors/DD4hepDetector/CMakeLists.txt b/Examples/Detectors/DD4hepDetector/CMakeLists.txt index 244b4f20a6d..9c616903a38 100644 --- a/Examples/Detectors/DD4hepDetector/CMakeLists.txt +++ b/Examples/Detectors/DD4hepDetector/CMakeLists.txt @@ -3,6 +3,7 @@ add_library( SHARED src/DD4hepDetector.cpp src/DD4hepGeometryService.cpp + src/DD4hepAlignmentDecorator.cpp ) target_include_directories( diff --git a/Examples/Detectors/DD4hepDetector/include/ActsExamples/DD4hepDetector/DD4hepAlignmentDecorator.hpp b/Examples/Detectors/DD4hepDetector/include/ActsExamples/DD4hepDetector/DD4hepAlignmentDecorator.hpp new file mode 100644 index 00000000000..32a50b6d22e --- /dev/null +++ b/Examples/Detectors/DD4hepDetector/include/ActsExamples/DD4hepDetector/DD4hepAlignmentDecorator.hpp @@ -0,0 +1,90 @@ +#pragma once + +#include "Acts/Definitions/Algebra.hpp" +#include "Acts/Plugins/DD4hep/DD4hepDetectorElement.hpp" +#include "Acts/Utilities/Logger.hpp" +#include "ActsExamples/Framework/AlgorithmContext.hpp" +#include "ActsExamples/Framework/IContextDecorator.hpp" +#include "ActsExamples/Framework/ProcessCode.hpp" +#include "ActsExamples/Framework/RandomNumbers.hpp" +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace ActsExamples { +struct AlgorithmContext; + +namespace DD4hep { +class DD4hepAlignmentDecorator : public IContextDecorator { + public: + using LayerStore = std::vector>; + using DetectorStore = std::vector; + struct Config { + // whether use the nominal geometry + bool nominal = true; + // path of Json file which is used to store the misalignment matrix of each + // detector element + // @todo use `JsonMisalignmentConfig` + std::string misAlignedGeoJsonPath = "odd-misalignment-matrix.json"; + // tracking geometry + std::shared_ptr trackingGeometry = nullptr; + }; + + DD4hepAlignmentDecorator(const Config& cfg, + std::unique_ptr logger = + Acts::getDefaultLogger("AlignmentDecorator", + Acts::Logging::INFO)); + ~DD4hepAlignmentDecorator() override = default; + ProcessCode decorate(AlgorithmContext& context) override; + const std::string& name() const override { return m_name; } + + private: + Config m_cfg; ///< the configuration class + std::unique_ptr m_logger; ///!< the logging instance + const Acts::Logger& logger() const { return *m_logger; } + std::string m_name = "Aligned Detector"; + std::unordered_map + m_misalignmentAtConstruction; + std::unordered_map m_nominalStore; + std::unordered_map m_mistransform; + void parseGeometry(const Acts::TrackingGeometry& tGeometry); + void initializeMisFromJson(const std::string& misAlignedGeoJsonFile); +}; + +inline void DD4hepAlignmentDecorator::initializeMisFromJson( + const std::string& misJson) { + std::ifstream file(misJson); + if (!file.is_open()) + throw std::runtime_error("Unable to open file"); + nlohmann::json jsonData; + file >> jsonData; + for (auto& [key, value] : jsonData.items()) { + if (value.is_array() && value.size() == 6) { + double x = value[0].get(); + double y = value[1].get(); + double z = value[2].get(); + double alpha = value[3].get() / 180 * M_PI; + double beta = value[4].get() / 180 * M_PI; + double gamma = value[5].get() / 180 * M_PI; + Acts::Transform3 translation = + Eigen::Affine3d(Eigen::Translation3d(x, y, z)); + Acts::Transform3 delta_rotationx = + Eigen::Affine3d(Eigen::AngleAxisd(alpha, Eigen::Vector3d::UnitX())); + Acts::Transform3 delta_rotationy = + Eigen::Affine3d(Eigen::AngleAxisd(beta, Eigen::Vector3d::UnitY())); + Acts::Transform3 delta_rotationz = + Eigen::Affine3d(Eigen::AngleAxisd(gamma, Eigen::Vector3d::UnitZ())); + m_misalignmentAtConstruction[key] = + translation * delta_rotationx * delta_rotationy * delta_rotationz; + } + } + std::cout << "Successfully initialize the JSON file" << std::endl; +} +} // namespace DD4hep +} // namespace ActsExamples diff --git a/Examples/Detectors/DD4hepDetector/include/ActsExamples/DD4hepDetector/DD4hepDetector.hpp b/Examples/Detectors/DD4hepDetector/include/ActsExamples/DD4hepDetector/DD4hepDetector.hpp index 015b511825f..9b94c26abbe 100644 --- a/Examples/Detectors/DD4hepDetector/include/ActsExamples/DD4hepDetector/DD4hepDetector.hpp +++ b/Examples/Detectors/DD4hepDetector/include/ActsExamples/DD4hepDetector/DD4hepDetector.hpp @@ -90,6 +90,9 @@ struct DD4hepDetector { /// @brief Access to the DD4hep field /// @return a shared pointer to the DD4hep field std::shared_ptr field() const; + + // whether use the nominal geometry + bool m_nominal = false; }; } // namespace ActsExamples::DD4hep diff --git a/Examples/Detectors/DD4hepDetector/src/DD4hepAlignmentDecorator.cpp b/Examples/Detectors/DD4hepDetector/src/DD4hepAlignmentDecorator.cpp new file mode 100644 index 00000000000..b467f1a6155 --- /dev/null +++ b/Examples/Detectors/DD4hepDetector/src/DD4hepAlignmentDecorator.cpp @@ -0,0 +1,82 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 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 https://mozilla.org/MPL/2.0/. + +#include "ActsExamples/DD4hepDetector/DD4hepAlignmentDecorator.hpp" + +#include "Acts/Definitions/Algebra.hpp" +#include "Acts/Geometry/GeometryContext.hpp" +#include "Acts/Plugins/DD4hep/DD4hepDetectorElement.hpp" +#include "Acts/Plugins/DD4hep/DD4hepGeometryContext.hpp" +#include "ActsExamples/Framework/AlgorithmContext.hpp" +#include "ActsExamples/Framework/RandomNumbers.hpp" +#include + +#include +#include +#include + +ActsExamples::DD4hep::DD4hepAlignmentDecorator::DD4hepAlignmentDecorator( + const Config& cfg, std::unique_ptr logger) + : m_cfg(cfg), m_logger(std::move(logger)) { + if (m_cfg.trackingGeometry != nullptr) { + parseGeometry(*m_cfg.trackingGeometry.get()); + } +} + +ActsExamples::ProcessCode +ActsExamples::DD4hep::DD4hepAlignmentDecorator::decorate( + AlgorithmContext& context) { + Acts::DD4hepGeometryContext dd4hepGeoCtx = + Acts::DD4hepGeometryContext(m_cfg.nominal); + if (!m_cfg.nominal) { + initializeMisFromJson(m_cfg.misAlignedGeoJsonPath); + for (const auto& entry : m_misalignmentAtConstruction) { + const std::string& identifier = entry.first; + const Acts::Transform3& misalignmentTransform = entry.second; + auto nominalIt = m_nominalStore.find(identifier); + if (nominalIt != m_nominalStore.end()) { + const Acts::Transform3& nominalTransform = nominalIt->second; + Eigen::Matrix3d R1 = nominalTransform.rotation(); + Eigen::Vector3d T1 = nominalTransform.translation(); + Eigen::Matrix3d R2 = misalignmentTransform.rotation(); + Eigen::Vector3d T2 = misalignmentTransform.translation(); + Eigen::Matrix3d R3 = R1 * R2; + Eigen::Vector3d T3 = T1 + T2; + m_mistransform[identifier] = + Eigen::Affine3d(Eigen::Translation3d(T3)) * Eigen::Affine3d(R3); + } + } + dd4hepGeoCtx.setAlignmentStore(m_mistransform); + } + context.geoContext = dd4hepGeoCtx; + return ProcessCode::SUCCESS; +} + +void ActsExamples::DD4hep::DD4hepAlignmentDecorator::parseGeometry( + const Acts::TrackingGeometry& tGeometry) { + // Double-visit - first count + std::size_t nTransforms = 0; + tGeometry.visitSurfaces([&nTransforms](const auto*) { ++nTransforms; }); + std::unordered_map aStore; + Acts::GeometryContext nominalCtx = {}; + // Collect the surfacas into the nominal store + auto fillTransforms = [&aStore, &nominalCtx](const auto* surface) -> void { + if (surface == nullptr) { + throw std::invalid_argument("Surface is nullptr."); + } + auto alignableElement = dynamic_cast( + surface->associatedDetectorElement()); + if (alignableElement == nullptr) { + throw std::invalid_argument("Surface is not alignable"); + } + unsigned long long id = alignableElement->identifier(); + aStore[Form("%lld", id)] = surface->transform(nominalCtx); + }; + tGeometry.visitSurfaces(fillTransforms); + m_nominalStore = std::move(aStore); +} diff --git a/Examples/Detectors/DD4hepDetector/src/DD4hepDetector.cpp b/Examples/Detectors/DD4hepDetector/src/DD4hepDetector.cpp index a6494ce0860..0b435e9f233 100644 --- a/Examples/Detectors/DD4hepDetector/src/DD4hepDetector.cpp +++ b/Examples/Detectors/DD4hepDetector/src/DD4hepDetector.cpp @@ -11,6 +11,7 @@ #include "Acts/Geometry/GeometryContext.hpp" #include "Acts/MagneticField/MagneticFieldProvider.hpp" #include "Acts/Plugins/DD4hep/DD4hepFieldAdapter.hpp" +#include "ActsExamples/DD4hepDetector/DD4hepAlignmentDecorator.hpp" #include "ActsExamples/DD4hepDetector/DD4hepGeometryService.hpp" #include @@ -43,7 +44,15 @@ auto DD4hepDetector::finalize( throw std::runtime_error{ "Did not receive tracking geometry from DD4hep geometry service"}; } + + DD4hepAlignmentDecorator::Config dd4hepAcfg; + dd4hepAcfg.nominal = m_nominal; + dd4hepAcfg.trackingGeometry = dd4tGeometry; ContextDecorators dd4ContextDecorators = {}; + dd4ContextDecorators.push_back(std::make_shared( + std::move(dd4hepAcfg), + Acts::getDefaultLogger("AlignmentDecorator", Acts::Logging::INFO))); + std::cout << "After dd4ContextDecorators push back" << std::endl; // return the pair of geometry and empty decorators return std::make_pair( std::move(dd4tGeometry), std::move(dd4ContextDecorators)); diff --git a/Plugins/DD4hep/CMakeLists.txt b/Plugins/DD4hep/CMakeLists.txt index bbaa90c77d0..47ebd9803ef 100644 --- a/Plugins/DD4hep/CMakeLists.txt +++ b/Plugins/DD4hep/CMakeLists.txt @@ -9,6 +9,7 @@ add_library( src/DD4hepDetectorStructure.cpp src/DD4hepMaterialHelpers.cpp src/DD4hepDetectorElement.cpp + src/DD4hepGeometryContext.cpp src/DD4hepDetectorSurfaceFactory.cpp src/DD4hepLayerBuilder.cpp src/DD4hepLayerStructure.cpp diff --git a/Plugins/DD4hep/include/Acts/Plugins/DD4hep/DD4hepDetectorElement.hpp b/Plugins/DD4hep/include/Acts/Plugins/DD4hep/DD4hepDetectorElement.hpp index 0dd6788c315..a63c89a86ca 100644 --- a/Plugins/DD4hep/include/Acts/Plugins/DD4hep/DD4hepDetectorElement.hpp +++ b/Plugins/DD4hep/include/Acts/Plugins/DD4hep/DD4hepDetectorElement.hpp @@ -9,6 +9,7 @@ #pragma once #include "Acts/Geometry/GeometryContext.hpp" +#include "Acts/Plugins/DD4hep/DD4hepGeometryContext.hpp" #include "Acts/Plugins/TGeo/TGeoDetectorElement.hpp" #include "Acts/Utilities/ThrowAssert.hpp" @@ -87,6 +88,11 @@ class DD4hepDetectorElement : public TGeoDetectorElement { // Give access to the DD4hep detector element const dd4hep::DetElement& sourceElement() const { return m_detElement; } + const Transform3& transform(const GeometryContext& gctx) const override; + Transform3 nominalTransform(const GeometryContext& gctx) const { + return TGeoDetectorElement::transform(gctx); + } + private: /// DD4hep detector element dd4hep::DetElement m_detElement; diff --git a/Plugins/DD4hep/include/Acts/Plugins/DD4hep/DD4hepGeometryContext.hpp b/Plugins/DD4hep/include/Acts/Plugins/DD4hep/DD4hepGeometryContext.hpp new file mode 100644 index 00000000000..b3790de4ea9 --- /dev/null +++ b/Plugins/DD4hep/include/Acts/Plugins/DD4hep/DD4hepGeometryContext.hpp @@ -0,0 +1,62 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016-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 https://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/Definitions/Algebra.hpp" +#include "Acts/Geometry/GeometryContext.hpp" + +#include + +namespace Acts { + +class DD4hepDetectorElement; + +/// @class GeometryContext +/// +/// @brief DD4hep specific geometry context for alignment handling +/// +/// Extends the base GeometryContext to provide DD4hep-specific alignment +/// capabilities. The context can be active or inactive, controlling whether +/// alignment corrections should be applied. +/// +/// @note This context is specifically designed to work with DD4hepDetectorElement +/// and provides contextual transformations for alignment purposes. +/// +class DD4hepGeometryContext : public GeometryContext { + public: + using AlignmentStore = std::unordered_map; + + /// Default constructor + DD4hepGeometryContext() = default; + + /// Constructor + explicit DD4hepGeometryContext(bool isGeometryNominal) + : m_nominal(isGeometryNominal) {} + + /// The transform of this detector element within the given context + /// + /// @param dElement The detector element + /// + /// @return The transform of the detector element + const Transform3& contextualTransform( + const DD4hepDetectorElement& dElement) const; + + void setAlignmentStore( + std::unordered_map alignmentStore); + + /// @brief Return the active status of the context + /// @return boolean that indicates if the context is active + bool isNominal() const { return m_nominal; } + + private: + bool m_nominal = true; + AlignmentStore m_alignmentStore = {}; +}; + +} // namespace Acts diff --git a/Plugins/DD4hep/src/DD4hepDetectorElement.cpp b/Plugins/DD4hep/src/DD4hepDetectorElement.cpp index e145e80d412..882642c8c3e 100644 --- a/Plugins/DD4hep/src/DD4hepDetectorElement.cpp +++ b/Plugins/DD4hep/src/DD4hepDetectorElement.cpp @@ -8,6 +8,9 @@ #include "Acts/Plugins/DD4hep/DD4hepDetectorElement.hpp" +#include "Acts/Plugins/DD4hep/DD4hepGeometryContext.hpp" + +#include #include #include @@ -23,3 +26,13 @@ Acts::DD4hepDetectorElement::DD4hepDetectorElement( detElement.nominal().worldTransformation(), axes, scalor, std::move(material)), m_detElement(detElement) {} + +const Acts::Transform3& Acts::DD4hepDetectorElement::transform( + const GeometryContext& gctx) const { + const Acts::DD4hepGeometryContext* dd4hepGtx = + gctx.maybeGet(); + if (dd4hepGtx != nullptr && !dd4hepGtx->isNominal()) { + return dd4hepGtx->contextualTransform(*this); + } + return TGeoDetectorElement::transform(gctx); +} diff --git a/Plugins/DD4hep/src/DD4hepGeometryContext.cpp b/Plugins/DD4hep/src/DD4hepGeometryContext.cpp new file mode 100644 index 00000000000..a1780433795 --- /dev/null +++ b/Plugins/DD4hep/src/DD4hepGeometryContext.cpp @@ -0,0 +1,36 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 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 https://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/Plugins/DD4hep/DD4hepGeometryContext.hpp" + +#include "Acts/Plugins/DD4hep/DD4hepDetectorElement.hpp" + +#include + +#include + +const Acts::Transform3& Acts::DD4hepGeometryContext::contextualTransform( + const DD4hepDetectorElement& dElement) const { + if (!this->isNominal()) { + auto it = m_alignmentStore.find(Form("%lld", dElement.identifier())); + if (it != m_alignmentStore.end()) { + return it->second; + } else { + return dElement.nominalTransform(DD4hepGeometryContext()); + } + } else { + return dElement.nominalTransform(DD4hepGeometryContext()); + } +} + +void Acts::DD4hepGeometryContext::setAlignmentStore( + std::unordered_map alignmentStore) { + m_alignmentStore = alignmentStore; +}