Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add misalignment decoration for DD4hep detector #3891

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Examples/Detectors/DD4hepDetector/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ add_library(
SHARED
src/DD4hepDetector.cpp
src/DD4hepGeometryService.cpp
src/DD4hepAlignmentDecorator.cpp
)

target_include_directories(
Expand Down
Original file line number Diff line number Diff line change
@@ -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 <Acts/Geometry/TrackingGeometry.hpp>

#include <cstddef>
#include <iostream>
#include <memory>
#include <mutex>
#include <string>
#include <unordered_map>
#include <vector>

namespace ActsExamples {
struct AlgorithmContext;

namespace DD4hep {
class DD4hepAlignmentDecorator : public IContextDecorator {
public:
using LayerStore = std::vector<std::shared_ptr<Acts::DD4hepDetectorElement>>;
using DetectorStore = std::vector<LayerStore>;
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<const Acts::TrackingGeometry> trackingGeometry = nullptr;
};

DD4hepAlignmentDecorator(const Config& cfg,
std::unique_ptr<const Acts::Logger> 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<const Acts::Logger> m_logger; ///!< the logging instance
const Acts::Logger& logger() const { return *m_logger; }
std::string m_name = "Aligned Detector";
std::unordered_map<std::string, Acts::Transform3>
m_misalignmentAtConstruction;
std::unordered_map<std::string, Acts::Transform3> m_nominalStore;
std::unordered_map<std::string, Acts::Transform3> 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>();
double y = value[1].get<double>();
double z = value[2].get<double>();
double alpha = value[3].get<double>() / 180 * M_PI;
double beta = value[4].get<double>() / 180 * M_PI;
double gamma = value[5].get<double>() / 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
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ struct DD4hepDetector {
/// @brief Access to the DD4hep field
/// @return a shared pointer to the DD4hep field
std::shared_ptr<Acts::DD4hepFieldAdapter> field() const;

// whether use the nominal geometry
bool m_nominal = false;
};

} // namespace ActsExamples::DD4hep
82 changes: 82 additions & 0 deletions Examples/Detectors/DD4hepDetector/src/DD4hepAlignmentDecorator.cpp
Original file line number Diff line number Diff line change
@@ -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 <Acts/Geometry/TrackingGeometry.hpp>

#include <ostream>
#include <thread>
#include <utility>

ActsExamples::DD4hep::DD4hepAlignmentDecorator::DD4hepAlignmentDecorator(
const Config& cfg, std::unique_ptr<const Acts::Logger> 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<std::string, Acts::Transform3> 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<const Acts::DD4hepDetectorElement*>(
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);
Comment on lines +60 to +81
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Exception throwing you do, but more detail helpful it may be.
When surfaces are null or not alignable, an invalid_argument is thrown. Additional context about which surface triggers the error, wise it would be to include.

}
9 changes: 9 additions & 0 deletions Examples/Detectors/DD4hepDetector/src/DD4hepDetector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 <cstddef>
Expand Down Expand Up @@ -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<DD4hepAlignmentDecorator>(
std::move(dd4hepAcfg),
Acts::getDefaultLogger("AlignmentDecorator", Acts::Logging::INFO)));
Comment on lines +48 to +54
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Handle decorator initialization failures, wise one must.

Hmmmm. Protect against initialization failures of alignment decorator, we should. Validate configuration before proceeding, essential it is.

   DD4hepAlignmentDecorator::Config dd4hepAcfg;
   dd4hepAcfg.nominal = m_nominal;
   dd4hepAcfg.trackingGeometry = dd4tGeometry;
+  if (!dd4hepAcfg.trackingGeometry) {
+    throw std::runtime_error{"Invalid tracking geometry in alignment decorator configuration"};
+  }
   ContextDecorators dd4ContextDecorators = {};
   dd4ContextDecorators.push_back(std::make_shared<DD4hepAlignmentDecorator>(
       std::move(dd4hepAcfg),
       Acts::getDefaultLogger("AlignmentDecorator", Acts::Logging::INFO)));
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
DD4hepAlignmentDecorator::Config dd4hepAcfg;
dd4hepAcfg.nominal = m_nominal;
dd4hepAcfg.trackingGeometry = dd4tGeometry;
ContextDecorators dd4ContextDecorators = {};
dd4ContextDecorators.push_back(std::make_shared<DD4hepAlignmentDecorator>(
std::move(dd4hepAcfg),
Acts::getDefaultLogger("AlignmentDecorator", Acts::Logging::INFO)));
DD4hepAlignmentDecorator::Config dd4hepAcfg;
dd4hepAcfg.nominal = m_nominal;
dd4hepAcfg.trackingGeometry = dd4tGeometry;
if (!dd4hepAcfg.trackingGeometry) {
throw std::runtime_error{"Invalid tracking geometry in alignment decorator configuration"};
}
ContextDecorators dd4ContextDecorators = {};
dd4ContextDecorators.push_back(std::make_shared<DD4hepAlignmentDecorator>(
std::move(dd4hepAcfg),
Acts::getDefaultLogger("AlignmentDecorator", Acts::Logging::INFO)));

std::cout << "After dd4ContextDecorators push back" << std::endl;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Use proper logging mechanism, you must. std::cout, a path to the dark side it is.

Replace direct console output with proper logging framework, we should.

-  std::cout << "After dd4ContextDecorators push back" << std::endl;
+  ACTS_DEBUG("Alignment decorator successfully initialized");
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
std::cout << "After dd4ContextDecorators push back" << std::endl;
ACTS_DEBUG("Alignment decorator successfully initialized");

// return the pair of geometry and empty decorators
return std::make_pair<TrackingGeometryPtr, ContextDecorators>(
std::move(dd4tGeometry), std::move(dd4ContextDecorators));
Expand Down
1 change: 1 addition & 0 deletions Plugins/DD4hep/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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 <nlohmann/json.hpp>

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<std::string, Transform3>;

/// 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<std::string, Transform3> 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
13 changes: 13 additions & 0 deletions Plugins/DD4hep/src/DD4hepDetectorElement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@

#include "Acts/Plugins/DD4hep/DD4hepDetectorElement.hpp"

#include "Acts/Plugins/DD4hep/DD4hepGeometryContext.hpp"

#include <iostream>
#include <utility>

#include <DD4hep/Alignments.h>
Expand All @@ -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<DD4hepGeometryContext>();
if (dd4hepGtx != nullptr && !dd4hepGtx->isNominal()) {
return dd4hepGtx->contextualTransform(*this);
}
return TGeoDetectorElement::transform(gctx);
}
36 changes: 36 additions & 0 deletions Plugins/DD4hep/src/DD4hepGeometryContext.cpp
Original file line number Diff line number Diff line change
@@ -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 <iostream>

#include <nlohmann/json.hpp>

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());
}
}
Comment on lines +19 to +31
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Error handling wisdom, share I must.

Consider adding error handling for invalid transformations:

 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()) {
+      // Validate transformation matrix
+      if (!it->second.matrix().allFinite()) {
+        throw std::runtime_error("Invalid transformation matrix for detector element " +
+                               std::to_string(dElement.identifier()));
+      }
       return it->second;
     } else {
       return dElement.nominalTransform(DD4hepGeometryContext());
     }
   } else {
     return dElement.nominalTransform(DD4hepGeometryContext());
   }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
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());
}
}
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()) {
// Validate transformation matrix
if (!it->second.matrix().allFinite()) {
throw std::runtime_error("Invalid transformation matrix for detector element " +
std::to_string(dElement.identifier()));
}
return it->second;
} else {
return dElement.nominalTransform(DD4hepGeometryContext());
}
} else {
return dElement.nominalTransform(DD4hepGeometryContext());
}
}


void Acts::DD4hepGeometryContext::setAlignmentStore(
std::unordered_map<std::string, Transform3> alignmentStore) {
m_alignmentStore = alignmentStore;
}
Loading